├── .eslintrc.js
├── .gitignore
├── .husky
├── commit-msg
└── pre-commit
├── .lintstagedrc
├── README.md
├── babel.config.js
├── build
├── rollup.config.base.mjs
├── rollup.config.browser.mjs
├── rollup.config.es.mjs
└── rollup.config.umd.mjs
├── commitlint.config.js
├── dist
├── html
│ └── popup.html
└── js
│ ├── background.min.js
│ ├── background.min.js.map
│ ├── content.min.js
│ └── content.min.js.map
├── icon-128.png
├── icon-16.png
├── images
├── demo-google-excel.gif
└── demo.gif
├── manifest.json
├── package-lock.json
├── package.json
└── src
├── background.js
├── common
└── util.js
├── content.js
└── pangu.js
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es2021: true,
5 | },
6 | extends: "eslint:recommended",
7 | overrides: [
8 | {
9 | env: {
10 | node: true,
11 | },
12 | files: [".eslintrc.{js,cjs}"],
13 | parserOptions: {
14 | sourceType: "script",
15 | },
16 | },
17 | ],
18 | parserOptions: {
19 | ecmaVersion: "latest",
20 | sourceType: "module",
21 | },
22 | rules: {},
23 | ignorePatterns: [
24 | "node_modules/",
25 | "dist/",
26 | "src/pangu.js",
27 | "babel.config.js",
28 | "commitlint.config.js",
29 | ],
30 | };
31 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .temp/
3 | .cache/
4 | .DS_Store
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | # npx --no -- commitlint --edit ${1}
5 |
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 | . "$(dirname -- "$0")/_/husky.sh"
3 |
4 | npx lint-staged
5 |
--------------------------------------------------------------------------------
/.lintstagedrc:
--------------------------------------------------------------------------------
1 | {
2 | "*.{js, json, md}": [
3 | "prettier --write",
4 | "git add"
5 | ],
6 | "*.{js,ts}": [
7 | "eslint --ext .js, ./src/*.js",
8 | "git add"
9 | ]
10 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | #
Format your text anytime in the web editor.
2 |
3 | 通过谷歌扩展,快速将选中文本,格式化为符合 [中文文案排版指北](https://github.com/sparanoid/chinese-copywriting-guidelines) 的文本。
4 |
5 | 已经支持的平台:
6 |
7 | + [谷歌文档 - Excel](https://docs.google.com/spreadsheets)
8 | + [石墨文档](https://shimo.im/)
9 |
10 | ## How to install
11 |
12 | 1. 打开 Chrome 扩展页面 `chrome://extensions/`
13 | 2. 下载本项目至本地
14 | 3. 点击左上角 `Load unpacked` 按钮,加载此项目
15 | 4. 如果扩展程序需要特定的权限或数据,您会看到提示。若要批准,请点击添加扩展程序
16 |
17 | 完成安装后,将会看到本扩展被添加到扩展列表。
18 |
19 | ## How to use
20 |
21 | 1. 选择你需要格式化的文本内容
22 | 2. 右键呼出菜单,选择 `Text formatting` 按钮
23 | - 如果当前选择内容处于可编辑输入框内,会完成自动文本替换
24 | - 如果当前选择内容处于不可编辑状态,可打开控制台查看格式化后的内容
25 |
26 | 
27 |
28 | ## Update Logs
29 |
30 | 1. 【2023-10-27】,新增对谷歌文档 Excel 的支持
31 |
32 | 
33 |
34 | ## Next
35 |
36 | 写给自己用的扩展,计划逐步兼容语雀文档、有道云、飞书文档、掘金编辑器、Github issues 输入框,感兴趣可提前 Star。
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [[require("@babel/preset-env"), { modules: false }]],
3 | };
4 |
--------------------------------------------------------------------------------
/build/rollup.config.base.mjs:
--------------------------------------------------------------------------------
1 | import { babel } from '@rollup/plugin-babel';
2 | import resolve from '@rollup/plugin-node-resolve';
3 | import commonjs from '@rollup/plugin-commonjs';
4 | import cjs from '@rollup/plugin-commonjs';
5 | import livereload from 'rollup-plugin-livereload';
6 |
7 | const commonConfig = {
8 | plugins: [
9 | resolve({
10 | mainFields: ['module', 'jsnext', 'main', 'browser'],
11 | }),
12 | commonjs(),
13 | babel({
14 | exclude: 'node_modules/**',
15 | }),
16 | cjs({
17 | include: /node_modules/,
18 | }),
19 | livereload(),
20 | ],
21 | watch: {
22 | include: 'src/**',
23 | },
24 | external: []
25 | };
26 |
27 | export default {
28 | content: {
29 | input: 'src/content.js',
30 | ...commonConfig
31 | },
32 | background: {
33 | input: 'src/background.js',
34 | ...commonConfig
35 | }
36 | };
--------------------------------------------------------------------------------
/build/rollup.config.browser.mjs:
--------------------------------------------------------------------------------
1 | import base from './rollup.config.base.mjs';
2 | import { terser } from 'rollup-plugin-terser';
3 |
4 | const commonConfig = {
5 | format: 'iife',
6 | sourcemap: true,
7 | }
8 |
9 | const configContent = Object.assign({}, base.content, {
10 | output: {
11 | exports: 'named',
12 | name: 'content',
13 | file: 'dist/js/content.min.js',
14 | globals: {},
15 | ...commonConfig
16 | },
17 | });
18 | const configBackground = Object.assign({}, base.background, {
19 | output: {
20 | exports: 'named',
21 | name: 'background',
22 | file: 'dist/js/background.min.js',
23 | globals: {
24 | 'pangu': 'https://jspm.dev/pangu@4.0.7'
25 | },
26 | external: ['pangu'],
27 | ...commonConfig
28 | },
29 | });
30 |
31 | configContent.plugins.push(terser());
32 | configBackground.plugins.push(terser());
33 |
34 | console.log('configContent', configContent);
35 | console.log('configBackground', configBackground);
36 |
37 | export default [configContent, configBackground];
--------------------------------------------------------------------------------
/build/rollup.config.es.mjs:
--------------------------------------------------------------------------------
1 | import base from './rollup.config.base.mjs';
2 |
3 | const config = Object.assign({}, base, {
4 | output: {
5 | name: 'content',
6 | file: 'dist/js/content.esm.js',
7 | format: 'es',
8 | sourcemap: true,
9 | },
10 | external: [
11 | ...base.external,
12 | ],
13 | })
14 |
15 | export default config;
--------------------------------------------------------------------------------
/build/rollup.config.umd.mjs:
--------------------------------------------------------------------------------
1 | import base from './rollup.config.base.mjs';
2 |
3 | const config = Object.assign({}, base, {
4 | output: {
5 | exports: 'named',
6 | name: 'content',
7 | file: 'dist/js/content.umd.js',
8 | format: 'umd',
9 | sourcemap: true,
10 | },
11 | })
12 |
13 | export default config;
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = { extends: ["@commitlint/config-conventional"] };
2 |
--------------------------------------------------------------------------------
/dist/html/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Text Formatting
6 |
7 |
8 |
9 |
10 | Text Formatting
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/dist/js/background.min.js:
--------------------------------------------------------------------------------
1 | !(function (e, t) {
2 | e &&
3 | !e.getElementById("livereloadscript") &&
4 | (((t = e.createElement("script")).async = 1),
5 | (t.src =
6 | "//" +
7 | (self.location.host || "localhost").split(":")[0] +
8 | ":35729/livereload.js?snipver=1"),
9 | (t.id = "livereloadscript"),
10 | e.getElementsByTagName("head")[0].appendChild(t));
11 | })(self.document),
12 | (function () {
13 | "use strict";
14 | chrome.contextMenus.create({
15 | title: "Text Formatting",
16 | contexts: ["all"],
17 | onclick: function (e) {
18 | var t,
19 | s = e.selectionText,
20 | c = e.editable;
21 | console.log("selectedText", s),
22 | console.log("isEditable", c),
23 | s &&
24 | ((t = { code: 1, isEditable: c, message: s }),
25 | chrome.tabs.query({ active: !0, currentWindow: !0 }, function (e) {
26 | chrome.tabs.sendMessage(e[0].id, t);
27 | }));
28 | },
29 | });
30 | })();
31 | //# sourceMappingURL=background.min.js.map
32 |
--------------------------------------------------------------------------------
/dist/js/background.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"background.min.js","sources":["../../src/background.js"],"sourcesContent":["// eslint-disable-next-line no-undef\nchrome.contextMenus.create({\n title: \"Text Formatting\",\n contexts: [\"all\"],\n onclick: (val) => {\n const selectedText = val.selectionText;\n const isEditable = val.editable;\n\n console.log(\"selectedText\", selectedText);\n console.log(\"isEditable\", isEditable);\n\n if (selectedText) {\n sendMsgToContentScript({\n code: 1,\n isEditable,\n message: selectedText,\n });\n }\n },\n});\n\nfunction sendMsgToContentScript(data) {\n // eslint-disable-next-line no-undef\n chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {\n // eslint-disable-next-line no-undef\n chrome.tabs.sendMessage(tabs[0].id, data);\n });\n}\n\n// function addSpaceBetweenChineseAndEnglish(text) {\n// // 使用正则表达式进行匹配和替换\n// var modifiedText = text.replace(/([\\u4e00-\\u9fa5])([a-zA-Z1-9])/g, '$1 $2');\n// modifiedText = modifiedText.replace(/([a-zA-Z1-9])([\\u4e00-\\u9fa5])/g, '$1 $2');\n\n// return modifiedText;\n// }\n"],"names":["chrome","contextMenus","create","title","contexts","onclick","val","data","selectedText","selectionText","isEditable","editable","console","log","code","message","tabs","query","active","currentWindow","sendMessage","id"],"mappings":"8SACAA,OAAOC,aAAaC,OAAO,CACzBC,MAAO,kBACPC,SAAU,CAAC,OACXC,QAAS,SAACC,GACR,IAgB4BC,EAhBtBC,EAAeF,EAAIG,cACnBC,EAAaJ,EAAIK,SAEvBC,QAAQC,IAAI,eAAgBL,GAC5BI,QAAQC,IAAI,aAAcH,GAEtBF,IAUwBD,EATH,CACrBO,KAAM,EACNJ,WAAAA,EACAK,QAASP,GAQfR,OAAOgB,KAAKC,MAAM,CAAEC,QAAAA,EAAcC,eAAe,IAAA,SAAkBH,GAEjEhB,OAAOgB,KAAKI,YAAYJ,EAAK,GAAGK,GAAId,EAPtC,IAAA"}
--------------------------------------------------------------------------------
/dist/js/content.min.js:
--------------------------------------------------------------------------------
1 | !(function (e, t) {
2 | e &&
3 | !e.getElementById("livereloadscript") &&
4 | (((t = e.createElement("script")).async = 1),
5 | (t.src =
6 | "//" +
7 | (self.location.host || "localhost").split(":")[0] +
8 | ":35729/livereload.js?snipver=1"),
9 | (t.id = "livereloadscript"),
10 | e.getElementsByTagName("head")[0].appendChild(t));
11 | })(self.document),
12 | (function () {
13 | "use strict";
14 | console.info("【格式化插件】Text formatting init.");
15 | var e = (function (e) {
16 | switch (((e = e || location.href), !0)) {
17 | case /docs\.google\.com\/spreadsheets/i.test(e):
18 | return "GoogleExcel";
19 | case /docs\.google\.com\/document/i.test(e):
20 | return "GoogleDocs";
21 | case /shimo\.im\/docs/i.test(e):
22 | return "Shimo";
23 | case /yuque\.com/i.test(e):
24 | return "Yueque";
25 | default:
26 | return "Unknown";
27 | }
28 | })(),
29 | t = window.getSelection();
30 | console.info("【格式化插件】当前插件宿主:", e),
31 | chrome.runtime.onMessage.addListener(function (o) {
32 | if ((console.log("request", o), 1 === o.code)) {
33 | var n = pangu.spacing(o.message);
34 | if (
35 | (console.log("【格式化插件】替换前文本:", o.message),
36 | console.log("【格式化插件】替换后文本:", n),
37 | n && o.isEditable)
38 | ) {
39 | if (1 == ("GoogleExcel" === e || "Shimo" === e))
40 | return void (function (e) {
41 | if (window.getSelection && t.rangeCount > 0) {
42 | var o = t.getRangeAt(0),
43 | n = document.createTextNode(e);
44 | o.deleteContents(),
45 | o.insertNode(n),
46 | t.removeAllRanges(),
47 | t.addRange(o);
48 | } else
49 | document.selection &&
50 | "Control" != document.selection.type &&
51 | (document.selection.createRange().text = e);
52 | })(n);
53 | console.log("【格式化插件】通用框逻辑"),
54 | (function (e) {
55 | var t = document.activeElement;
56 | if ("INPUT" === t.tagName || "TEXTAREA" === t.tagName) {
57 | var o = t.selectionStart,
58 | n = t.selectionEnd,
59 | s = t.value.substring(0, o) + e + t.value.substring(n);
60 | t.value = s;
61 | var i = o + e.length;
62 | t.setSelectionRange(i, i);
63 | }
64 | })(n);
65 | }
66 | }
67 | });
68 | })();
69 | //# sourceMappingURL=content.min.js.map
70 |
--------------------------------------------------------------------------------
/dist/js/content.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"file":"content.min.js","sources":["../../src/content.js","../../src/common/util.js"],"sourcesContent":["import { businessJudgment } from \"./common/util\";\n\nconsole.info(\"【格式化插件】Text formatting init.\");\n\nconst business = businessJudgment();\nconst selection = window.getSelection();\n\nconsole.info(\"【格式化插件】当前插件宿主:\", business);\n\n// eslint-disable-next-line no-undef\nchrome.runtime.onMessage.addListener(function (request) {\n console.log(\"request\", request);\n\n // 接收到 background 传递的替换信息\n if (request.code === 1) {\n // eslint-disable-next-line no-undef\n var replacedText = pangu.spacing(request.message); // 要替换的文本\n console.log(\"【格式化插件】替换前文本:\", request.message);\n console.log(\"【格式化插件】替换后文本:\", replacedText);\n\n if (replacedText && request.isEditable) {\n switch (true) {\n case business === \"GoogleExcel\" || business === \"Shimo\":\n googleExcelreplaceSelectedText(replacedText);\n return;\n default:\n break;\n }\n console.log(\"【格式化插件】通用框逻辑\");\n replaceSelectedText(replacedText);\n }\n }\n});\n\n// eslint-disable-next-line no-unused-vars\nfunction getSelectedText() {\n // 获取当前活动的输入框\n var inputElement = document.activeElement;\n var selectedText = \"\";\n\n if (inputElement.tagName === \"INPUT\" || inputElement.tagName === \"TEXTAREA\") {\n selectedText = inputElement.value.substring(\n inputElement.selectionStart,\n inputElement.selectionEnd,\n );\n }\n\n return selectedText;\n}\n\n// 替换选中的文本片段\nfunction replaceSelectedText(replacement) {\n // 获取当前活动的输入框\n var inputElement = document.activeElement;\n\n if (inputElement.tagName === \"INPUT\" || inputElement.tagName === \"TEXTAREA\") {\n var start = inputElement.selectionStart;\n var end = inputElement.selectionEnd;\n\n var newValue =\n inputElement.value.substring(0, start) +\n replacement +\n inputElement.value.substring(end);\n\n inputElement.value = newValue;\n\n // 重新设置光标位置\n var newCursorPosition = start + replacement.length;\n inputElement.setSelectionRange(newCursorPosition, newCursorPosition);\n }\n}\n\nfunction googleExcelreplaceSelectedText(newText) {\n if (window.getSelection && selection.rangeCount > 0) {\n const range = selection.getRangeAt(0);\n const newTextNode = document.createTextNode(newText);\n range.deleteContents();\n range.insertNode(newTextNode);\n selection.removeAllRanges();\n selection.addRange(range);\n } else if (document.selection && document.selection.type != \"Control\") {\n document.selection.createRange().text = newText;\n }\n}\n\n// console.info(\"Text formatting end.\");\n","export function businessJudgment(domain) {\n domain = domain || location.href;\n\n const excelRegex = /docs\\.google\\.com\\/spreadsheets/i;\n const docsRegex = /docs\\.google\\.com\\/document/i;\n const shimoRegex = /shimo\\.im\\/docs/i;\n const yuqueRegex = /yuque\\.com/i;\n\n switch (true) {\n case excelRegex.test(domain):\n return \"GoogleExcel\";\n case docsRegex.test(domain):\n return \"GoogleDocs\";\n case shimoRegex.test(domain):\n return \"Shimo\";\n case yuqueRegex.test(domain):\n return \"Yueque\";\n default:\n return \"Unknown\";\n }\n}\n"],"names":["console","info","business","domain","location","href","test","selection","window","getSelection","chrome","runtime","onMessage","addListener","request","log","code","replacedText","pangu","spacing","message","isEditable","newText","rangeCount","range","getRangeAt","newTextNode","document","createTextNode","deleteContents","insertNode","removeAllRanges","addRange","type","createRange","text","replacement","inputElement","activeElement","tagName","start","selectionStart","end","selectionEnd","newValue","value","substring","newCursorPosition","length","setSelectionRange"],"mappings":"8SAEAA,QAAQC,KAAK,gCAEb,IAAMC,ECJC,SAA0BC,GAQ/B,OAPAA,EAASA,GAAUC,SAASC,MAOpB,GACN,IANiB,mCAMDC,KAAKH,GACnB,MAAO,cACT,IAPgB,+BAODG,KAAKH,GAClB,MAAO,aACT,IARiB,mBAQDG,KAAKH,GACnB,MAAO,QACT,IATiB,cASDG,KAAKH,GACnB,MAAO,SACT,QACE,MAAO,UAEb,CApBO,GDKDI,EAAYC,OAAOC,eAEzBT,QAAQC,KAAK,iBAAkBC,GAG/BQ,OAAOC,QAAQC,UAAUC,aAAAA,SAAsBC,GAI7C,GAHAd,QAAQe,IAAI,UAAWD,GAGF,IAAjBA,EAAQE,KAAY,CAEtB,IAAIC,EAAeC,MAAMC,QAAQL,EAAQM,SAIzC,GAHApB,QAAQe,IAAI,gBAAiBD,EAAQM,SACrCpB,QAAQe,IAAI,gBAAiBE,GAEzBA,GAAgBH,EAAQO,WAAY,CACtC,GAAA,IACoB,gBAAbnB,GAA2C,UAAbA,GAEjC,YAgDV,SAAwCoB,GACtC,GAAId,OAAOC,cAAgBF,EAAUgB,WAAa,EAAG,CACnD,IAAMC,EAAQjB,EAAUkB,WAAW,GAC7BC,EAAcC,SAASC,eAAeN,GAC5CE,EAAMK,iBACNL,EAAMM,WAAWJ,GACjBnB,EAAUwB,kBACVxB,EAAUyB,SAASR,EACrB,MAAWG,SAASpB,WAAwC,WAA3BoB,SAASpB,UAAU0B,OAClDN,SAASpB,UAAU2B,cAAcC,KAAOb,EAE5C,CAXA,CAjDyCL,GAKnCjB,QAAQe,IAAI,gBAuBlB,SAA6BqB,GAE3B,IAAIC,EAAeV,SAASW,cAE5B,GAA6B,UAAzBD,EAAaE,SAAgD,aAAzBF,EAAaE,QAAwB,CAC3E,IAAIC,EAAQH,EAAaI,eACrBC,EAAML,EAAaM,aAEnBC,EACFP,EAAaQ,MAAMC,UAAU,EAAGN,GAChCJ,EACAC,EAAaQ,MAAMC,UAAUJ,GAE/BL,EAAaQ,MAAQD,EAGrB,IAAIG,EAAoBP,EAAQJ,EAAYY,OAC5CX,EAAaY,kBAAkBF,EAAmBA,EACpD,CACF,CAnBA,CAtB0B9B,EACtB,CACF,CACF"}
--------------------------------------------------------------------------------
/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chokcoco/chrome-extension-text-formatting/51adcd84513c5572c75b92f1710055b5645d745d/icon-128.png
--------------------------------------------------------------------------------
/icon-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chokcoco/chrome-extension-text-formatting/51adcd84513c5572c75b92f1710055b5645d745d/icon-16.png
--------------------------------------------------------------------------------
/images/demo-google-excel.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chokcoco/chrome-extension-text-formatting/51adcd84513c5572c75b92f1710055b5645d745d/images/demo-google-excel.gif
--------------------------------------------------------------------------------
/images/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chokcoco/chrome-extension-text-formatting/51adcd84513c5572c75b92f1710055b5645d745d/images/demo.gif
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Text Formatting",
3 | "version": "1.0.0",
4 | "author": "Coco",
5 | "manifest_version": 2,
6 | "description": "Format your text anytime in the web editor.",
7 | "permissions": [
8 | "contextMenus",
9 | "http://*/",
10 | "http://*/*",
11 | "https://*/",
12 | "https://*/*"
13 | ],
14 | "icons": {
15 | "16": "icon-16.png",
16 | "128": "icon-128.png"
17 | },
18 | "browser_action": {
19 | "default_icon": "icon-16.png",
20 | "default_popup": "dist/html/popup.html"
21 | },
22 | "background": {
23 | "scripts": ["dist/js/background.min.js"]
24 | },
25 | "content_scripts": [
26 | {
27 | "matches": ["http://*/", "https://*/", "http://*/*", "https://*/*"],
28 | "js": ["src/pangu.js", "dist/js/content.min.js"]
29 | }
30 | ]
31 | }
32 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "text-formatting",
3 | "version": "1.0.0",
4 | "description": "Format your text anytime in the web editor.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1",
8 | "build": "npm run build:browser && npm run build:es && npm run build:umd",
9 | "build:browser": "rollup -c build/rollup.config.browser.mjs -w",
10 | "build:es": "rollup --config build/rollup.config.es.mjs",
11 | "build:umd": "rollup --config build/rollup.config.umd.mjs",
12 | "lint": "eslint --ext .js, ./src/*.js",
13 | "prettier": "prettier --write *.{js,md,json} src/*.js"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/chokcoco/chrome-extension-text-formatting.git"
18 | },
19 | "author": "Coco",
20 | "license": "ISC",
21 | "bugs": {
22 | "url": "https://github.com/chokcoco/chrome-extension-text-formatting/issues"
23 | },
24 | "homepage": "https://github.com/chokcoco/chrome-extension-text-formatting#readme",
25 | "dependencies": {
26 | "rollup": "^4.1.4"
27 | },
28 | "devDependencies": {
29 | "@babel/core": "^7.23.2",
30 | "@babel/preset-env": "^7.23.2",
31 | "@commitlint/cli": "^17.6.5",
32 | "@commitlint/config-conventional": "^17.6.5",
33 | "@rollup/plugin-babel": "^6.0.4",
34 | "@rollup/plugin-commonjs": "^25.0.7",
35 | "@rollup/plugin-node-resolve": "^15.2.3",
36 | "eslint": "^8.52.0",
37 | "husky": "^8.0.3",
38 | "lint-staged": "^15.0.2",
39 | "prettier": "3.0.3",
40 | "rollup-plugin-livereload": "^2.0.5",
41 | "rollup-plugin-terser": "^7.0.2"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/background.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line no-undef
2 | chrome.contextMenus.create({
3 | title: "Text Formatting",
4 | contexts: ["all"],
5 | onclick: (val) => {
6 | const selectedText = val.selectionText;
7 | const isEditable = val.editable;
8 |
9 | console.log("selectedText", selectedText);
10 | console.log("isEditable", isEditable);
11 |
12 | if (selectedText) {
13 | sendMsgToContentScript({
14 | code: 1,
15 | isEditable,
16 | message: selectedText,
17 | });
18 | }
19 | },
20 | });
21 |
22 | function sendMsgToContentScript(data) {
23 | // eslint-disable-next-line no-undef
24 | chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
25 | // eslint-disable-next-line no-undef
26 | chrome.tabs.sendMessage(tabs[0].id, data);
27 | });
28 | }
29 |
30 | // function addSpaceBetweenChineseAndEnglish(text) {
31 | // // 使用正则表达式进行匹配和替换
32 | // var modifiedText = text.replace(/([\u4e00-\u9fa5])([a-zA-Z1-9])/g, '$1 $2');
33 | // modifiedText = modifiedText.replace(/([a-zA-Z1-9])([\u4e00-\u9fa5])/g, '$1 $2');
34 |
35 | // return modifiedText;
36 | // }
37 |
--------------------------------------------------------------------------------
/src/common/util.js:
--------------------------------------------------------------------------------
1 | export function businessJudgment(domain) {
2 | domain = domain || location.href;
3 |
4 | const excelRegex = /docs\.google\.com\/spreadsheets/i;
5 | const docsRegex = /docs\.google\.com\/document/i;
6 | const shimoRegex = /shimo\.im\/docs/i;
7 | const yuqueRegex = /yuque\.com/i;
8 |
9 | switch (true) {
10 | case excelRegex.test(domain):
11 | return "GoogleExcel";
12 | case docsRegex.test(domain):
13 | return "GoogleDocs";
14 | case shimoRegex.test(domain):
15 | return "Shimo";
16 | case yuqueRegex.test(domain):
17 | return "Yueque";
18 | default:
19 | return "Unknown";
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/content.js:
--------------------------------------------------------------------------------
1 | import { businessJudgment } from "./common/util";
2 |
3 | console.info("【格式化插件】Text formatting init.");
4 |
5 | const business = businessJudgment();
6 | const selection = window.getSelection();
7 |
8 | console.info("【格式化插件】当前插件宿主:", business);
9 |
10 | // eslint-disable-next-line no-undef
11 | chrome.runtime.onMessage.addListener(function (request) {
12 | console.log("request", request);
13 |
14 | // 接收到 background 传递的替换信息
15 | if (request.code === 1) {
16 | // eslint-disable-next-line no-undef
17 | var replacedText = pangu.spacing(request.message); // 要替换的文本
18 | console.log("【格式化插件】替换前文本:", request.message);
19 | console.log("【格式化插件】替换后文本:", replacedText);
20 |
21 | if (replacedText && request.isEditable) {
22 | switch (true) {
23 | case business === "GoogleExcel" || business === "Shimo":
24 | googleExcelreplaceSelectedText(replacedText);
25 | return;
26 | default:
27 | break;
28 | }
29 | console.log("【格式化插件】通用框逻辑");
30 | replaceSelectedText(replacedText);
31 | }
32 | }
33 | });
34 |
35 | // eslint-disable-next-line no-unused-vars
36 | function getSelectedText() {
37 | // 获取当前活动的输入框
38 | var inputElement = document.activeElement;
39 | var selectedText = "";
40 |
41 | if (inputElement.tagName === "INPUT" || inputElement.tagName === "TEXTAREA") {
42 | selectedText = inputElement.value.substring(
43 | inputElement.selectionStart,
44 | inputElement.selectionEnd,
45 | );
46 | }
47 |
48 | return selectedText;
49 | }
50 |
51 | // 替换选中的文本片段
52 | function replaceSelectedText(replacement) {
53 | // 获取当前活动的输入框
54 | var inputElement = document.activeElement;
55 |
56 | if (inputElement.tagName === "INPUT" || inputElement.tagName === "TEXTAREA") {
57 | var start = inputElement.selectionStart;
58 | var end = inputElement.selectionEnd;
59 |
60 | var newValue =
61 | inputElement.value.substring(0, start) +
62 | replacement +
63 | inputElement.value.substring(end);
64 |
65 | inputElement.value = newValue;
66 |
67 | // 重新设置光标位置
68 | var newCursorPosition = start + replacement.length;
69 | inputElement.setSelectionRange(newCursorPosition, newCursorPosition);
70 | }
71 | }
72 |
73 | function googleExcelreplaceSelectedText(newText) {
74 | if (window.getSelection && selection.rangeCount > 0) {
75 | const range = selection.getRangeAt(0);
76 | const newTextNode = document.createTextNode(newText);
77 | range.deleteContents();
78 | range.insertNode(newTextNode);
79 | selection.removeAllRanges();
80 | selection.addRange(range);
81 | } else if (document.selection && document.selection.type != "Control") {
82 | document.selection.createRange().text = newText;
83 | }
84 | }
85 |
86 | // console.info("Text formatting end.");
87 |
--------------------------------------------------------------------------------
/src/pangu.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * pangu.js
3 | * --------
4 | * @version: 4.0.7
5 | * @homepage: https://github.com/vinta/pangu.js
6 | * @license: MIT
7 | * @author: Vinta Chen (https://github.com/vinta)
8 | */
9 | (function webpackUniversalModuleDefinition(root, factory) {
10 | if (typeof exports === "object" && typeof module === "object")
11 | module.exports = factory();
12 | else if (typeof define === "function" && define.amd)
13 | define("pangu", [], factory);
14 | else if (typeof exports === "object") exports["pangu"] = factory();
15 | else root["pangu"] = factory();
16 | })(window, function () {
17 | return /******/ (function (modules) {
18 | // webpackBootstrap
19 | /******/ // The module cache
20 | /******/ var installedModules = {};
21 | /******/
22 | /******/ // The require function
23 | /******/ function __webpack_require__(moduleId) {
24 | /******/
25 | /******/ // Check if module is in cache
26 | /******/ if (installedModules[moduleId]) {
27 | /******/ return installedModules[moduleId].exports;
28 | /******/
29 | }
30 | /******/ // Create a new module (and put it into the cache)
31 | /******/ var module = (installedModules[moduleId] = {
32 | /******/ i: moduleId,
33 | /******/ l: false,
34 | /******/ exports: {},
35 | /******/
36 | });
37 | /******/
38 | /******/ // Execute the module function
39 | /******/ modules[moduleId].call(
40 | module.exports,
41 | module,
42 | module.exports,
43 | __webpack_require__,
44 | );
45 | /******/
46 | /******/ // Flag the module as loaded
47 | /******/ module.l = true;
48 | /******/
49 | /******/ // Return the exports of the module
50 | /******/ return module.exports;
51 | /******/
52 | }
53 | /******/
54 | /******/
55 | /******/ // expose the modules object (__webpack_modules__)
56 | /******/ __webpack_require__.m = modules;
57 | /******/
58 | /******/ // expose the module cache
59 | /******/ __webpack_require__.c = installedModules;
60 | /******/
61 | /******/ // define getter function for harmony exports
62 | /******/ __webpack_require__.d = function (exports, name, getter) {
63 | /******/ if (!__webpack_require__.o(exports, name)) {
64 | /******/ Object.defineProperty(exports, name, {
65 | enumerable: true,
66 | get: getter,
67 | });
68 | /******/
69 | }
70 | /******/
71 | };
72 | /******/
73 | /******/ // define __esModule on exports
74 | /******/ __webpack_require__.r = function (exports) {
75 | /******/ if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
76 | /******/ Object.defineProperty(exports, Symbol.toStringTag, {
77 | value: "Module",
78 | });
79 | /******/
80 | }
81 | /******/ Object.defineProperty(exports, "__esModule", { value: true });
82 | /******/
83 | };
84 | /******/
85 | /******/ // create a fake namespace object
86 | /******/ // mode & 1: value is a module id, require it
87 | /******/ // mode & 2: merge all properties of value into the ns
88 | /******/ // mode & 4: return value when already ns object
89 | /******/ // mode & 8|1: behave like require
90 | /******/ __webpack_require__.t = function (value, mode) {
91 | /******/ if (mode & 1) value = __webpack_require__(value);
92 | /******/ if (mode & 8) return value;
93 | /******/ if (
94 | mode & 4 &&
95 | typeof value === "object" &&
96 | value &&
97 | value.__esModule
98 | )
99 | return value;
100 | /******/ var ns = Object.create(null);
101 | /******/ __webpack_require__.r(ns);
102 | /******/ Object.defineProperty(ns, "default", {
103 | enumerable: true,
104 | value: value,
105 | });
106 | /******/ if (mode & 2 && typeof value != "string")
107 | for (var key in value)
108 | __webpack_require__.d(
109 | ns,
110 | key,
111 | function (key) {
112 | return value[key];
113 | }.bind(null, key),
114 | );
115 | /******/ return ns;
116 | /******/
117 | };
118 | /******/
119 | /******/ // getDefaultExport function for compatibility with non-harmony modules
120 | /******/ __webpack_require__.n = function (module) {
121 | /******/ var getter =
122 | module && module.__esModule
123 | ? /******/ function getDefault() {
124 | return module["default"];
125 | }
126 | : /******/ function getModuleExports() {
127 | return module;
128 | };
129 | /******/ __webpack_require__.d(getter, "a", getter);
130 | /******/ return getter;
131 | /******/
132 | };
133 | /******/
134 | /******/ // Object.prototype.hasOwnProperty.call
135 | /******/ __webpack_require__.o = function (object, property) {
136 | return Object.prototype.hasOwnProperty.call(object, property);
137 | };
138 | /******/
139 | /******/ // __webpack_public_path__
140 | /******/ __webpack_require__.p = "";
141 | /******/
142 | /******/
143 | /******/ // Load entry module and return exports
144 | /******/ return __webpack_require__((__webpack_require__.s = 0));
145 | /******/
146 | })(
147 | /************************************************************************/
148 | /******/ [
149 | /* 0 */
150 | /***/ function (module, exports, __webpack_require__) {
151 | var __WEBPACK_AMD_DEFINE_FACTORY__,
152 | __WEBPACK_AMD_DEFINE_ARRAY__,
153 | __WEBPACK_AMD_DEFINE_RESULT__;
154 | (function (global, factory) {
155 | if (true) {
156 | !((__WEBPACK_AMD_DEFINE_ARRAY__ = []),
157 | (__WEBPACK_AMD_DEFINE_FACTORY__ = factory),
158 | (__WEBPACK_AMD_DEFINE_RESULT__ =
159 | typeof __WEBPACK_AMD_DEFINE_FACTORY__ === "function"
160 | ? __WEBPACK_AMD_DEFINE_FACTORY__.apply(
161 | exports,
162 | __WEBPACK_AMD_DEFINE_ARRAY__,
163 | )
164 | : __WEBPACK_AMD_DEFINE_FACTORY__),
165 | __WEBPACK_AMD_DEFINE_RESULT__ !== undefined &&
166 | (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
167 | } else {
168 | var mod;
169 | }
170 | })(this, function () {
171 | "use strict";
172 |
173 | function _typeof(obj) {
174 | if (
175 | typeof Symbol === "function" &&
176 | typeof Symbol.iterator === "symbol"
177 | ) {
178 | _typeof = function _typeof(obj) {
179 | return typeof obj;
180 | };
181 | } else {
182 | _typeof = function _typeof(obj) {
183 | return obj &&
184 | typeof Symbol === "function" &&
185 | obj.constructor === Symbol &&
186 | obj !== Symbol.prototype
187 | ? "symbol"
188 | : typeof obj;
189 | };
190 | }
191 | return _typeof(obj);
192 | }
193 |
194 | function _classCallCheck(instance, Constructor) {
195 | if (!(instance instanceof Constructor)) {
196 | throw new TypeError("Cannot call a class as a function");
197 | }
198 | }
199 |
200 | function _defineProperties(target, props) {
201 | for (var i = 0; i < props.length; i++) {
202 | var descriptor = props[i];
203 | descriptor.enumerable = descriptor.enumerable || false;
204 | descriptor.configurable = true;
205 | if ("value" in descriptor) descriptor.writable = true;
206 | Object.defineProperty(target, descriptor.key, descriptor);
207 | }
208 | }
209 |
210 | function _createClass(Constructor, protoProps, staticProps) {
211 | if (protoProps)
212 | _defineProperties(Constructor.prototype, protoProps);
213 | if (staticProps) _defineProperties(Constructor, staticProps);
214 | return Constructor;
215 | }
216 |
217 | function _possibleConstructorReturn(self, call) {
218 | if (
219 | call &&
220 | (_typeof(call) === "object" || typeof call === "function")
221 | ) {
222 | return call;
223 | }
224 | return _assertThisInitialized(self);
225 | }
226 |
227 | function _assertThisInitialized(self) {
228 | if (self === void 0) {
229 | throw new ReferenceError(
230 | "this hasn't been initialised - super() hasn't been called",
231 | );
232 | }
233 | return self;
234 | }
235 |
236 | function _getPrototypeOf(o) {
237 | _getPrototypeOf = Object.setPrototypeOf
238 | ? Object.getPrototypeOf
239 | : function _getPrototypeOf(o) {
240 | return o.__proto__ || Object.getPrototypeOf(o);
241 | };
242 | return _getPrototypeOf(o);
243 | }
244 |
245 | function _inherits(subClass, superClass) {
246 | if (typeof superClass !== "function" && superClass !== null) {
247 | throw new TypeError(
248 | "Super expression must either be null or a function",
249 | );
250 | }
251 | subClass.prototype = Object.create(
252 | superClass && superClass.prototype,
253 | {
254 | constructor: {
255 | value: subClass,
256 | writable: true,
257 | configurable: true,
258 | },
259 | },
260 | );
261 | if (superClass) _setPrototypeOf(subClass, superClass);
262 | }
263 |
264 | function _setPrototypeOf(o, p) {
265 | _setPrototypeOf =
266 | Object.setPrototypeOf ||
267 | function _setPrototypeOf(o, p) {
268 | o.__proto__ = p;
269 | return o;
270 | };
271 | return _setPrototypeOf(o, p);
272 | }
273 |
274 | var _require = __webpack_require__(1),
275 | Pangu = _require.Pangu;
276 |
277 | function once(func) {
278 | var _this = this,
279 | _arguments = arguments;
280 |
281 | var executed = false;
282 | return function () {
283 | if (executed) {
284 | return;
285 | }
286 |
287 | var self = _this;
288 | executed = true;
289 | func.apply(self, _arguments);
290 | };
291 | }
292 |
293 | function debounce(func, delay, mustRunDelay) {
294 | var _this2 = this,
295 | _arguments2 = arguments;
296 |
297 | var timer = null;
298 | var startTime = null;
299 | return function () {
300 | var self = _this2;
301 | var args = _arguments2;
302 | var currentTime = +new Date();
303 | clearTimeout(timer);
304 |
305 | if (!startTime) {
306 | startTime = currentTime;
307 | }
308 |
309 | if (currentTime - startTime >= mustRunDelay) {
310 | func.apply(self, args);
311 | startTime = currentTime;
312 | } else {
313 | timer = setTimeout(function () {
314 | func.apply(self, args);
315 | }, delay);
316 | }
317 | };
318 | }
319 |
320 | var BrowserPangu = (function (_Pangu) {
321 | _inherits(BrowserPangu, _Pangu);
322 |
323 | function BrowserPangu() {
324 | var _this3;
325 |
326 | _classCallCheck(this, BrowserPangu);
327 |
328 | _this3 = _possibleConstructorReturn(
329 | this,
330 | _getPrototypeOf(BrowserPangu).call(this),
331 | );
332 | _this3.blockTags = /^(div|p|h1|h2|h3|h4|h5|h6)$/i;
333 | _this3.ignoredTags = /^(script|code|pre|textarea)$/i;
334 | _this3.presentationalTags = /^(b|code|del|em|i|s|strong|kbd)$/i;
335 | _this3.spaceLikeTags = /^(br|hr|i|img|pangu)$/i;
336 | _this3.spaceSensitiveTags = /^(a|del|pre|s|strike|u)$/i;
337 | _this3.isAutoSpacingPageExecuted = false;
338 | return _this3;
339 | }
340 |
341 | _createClass(BrowserPangu, [
342 | {
343 | key: "isContentEditable",
344 | value: function isContentEditable(node) {
345 | return (
346 | node.isContentEditable ||
347 | (node.getAttribute &&
348 | node.getAttribute("g_editable") === "true")
349 | );
350 | },
351 | },
352 | {
353 | key: "isSpecificTag",
354 | value: function isSpecificTag(node, tagRegex) {
355 | return (
356 | node && node.nodeName && node.nodeName.search(tagRegex) >= 0
357 | );
358 | },
359 | },
360 | {
361 | key: "isInsideSpecificTag",
362 | value: function isInsideSpecificTag(node, tagRegex) {
363 | var checkCurrent =
364 | arguments.length > 2 && arguments[2] !== undefined
365 | ? arguments[2]
366 | : false;
367 | var currentNode = node;
368 |
369 | if (checkCurrent) {
370 | if (this.isSpecificTag(currentNode, tagRegex)) {
371 | return true;
372 | }
373 | }
374 |
375 | while (currentNode.parentNode) {
376 | currentNode = currentNode.parentNode;
377 |
378 | if (this.isSpecificTag(currentNode, tagRegex)) {
379 | return true;
380 | }
381 | }
382 |
383 | return false;
384 | },
385 | },
386 | {
387 | key: "canIgnoreNode",
388 | value: function canIgnoreNode(node) {
389 | var currentNode = node;
390 |
391 | if (
392 | currentNode &&
393 | (this.isSpecificTag(currentNode, this.ignoredTags) ||
394 | this.isContentEditable(currentNode))
395 | ) {
396 | return true;
397 | }
398 |
399 | while (currentNode.parentNode) {
400 | currentNode = currentNode.parentNode;
401 |
402 | if (
403 | currentNode &&
404 | (this.isSpecificTag(currentNode, this.ignoredTags) ||
405 | this.isContentEditable(currentNode))
406 | ) {
407 | return true;
408 | }
409 | }
410 |
411 | return false;
412 | },
413 | },
414 | {
415 | key: "isFirstTextChild",
416 | value: function isFirstTextChild(parentNode, targetNode) {
417 | var childNodes = parentNode.childNodes;
418 |
419 | for (var i = 0; i < childNodes.length; i++) {
420 | var childNode = childNodes[i];
421 |
422 | if (
423 | childNode.nodeType !== Node.COMMENT_NODE &&
424 | childNode.textContent
425 | ) {
426 | return childNode === targetNode;
427 | }
428 | }
429 |
430 | return false;
431 | },
432 | },
433 | {
434 | key: "isLastTextChild",
435 | value: function isLastTextChild(parentNode, targetNode) {
436 | var childNodes = parentNode.childNodes;
437 |
438 | for (var i = childNodes.length - 1; i > -1; i--) {
439 | var childNode = childNodes[i];
440 |
441 | if (
442 | childNode.nodeType !== Node.COMMENT_NODE &&
443 | childNode.textContent
444 | ) {
445 | return childNode === targetNode;
446 | }
447 | }
448 |
449 | return false;
450 | },
451 | },
452 | {
453 | key: "spacingNodeByXPath",
454 | value: function spacingNodeByXPath(xPathQuery, contextNode) {
455 | if (
456 | !(contextNode instanceof Node) ||
457 | contextNode instanceof DocumentFragment
458 | ) {
459 | return;
460 | }
461 |
462 | var textNodes = document.evaluate(
463 | xPathQuery,
464 | contextNode,
465 | null,
466 | XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
467 | null,
468 | );
469 | var currentTextNode;
470 | var nextTextNode;
471 |
472 | for (var i = textNodes.snapshotLength - 1; i > -1; --i) {
473 | currentTextNode = textNodes.snapshotItem(i);
474 |
475 | if (
476 | this.isSpecificTag(
477 | currentTextNode.parentNode,
478 | this.presentationalTags,
479 | ) &&
480 | !this.isInsideSpecificTag(
481 | currentTextNode.parentNode,
482 | this.ignoredTags,
483 | )
484 | ) {
485 | var elementNode = currentTextNode.parentNode;
486 |
487 | if (elementNode.previousSibling) {
488 | var previousSibling = elementNode.previousSibling;
489 |
490 | if (previousSibling.nodeType === Node.TEXT_NODE) {
491 | var testText =
492 | previousSibling.data.substr(-1) +
493 | currentTextNode.data.toString().charAt(0);
494 | var testNewText = this.spacing(testText);
495 |
496 | if (testText !== testNewText) {
497 | previousSibling.data = "".concat(
498 | previousSibling.data,
499 | " ",
500 | );
501 | }
502 | }
503 | }
504 |
505 | if (elementNode.nextSibling) {
506 | var nextSibling = elementNode.nextSibling;
507 |
508 | if (nextSibling.nodeType === Node.TEXT_NODE) {
509 | var _testText =
510 | currentTextNode.data.substr(-1) +
511 | nextSibling.data.toString().charAt(0);
512 |
513 | var _testNewText = this.spacing(_testText);
514 |
515 | if (_testText !== _testNewText) {
516 | nextSibling.data = " ".concat(nextSibling.data);
517 | }
518 | }
519 | }
520 | }
521 |
522 | if (this.canIgnoreNode(currentTextNode)) {
523 | nextTextNode = currentTextNode;
524 | continue;
525 | }
526 |
527 | var newText = this.spacing(currentTextNode.data);
528 |
529 | if (currentTextNode.data !== newText) {
530 | currentTextNode.data = newText;
531 | }
532 |
533 | if (nextTextNode) {
534 | if (
535 | currentTextNode.nextSibling &&
536 | currentTextNode.nextSibling.nodeName.search(
537 | this.spaceLikeTags,
538 | ) >= 0
539 | ) {
540 | nextTextNode = currentTextNode;
541 | continue;
542 | }
543 |
544 | var _testText2 =
545 | currentTextNode.data.toString().substr(-1) +
546 | nextTextNode.data.toString().substr(0, 1);
547 |
548 | var _testNewText2 = this.spacing(_testText2);
549 |
550 | if (_testNewText2 !== _testText2) {
551 | var nextNode = nextTextNode;
552 |
553 | while (
554 | nextNode.parentNode &&
555 | nextNode.nodeName.search(this.spaceSensitiveTags) ===
556 | -1 &&
557 | this.isFirstTextChild(nextNode.parentNode, nextNode)
558 | ) {
559 | nextNode = nextNode.parentNode;
560 | }
561 |
562 | var currentNode = currentTextNode;
563 |
564 | while (
565 | currentNode.parentNode &&
566 | currentNode.nodeName.search(
567 | this.spaceSensitiveTags,
568 | ) === -1 &&
569 | this.isLastTextChild(
570 | currentNode.parentNode,
571 | currentNode,
572 | )
573 | ) {
574 | currentNode = currentNode.parentNode;
575 | }
576 |
577 | if (currentNode.nextSibling) {
578 | if (
579 | currentNode.nextSibling.nodeName.search(
580 | this.spaceLikeTags,
581 | ) >= 0
582 | ) {
583 | nextTextNode = currentTextNode;
584 | continue;
585 | }
586 | }
587 |
588 | if (
589 | currentNode.nodeName.search(this.blockTags) === -1
590 | ) {
591 | if (
592 | nextNode.nodeName.search(
593 | this.spaceSensitiveTags,
594 | ) === -1
595 | ) {
596 | if (
597 | nextNode.nodeName.search(this.ignoredTags) ===
598 | -1 &&
599 | nextNode.nodeName.search(this.blockTags) === -1
600 | ) {
601 | if (nextTextNode.previousSibling) {
602 | if (
603 | nextTextNode.previousSibling.nodeName.search(
604 | this.spaceLikeTags,
605 | ) === -1
606 | ) {
607 | nextTextNode.data = " ".concat(
608 | nextTextNode.data,
609 | );
610 | }
611 | } else {
612 | if (!this.canIgnoreNode(nextTextNode)) {
613 | nextTextNode.data = " ".concat(
614 | nextTextNode.data,
615 | );
616 | }
617 | }
618 | }
619 | } else if (
620 | currentNode.nodeName.search(
621 | this.spaceSensitiveTags,
622 | ) === -1
623 | ) {
624 | currentTextNode.data = "".concat(
625 | currentTextNode.data,
626 | " ",
627 | );
628 | } else {
629 | var panguSpace = document.createElement("pangu");
630 | panguSpace.innerHTML = " ";
631 |
632 | if (nextNode.previousSibling) {
633 | if (
634 | nextNode.previousSibling.nodeName.search(
635 | this.spaceLikeTags,
636 | ) === -1
637 | ) {
638 | nextNode.parentNode.insertBefore(
639 | panguSpace,
640 | nextNode,
641 | );
642 | }
643 | } else {
644 | nextNode.parentNode.insertBefore(
645 | panguSpace,
646 | nextNode,
647 | );
648 | }
649 |
650 | if (!panguSpace.previousElementSibling) {
651 | if (panguSpace.parentNode) {
652 | panguSpace.parentNode.removeChild(panguSpace);
653 | }
654 | }
655 | }
656 | }
657 | }
658 | }
659 |
660 | nextTextNode = currentTextNode;
661 | }
662 | },
663 | },
664 | {
665 | key: "spacingNode",
666 | value: function spacingNode(contextNode) {
667 | var xPathQuery = ".//*/text()[normalize-space(.)]";
668 |
669 | if (
670 | contextNode.children &&
671 | contextNode.children.length === 0
672 | ) {
673 | xPathQuery = ".//text()[normalize-space(.)]";
674 | }
675 |
676 | this.spacingNodeByXPath(xPathQuery, contextNode);
677 | },
678 | },
679 | {
680 | key: "spacingElementById",
681 | value: function spacingElementById(idName) {
682 | var xPathQuery = 'id("'.concat(idName, '")//text()');
683 | this.spacingNodeByXPath(xPathQuery, document);
684 | },
685 | },
686 | {
687 | key: "spacingElementByClassName",
688 | value: function spacingElementByClassName(className) {
689 | var xPathQuery =
690 | '//*[contains(concat(" ", normalize-space(@class), " "), "'.concat(
691 | className,
692 | '")]//text()',
693 | );
694 | this.spacingNodeByXPath(xPathQuery, document);
695 | },
696 | },
697 | {
698 | key: "spacingElementByTagName",
699 | value: function spacingElementByTagName(tagName) {
700 | var xPathQuery = "//".concat(tagName, "//text()");
701 | this.spacingNodeByXPath(xPathQuery, document);
702 | },
703 | },
704 | {
705 | key: "spacingPageTitle",
706 | value: function spacingPageTitle() {
707 | var xPathQuery = "/html/head/title/text()";
708 | this.spacingNodeByXPath(xPathQuery, document);
709 | },
710 | },
711 | {
712 | key: "spacingPageBody",
713 | value: function spacingPageBody() {
714 | var xPathQuery = "/html/body//*/text()[normalize-space(.)]";
715 | ["script", "style", "textarea"].forEach(function (tag) {
716 | xPathQuery = ""
717 | .concat(
718 | xPathQuery,
719 | '[translate(name(..),"ABCDEFGHIJKLMNOPQRSTUVWXYZ","abcdefghijklmnopqrstuvwxyz")!="',
720 | )
721 | .concat(tag, '"]');
722 | });
723 | this.spacingNodeByXPath(xPathQuery, document);
724 | },
725 | },
726 | {
727 | key: "spacingPage",
728 | value: function spacingPage() {
729 | this.spacingPageTitle();
730 | this.spacingPageBody();
731 | },
732 | },
733 | {
734 | key: "autoSpacingPage",
735 | value: function autoSpacingPage() {
736 | var pageDelay =
737 | arguments.length > 0 && arguments[0] !== undefined
738 | ? arguments[0]
739 | : 1000;
740 | var nodeDelay =
741 | arguments.length > 1 && arguments[1] !== undefined
742 | ? arguments[1]
743 | : 500;
744 | var nodeMaxWait =
745 | arguments.length > 2 && arguments[2] !== undefined
746 | ? arguments[2]
747 | : 2000;
748 |
749 | if (!(document.body instanceof Node)) {
750 | return;
751 | }
752 |
753 | if (this.isAutoSpacingPageExecuted) {
754 | return;
755 | }
756 |
757 | this.isAutoSpacingPageExecuted = true;
758 | var self = this;
759 | var onceSpacingPage = once(function () {
760 | self.spacingPage();
761 | });
762 | var videos = document.getElementsByTagName("video");
763 |
764 | if (videos.length === 0) {
765 | setTimeout(function () {
766 | onceSpacingPage();
767 | }, pageDelay);
768 | } else {
769 | for (var i = 0; i < videos.length; i++) {
770 | var video = videos[i];
771 |
772 | if (video.readyState === 4) {
773 | setTimeout(function () {
774 | onceSpacingPage();
775 | }, 3000);
776 | break;
777 | }
778 |
779 | video.addEventListener("loadeddata", function () {
780 | setTimeout(function () {
781 | onceSpacingPage();
782 | }, 4000);
783 | });
784 | }
785 | }
786 |
787 | var queue = [];
788 | var debouncedSpacingNodes = debounce(
789 | function () {
790 | while (queue.length) {
791 | var node = queue.shift();
792 |
793 | if (node) {
794 | self.spacingNode(node);
795 | }
796 | }
797 | },
798 | nodeDelay,
799 | {
800 | maxWait: nodeMaxWait,
801 | },
802 | );
803 | var mutationObserver = new MutationObserver(function (
804 | mutations,
805 | observer,
806 | ) {
807 | mutations.forEach(function (mutation) {
808 | switch (mutation.type) {
809 | case "childList":
810 | mutation.addedNodes.forEach(function (node) {
811 | if (node.nodeType === Node.ELEMENT_NODE) {
812 | queue.push(node);
813 | } else if (node.nodeType === Node.TEXT_NODE) {
814 | queue.push(node.parentNode);
815 | }
816 | });
817 | break;
818 |
819 | case "characterData":
820 | var node = mutation.target;
821 |
822 | if (node.nodeType === Node.TEXT_NODE) {
823 | queue.push(node.parentNode);
824 | }
825 |
826 | break;
827 |
828 | default:
829 | break;
830 | }
831 | });
832 | debouncedSpacingNodes();
833 | });
834 | mutationObserver.observe(document.body, {
835 | characterData: true,
836 | childList: true,
837 | subtree: true,
838 | });
839 | },
840 | },
841 | ]);
842 |
843 | return BrowserPangu;
844 | })(Pangu);
845 |
846 | var pangu = new BrowserPangu();
847 | module.exports = pangu;
848 | module.exports.default = pangu;
849 | module.exports.Pangu = BrowserPangu;
850 | });
851 |
852 | /***/
853 | },
854 | /* 1 */
855 | /***/ function (module, exports, __webpack_require__) {
856 | var __WEBPACK_AMD_DEFINE_FACTORY__,
857 | __WEBPACK_AMD_DEFINE_ARRAY__,
858 | __WEBPACK_AMD_DEFINE_RESULT__;
859 | (function (global, factory) {
860 | if (true) {
861 | !((__WEBPACK_AMD_DEFINE_ARRAY__ = []),
862 | (__WEBPACK_AMD_DEFINE_FACTORY__ = factory),
863 | (__WEBPACK_AMD_DEFINE_RESULT__ =
864 | typeof __WEBPACK_AMD_DEFINE_FACTORY__ === "function"
865 | ? __WEBPACK_AMD_DEFINE_FACTORY__.apply(
866 | exports,
867 | __WEBPACK_AMD_DEFINE_ARRAY__,
868 | )
869 | : __WEBPACK_AMD_DEFINE_FACTORY__),
870 | __WEBPACK_AMD_DEFINE_RESULT__ !== undefined &&
871 | (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
872 | } else {
873 | var mod;
874 | }
875 | })(this, function () {
876 | "use strict";
877 |
878 | function _typeof(obj) {
879 | if (
880 | typeof Symbol === "function" &&
881 | typeof Symbol.iterator === "symbol"
882 | ) {
883 | _typeof = function _typeof(obj) {
884 | return typeof obj;
885 | };
886 | } else {
887 | _typeof = function _typeof(obj) {
888 | return obj &&
889 | typeof Symbol === "function" &&
890 | obj.constructor === Symbol &&
891 | obj !== Symbol.prototype
892 | ? "symbol"
893 | : typeof obj;
894 | };
895 | }
896 | return _typeof(obj);
897 | }
898 |
899 | function _classCallCheck(instance, Constructor) {
900 | if (!(instance instanceof Constructor)) {
901 | throw new TypeError("Cannot call a class as a function");
902 | }
903 | }
904 |
905 | function _defineProperties(target, props) {
906 | for (var i = 0; i < props.length; i++) {
907 | var descriptor = props[i];
908 | descriptor.enumerable = descriptor.enumerable || false;
909 | descriptor.configurable = true;
910 | if ("value" in descriptor) descriptor.writable = true;
911 | Object.defineProperty(target, descriptor.key, descriptor);
912 | }
913 | }
914 |
915 | function _createClass(Constructor, protoProps, staticProps) {
916 | if (protoProps)
917 | _defineProperties(Constructor.prototype, protoProps);
918 | if (staticProps) _defineProperties(Constructor, staticProps);
919 | return Constructor;
920 | }
921 |
922 | var CJK =
923 | "\u2E80-\u2EFF\u2F00-\u2FDF\u3040-\u309F\u30A0-\u30FA\u30FC-\u30FF\u3100-\u312F\u3200-\u32FF\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF";
924 | var ANY_CJK = new RegExp("[".concat(CJK, "]"));
925 | var CONVERT_TO_FULLWIDTH_CJK_SYMBOLS_CJK = new RegExp(
926 | "([".concat(CJK, "])[ ]*([\\:]+|\\.)[ ]*([").concat(CJK, "])"),
927 | "g",
928 | );
929 | var CONVERT_TO_FULLWIDTH_CJK_SYMBOLS = new RegExp(
930 | "([".concat(CJK, "])[ ]*([~\\!;,\\?]+)[ ]*"),
931 | "g",
932 | );
933 | var DOTS_CJK = new RegExp(
934 | "([\\.]{2,}|\u2026)([".concat(CJK, "])"),
935 | "g",
936 | );
937 | var FIX_CJK_COLON_ANS = new RegExp(
938 | "([".concat(CJK, "])\\:([A-Z0-9\\(\\)])"),
939 | "g",
940 | );
941 | var CJK_QUOTE = new RegExp("([".concat(CJK, '])([`"\u05F4])'), "g");
942 | var QUOTE_CJK = new RegExp('([`"\u05F4])(['.concat(CJK, "])"), "g");
943 | var FIX_QUOTE_ANY_QUOTE = /([`"\u05f4]+)[ ]*(.+?)[ ]*([`"\u05f4]+)/g;
944 | var CJK_SINGLE_QUOTE_BUT_POSSESSIVE = new RegExp(
945 | "([".concat(CJK, "])('[^s])"),
946 | "g",
947 | );
948 | var SINGLE_QUOTE_CJK = new RegExp("(')([".concat(CJK, "])"), "g");
949 | var FIX_POSSESSIVE_SINGLE_QUOTE = new RegExp(
950 | "([A-Za-z0-9".concat(CJK, "])( )('s)"),
951 | "g",
952 | );
953 | var HASH_ANS_CJK_HASH = new RegExp(
954 | "(["
955 | .concat(CJK, "])(#)([")
956 | .concat(CJK, "]+)(#)([")
957 | .concat(CJK, "])"),
958 | "g",
959 | );
960 | var CJK_HASH = new RegExp("([".concat(CJK, "])(#([^ ]))"), "g");
961 | var HASH_CJK = new RegExp("(([^ ])#)([".concat(CJK, "])"), "g");
962 | var CJK_OPERATOR_ANS = new RegExp(
963 | "([".concat(CJK, "])([\\+\\-\\*\\/=&\\|<>])([A-Za-z0-9])"),
964 | "g",
965 | );
966 | var ANS_OPERATOR_CJK = new RegExp(
967 | "([A-Za-z0-9])([\\+\\-\\*\\/=&\\|<>])([".concat(CJK, "])"),
968 | "g",
969 | );
970 | var FIX_SLASH_AS = /([/]) ([a-z\-_\./]+)/g;
971 | var FIX_SLASH_AS_SLASH = /([/\.])([A-Za-z\-_\./]+) ([/])/g;
972 | var CJK_LEFT_BRACKET = new RegExp(
973 | "([".concat(CJK, "])([\\(\\[\\{<>\u201C])"),
974 | "g",
975 | );
976 | var RIGHT_BRACKET_CJK = new RegExp(
977 | "([\\)\\]\\}<>\u201D])([".concat(CJK, "])"),
978 | "g",
979 | );
980 | var FIX_LEFT_BRACKET_ANY_RIGHT_BRACKET =
981 | /([\(\[\{<\u201c]+)[ ]*(.+?)[ ]*([\)\]\}>\u201d]+)/;
982 | var ANS_CJK_LEFT_BRACKET_ANY_RIGHT_BRACKET = new RegExp(
983 | "([A-Za-z0-9"
984 | .concat(CJK, "])[ ]*([\u201C])([A-Za-z0-9")
985 | .concat(CJK, "\\-_ ]+)([\u201D])"),
986 | "g",
987 | );
988 | var LEFT_BRACKET_ANY_RIGHT_BRACKET_ANS_CJK = new RegExp(
989 | "([\u201C])([A-Za-z0-9"
990 | .concat(CJK, "\\-_ ]+)([\u201D])[ ]*([A-Za-z0-9")
991 | .concat(CJK, "])"),
992 | "g",
993 | );
994 | var AN_LEFT_BRACKET = /([A-Za-z0-9])([\(\[\{])/g;
995 | var RIGHT_BRACKET_AN = /([\)\]\}])([A-Za-z0-9])/g;
996 | var CJK_ANS = new RegExp(
997 | "([".concat(
998 | CJK,
999 | "])([A-Za-z\u0370-\u03FF0-9@\\$%\\^&\\*\\-\\+\\\\=\\|/\xA1-\xFF\u2150-\u218F\u2700\u2014\u27BF])",
1000 | ),
1001 | "g",
1002 | );
1003 | var ANS_CJK = new RegExp(
1004 | "([A-Za-z\u0370-\u03FF0-9~\\$%\\^&\\*\\-\\+\\\\=\\|/!;:,\\.\\?\xA1-\xFF\u2150-\u218F\u2700\u2014\u27BF])([".concat(
1005 | CJK,
1006 | "])",
1007 | ),
1008 | "g",
1009 | );
1010 | var S_A = /(%)([A-Za-z])/g;
1011 | var MIDDLE_DOT = /([ ]*)([\u00b7\u2022\u2027])([ ]*)/g;
1012 |
1013 | var Pangu = (function () {
1014 | function Pangu() {
1015 | _classCallCheck(this, Pangu);
1016 |
1017 | this.version = "4.0.7";
1018 | }
1019 |
1020 | _createClass(Pangu, [
1021 | {
1022 | key: "convertToFullwidth",
1023 | value: function convertToFullwidth(symbols) {
1024 | return symbols
1025 | .replace(/~/g, "~")
1026 | .replace(/!/g, "!")
1027 | .replace(/;/g, ";")
1028 | .replace(/:/g, ":")
1029 | .replace(/,/g, ",")
1030 | .replace(/\./g, "。")
1031 | .replace(/\?/g, "?");
1032 | },
1033 | },
1034 | {
1035 | key: "spacing",
1036 | value: function spacing(text) {
1037 | if (typeof text !== "string") {
1038 | console.warn(
1039 | "spacing(text) only accepts string but got ".concat(
1040 | _typeof(text),
1041 | ),
1042 | );
1043 | return text;
1044 | }
1045 |
1046 | if (text.length <= 1 || !ANY_CJK.test(text)) {
1047 | return text;
1048 | }
1049 |
1050 | var self = this;
1051 | var newText = text;
1052 | newText = newText.replace(
1053 | CONVERT_TO_FULLWIDTH_CJK_SYMBOLS_CJK,
1054 | function (match, leftCjk, symbols, rightCjk) {
1055 | var fullwidthSymbols = self.convertToFullwidth(symbols);
1056 | return ""
1057 | .concat(leftCjk)
1058 | .concat(fullwidthSymbols)
1059 | .concat(rightCjk);
1060 | },
1061 | );
1062 | newText = newText.replace(
1063 | CONVERT_TO_FULLWIDTH_CJK_SYMBOLS,
1064 | function (match, cjk, symbols) {
1065 | var fullwidthSymbols = self.convertToFullwidth(symbols);
1066 | return "".concat(cjk).concat(fullwidthSymbols);
1067 | },
1068 | );
1069 | newText = newText.replace(DOTS_CJK, "$1 $2");
1070 | newText = newText.replace(FIX_CJK_COLON_ANS, "$1:$2");
1071 | newText = newText.replace(CJK_QUOTE, "$1 $2");
1072 | newText = newText.replace(QUOTE_CJK, "$1 $2");
1073 | newText = newText.replace(FIX_QUOTE_ANY_QUOTE, "$1$2$3");
1074 | newText = newText.replace(
1075 | CJK_SINGLE_QUOTE_BUT_POSSESSIVE,
1076 | "$1 $2",
1077 | );
1078 | newText = newText.replace(SINGLE_QUOTE_CJK, "$1 $2");
1079 | newText = newText.replace(
1080 | FIX_POSSESSIVE_SINGLE_QUOTE,
1081 | "$1's",
1082 | );
1083 | newText = newText.replace(HASH_ANS_CJK_HASH, "$1 $2$3$4 $5");
1084 | newText = newText.replace(CJK_HASH, "$1 $2");
1085 | newText = newText.replace(HASH_CJK, "$1 $3");
1086 | newText = newText.replace(CJK_OPERATOR_ANS, "$1 $2 $3");
1087 | newText = newText.replace(ANS_OPERATOR_CJK, "$1 $2 $3");
1088 | newText = newText.replace(FIX_SLASH_AS, "$1$2");
1089 | newText = newText.replace(FIX_SLASH_AS_SLASH, "$1$2$3");
1090 | newText = newText.replace(CJK_LEFT_BRACKET, "$1 $2");
1091 | newText = newText.replace(RIGHT_BRACKET_CJK, "$1 $2");
1092 | newText = newText.replace(
1093 | FIX_LEFT_BRACKET_ANY_RIGHT_BRACKET,
1094 | "$1$2$3",
1095 | );
1096 | newText = newText.replace(
1097 | ANS_CJK_LEFT_BRACKET_ANY_RIGHT_BRACKET,
1098 | "$1 $2$3$4",
1099 | );
1100 | newText = newText.replace(
1101 | LEFT_BRACKET_ANY_RIGHT_BRACKET_ANS_CJK,
1102 | "$1$2$3 $4",
1103 | );
1104 | newText = newText.replace(AN_LEFT_BRACKET, "$1 $2");
1105 | newText = newText.replace(RIGHT_BRACKET_AN, "$1 $2");
1106 | newText = newText.replace(CJK_ANS, "$1 $2");
1107 | newText = newText.replace(ANS_CJK, "$1 $2");
1108 | newText = newText.replace(S_A, "$1 $2");
1109 | newText = newText.replace(MIDDLE_DOT, "・");
1110 | return newText;
1111 | },
1112 | },
1113 | {
1114 | key: "spacingText",
1115 | value: function spacingText(text) {
1116 | var callback =
1117 | arguments.length > 1 && arguments[1] !== undefined
1118 | ? arguments[1]
1119 | : function () {};
1120 | var newText;
1121 |
1122 | try {
1123 | newText = this.spacing(text);
1124 | } catch (err) {
1125 | callback(err);
1126 | return;
1127 | }
1128 |
1129 | callback(null, newText);
1130 | },
1131 | },
1132 | {
1133 | key: "spacingTextSync",
1134 | value: function spacingTextSync(text) {
1135 | return this.spacing(text);
1136 | },
1137 | },
1138 | ]);
1139 |
1140 | return Pangu;
1141 | })();
1142 |
1143 | var pangu = new Pangu();
1144 | module.exports = pangu;
1145 | module.exports.default = pangu;
1146 | module.exports.Pangu = Pangu;
1147 | });
1148 |
1149 | /***/
1150 | },
1151 | /******/
1152 | ],
1153 | );
1154 | });
1155 |
--------------------------------------------------------------------------------