mirror of https://github.com/laurent22/joplin.git
Desktop: Added support for checkboxes and fixed various issues with WYSIWYG editor
parent
c607444c23
commit
41acdce165
|
@ -21,38 +21,40 @@ Clipper/content_scripts/Readability.js
|
||||||
Clipper/dist
|
Clipper/dist
|
||||||
Clipper/icons
|
Clipper/icons
|
||||||
Clipper/popup/build
|
Clipper/popup/build
|
||||||
|
Clipper/popup/config/webpack.config.js
|
||||||
Clipper/popup/node_modules
|
Clipper/popup/node_modules
|
||||||
|
Clipper/popup/scripts/build.js
|
||||||
docs/
|
docs/
|
||||||
ElectronClient/dist
|
ElectronClient/dist
|
||||||
|
ElectronClient/gui/editors/TinyMCE/plugins/lists.js
|
||||||
ElectronClient/lib
|
ElectronClient/lib
|
||||||
ElectronClient/lib/vendor/sjcl-rn.js
|
ElectronClient/lib/vendor/sjcl-rn.js
|
||||||
ElectronClient/lib/vendor/sjcl.js
|
ElectronClient/lib/vendor/sjcl.js
|
||||||
ElectronClient/locales
|
ElectronClient/locales
|
||||||
ElectronClient/node_modules
|
ElectronClient/node_modules
|
||||||
|
ElectronClient/packageInfo.js
|
||||||
highlight.pack.js
|
highlight.pack.js
|
||||||
|
Modules/TinyMCE/JoplinLists/
|
||||||
node_modules/
|
node_modules/
|
||||||
ReactNativeClient/android
|
ReactNativeClient/android
|
||||||
ReactNativeClient/ios
|
ReactNativeClient/ios
|
||||||
|
ReactNativeClient/lib/joplin-renderer/assets/
|
||||||
|
ReactNativeClient/lib/joplin-renderer/vendor/fountain.min.js
|
||||||
|
ReactNativeClient/lib/rnInjectedJs/
|
||||||
ReactNativeClient/lib/vendor/
|
ReactNativeClient/lib/vendor/
|
||||||
ReactNativeClient/lib/welcomeAssets.js
|
ReactNativeClient/lib/welcomeAssets.js
|
||||||
ReactNativeClient/locales
|
ReactNativeClient/locales
|
||||||
ReactNativeClient/node_modules
|
ReactNativeClient/node_modules
|
||||||
|
ReactNativeClient/pluginAssets/
|
||||||
readme/
|
readme/
|
||||||
Tools/node_modules
|
|
||||||
Tools/PortableAppsLauncher
|
|
||||||
Server/.git/
|
Server/.git/
|
||||||
Server/.github/
|
Server/.github/
|
||||||
Server/docs/
|
|
||||||
Server/dist/
|
|
||||||
Server/bin/
|
Server/bin/
|
||||||
|
Server/dist/
|
||||||
|
Server/docs/
|
||||||
Server/node_modules/
|
Server/node_modules/
|
||||||
ElectronClient/packageInfo.js
|
Tools/node_modules
|
||||||
ReactNativeClient/pluginAssets/
|
Tools/PortableAppsLauncher
|
||||||
ReactNativeClient/lib/joplin-renderer/vendor/fountain.min.js
|
|
||||||
ReactNativeClient/lib/joplin-renderer/assets/
|
|
||||||
ReactNativeClient/lib/rnInjectedJs/
|
|
||||||
Clipper/popup/config/webpack.config.js
|
|
||||||
Clipper/popup/scripts/build.js
|
|
||||||
|
|
||||||
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
# AUTO-GENERATED - EXCLUDED TYPESCRIPT BUILD
|
||||||
ElectronClient/gui/editors/PlainEditor.js
|
ElectronClient/gui/editors/PlainEditor.js
|
||||||
|
@ -64,6 +66,7 @@ ElectronClient/gui/ResourceScreen.js
|
||||||
ElectronClient/gui/ShareNoteDialog.js
|
ElectronClient/gui/ShareNoteDialog.js
|
||||||
ElectronClient/gui/utils/NoteText.js
|
ElectronClient/gui/utils/NoteText.js
|
||||||
ReactNativeClient/lib/AsyncActionQueue.js
|
ReactNativeClient/lib/AsyncActionQueue.js
|
||||||
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||||
|
|
|
@ -59,6 +59,7 @@ ElectronClient/gui/ResourceScreen.js
|
||||||
ElectronClient/gui/ShareNoteDialog.js
|
ElectronClient/gui/ShareNoteDialog.js
|
||||||
ElectronClient/gui/utils/NoteText.js
|
ElectronClient/gui/utils/NoteText.js
|
||||||
ReactNativeClient/lib/AsyncActionQueue.js
|
ReactNativeClient/lib/AsyncActionQueue.js
|
||||||
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
|
||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
|
||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
|
||||||
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
|
||||||
|
|
|
@ -33,9 +33,9 @@
|
||||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||||
},
|
},
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "5.7.3",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
|
||||||
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw=="
|
"integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg=="
|
||||||
},
|
},
|
||||||
"acorn-globals": {
|
"acorn-globals": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
|
@ -47,9 +47,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "6.4.0",
|
"version": "6.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
|
||||||
"integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw=="
|
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -344,11 +344,6 @@
|
||||||
"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
|
"integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"async-limiter": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
|
||||||
},
|
|
||||||
"async-mutex": {
|
"async-mutex": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.1.3.tgz",
|
||||||
|
@ -574,9 +569,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browser-process-hrtime": {
|
"browser-process-hrtime": {
|
||||||
"version": "0.1.3",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz",
|
||||||
"integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw=="
|
"integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow=="
|
||||||
},
|
},
|
||||||
"buffer-equal": {
|
"buffer-equal": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
@ -1160,17 +1155,24 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"cssom": {
|
||||||
|
"version": "0.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
|
||||||
|
"integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw=="
|
||||||
|
},
|
||||||
|
"cssstyle": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA==",
|
||||||
|
"requires": {
|
||||||
|
"cssom": "~0.3.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
"cssom": {
|
"cssom": {
|
||||||
"version": "0.3.8",
|
"version": "0.3.8",
|
||||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
|
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
|
||||||
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
|
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
|
||||||
},
|
}
|
||||||
"cssstyle": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
|
|
||||||
"requires": {
|
|
||||||
"cssom": "0.3.x"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cwise-compiler": {
|
"cwise-compiler": {
|
||||||
|
@ -1207,18 +1209,6 @@
|
||||||
"abab": "^2.0.0",
|
"abab": "^2.0.0",
|
||||||
"whatwg-mimetype": "^2.2.0",
|
"whatwg-mimetype": "^2.2.0",
|
||||||
"whatwg-url": "^7.0.0"
|
"whatwg-url": "^7.0.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"whatwg-url": {
|
|
||||||
"version": "7.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
|
|
||||||
"integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
|
|
||||||
"requires": {
|
|
||||||
"lodash.sortby": "^4.7.0",
|
|
||||||
"tr46": "^1.0.1",
|
|
||||||
"webidl-conversions": "^4.0.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
|
@ -3448,6 +3438,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz",
|
||||||
"integrity": "sha1-ge9X/l0FgUzVjCSDYyqZwwoOgIc="
|
"integrity": "sha1-ge9X/l0FgUzVjCSDYyqZwwoOgIc="
|
||||||
},
|
},
|
||||||
|
"ip-regex": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
|
||||||
|
"integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
|
||||||
|
},
|
||||||
"is-absolute": {
|
"is-absolute": {
|
||||||
"version": "0.2.6",
|
"version": "0.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz",
|
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-0.2.6.tgz",
|
||||||
|
@ -3735,13 +3730,13 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"joplin-turndown": {
|
"joplin-turndown": {
|
||||||
"version": "4.0.23",
|
"version": "4.0.24",
|
||||||
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.23.tgz",
|
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.24.tgz",
|
||||||
"integrity": "sha512-Dh93R7G/S/KRbOu4/+FIxoUcUDcoUL4QDsqGhperOi/cUxUeg8fngrmEzdP8kEpQzqm5+8jkq9Cc1w6695owpQ==",
|
"integrity": "sha512-mKd2rAFzJKnhTVjEpHomG+T01//uz5rXVSAOYRh3/JKXpY7QUhVp8jCmFfO+kaadrLABTz04mvTmyyoOadxdTA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"css": "^2.2.4",
|
"css": "^2.2.4",
|
||||||
"html-entities": "^1.2.1",
|
"html-entities": "^1.2.1",
|
||||||
"jsdom": "^11.9.0"
|
"jsdom": "^15.2.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"joplin-turndown-plugin-gfm": {
|
"joplin-turndown-plugin-gfm": {
|
||||||
|
@ -3766,35 +3761,35 @@
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"jsdom": {
|
"jsdom": {
|
||||||
"version": "11.12.0",
|
"version": "15.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz",
|
||||||
"integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
|
"integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"abab": "^2.0.0",
|
"abab": "^2.0.0",
|
||||||
"acorn": "^5.5.3",
|
"acorn": "^7.1.0",
|
||||||
"acorn-globals": "^4.1.0",
|
"acorn-globals": "^4.3.2",
|
||||||
"array-equal": "^1.0.0",
|
"array-equal": "^1.0.0",
|
||||||
"cssom": ">= 0.3.2 < 0.4.0",
|
"cssom": "^0.4.1",
|
||||||
"cssstyle": "^1.0.0",
|
"cssstyle": "^2.0.0",
|
||||||
"data-urls": "^1.0.0",
|
"data-urls": "^1.1.0",
|
||||||
"domexception": "^1.0.1",
|
"domexception": "^1.0.1",
|
||||||
"escodegen": "^1.9.1",
|
"escodegen": "^1.11.1",
|
||||||
"html-encoding-sniffer": "^1.0.2",
|
"html-encoding-sniffer": "^1.0.2",
|
||||||
"left-pad": "^1.3.0",
|
"nwsapi": "^2.2.0",
|
||||||
"nwsapi": "^2.0.7",
|
"parse5": "5.1.0",
|
||||||
"parse5": "4.0.0",
|
|
||||||
"pn": "^1.1.0",
|
"pn": "^1.1.0",
|
||||||
"request": "^2.87.0",
|
"request": "^2.88.0",
|
||||||
"request-promise-native": "^1.0.5",
|
"request-promise-native": "^1.0.7",
|
||||||
"sax": "^1.2.4",
|
"saxes": "^3.1.9",
|
||||||
"symbol-tree": "^3.2.2",
|
"symbol-tree": "^3.2.2",
|
||||||
"tough-cookie": "^2.3.4",
|
"tough-cookie": "^3.0.1",
|
||||||
"w3c-hr-time": "^1.0.1",
|
"w3c-hr-time": "^1.0.1",
|
||||||
|
"w3c-xmlserializer": "^1.1.2",
|
||||||
"webidl-conversions": "^4.0.2",
|
"webidl-conversions": "^4.0.2",
|
||||||
"whatwg-encoding": "^1.0.3",
|
"whatwg-encoding": "^1.0.5",
|
||||||
"whatwg-mimetype": "^2.1.0",
|
"whatwg-mimetype": "^2.3.0",
|
||||||
"whatwg-url": "^6.4.1",
|
"whatwg-url": "^7.0.0",
|
||||||
"ws": "^5.2.0",
|
"ws": "^7.0.0",
|
||||||
"xml-name-validator": "^3.0.0"
|
"xml-name-validator": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3935,11 +3930,6 @@
|
||||||
"flush-write-stream": "^1.0.2"
|
"flush-write-stream": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"left-pad": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA=="
|
|
||||||
},
|
|
||||||
"levenshtein": {
|
"levenshtein": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/levenshtein/-/levenshtein-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/levenshtein/-/levenshtein-1.0.5.tgz",
|
||||||
|
@ -4999,9 +4989,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"parse5": {
|
"parse5": {
|
||||||
"version": "4.0.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
|
||||||
"integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA=="
|
"integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ=="
|
||||||
},
|
},
|
||||||
"pascalcase": {
|
"pascalcase": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
|
@ -5693,6 +5683,22 @@
|
||||||
"request-promise-core": "1.1.3",
|
"request-promise-core": "1.1.3",
|
||||||
"stealthy-require": "^1.1.1",
|
"stealthy-require": "^1.1.1",
|
||||||
"tough-cookie": "^2.3.3"
|
"tough-cookie": "^2.3.3"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||||
|
},
|
||||||
|
"tough-cookie": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||||
|
"requires": {
|
||||||
|
"psl": "^1.1.28",
|
||||||
|
"punycode": "^2.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require-directory": {
|
"require-directory": {
|
||||||
|
@ -5786,6 +5792,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||||
},
|
},
|
||||||
|
"saxes": {
|
||||||
|
"version": "3.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz",
|
||||||
|
"integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==",
|
||||||
|
"requires": {
|
||||||
|
"xmlchars": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"semver": {
|
"semver": {
|
||||||
"version": "5.4.1",
|
"version": "5.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
|
||||||
|
@ -6699,10 +6713,11 @@
|
||||||
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw=="
|
||||||
},
|
},
|
||||||
"tough-cookie": {
|
"tough-cookie": {
|
||||||
"version": "2.5.0",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
|
||||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
"integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"ip-regex": "^2.1.0",
|
||||||
"psl": "^1.1.28",
|
"psl": "^1.1.28",
|
||||||
"punycode": "^2.1.1"
|
"punycode": "^2.1.1"
|
||||||
},
|
},
|
||||||
|
@ -7118,11 +7133,21 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"w3c-hr-time": {
|
"w3c-hr-time": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz",
|
||||||
"integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=",
|
"integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"browser-process-hrtime": "^0.1.2"
|
"browser-process-hrtime": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"w3c-xmlserializer": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==",
|
||||||
|
"requires": {
|
||||||
|
"domexception": "^1.0.1",
|
||||||
|
"webidl-conversions": "^4.0.2",
|
||||||
|
"xml-name-validator": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"webidl-conversions": {
|
"webidl-conversions": {
|
||||||
|
@ -7154,9 +7179,9 @@
|
||||||
"integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="
|
"integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="
|
||||||
},
|
},
|
||||||
"whatwg-url": {
|
"whatwg-url": {
|
||||||
"version": "6.5.0",
|
"version": "7.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
|
||||||
"integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
|
"integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lodash.sortby": "^4.7.0",
|
"lodash.sortby": "^4.7.0",
|
||||||
"tr46": "^1.0.1",
|
"tr46": "^1.0.1",
|
||||||
|
@ -7237,12 +7262,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ws": {
|
"ws": {
|
||||||
"version": "5.2.2",
|
"version": "7.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz",
|
||||||
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
|
"integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ=="
|
||||||
"requires": {
|
|
||||||
"async-limiter": "~1.0.0"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"xdg-basedir": {
|
"xdg-basedir": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
|
@ -7268,6 +7290,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.4.tgz",
|
||||||
"integrity": "sha1-UZy0ymhtAFqEINNJbz8MruzKWA8="
|
"integrity": "sha1-UZy0ymhtAFqEINNJbz8MruzKWA8="
|
||||||
},
|
},
|
||||||
|
"xmlchars": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
||||||
|
},
|
||||||
"xtend": {
|
"xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
"htmlparser2": "^4.1.0",
|
"htmlparser2": "^4.1.0",
|
||||||
"image-data-uri": "^2.0.0",
|
"image-data-uri": "^2.0.0",
|
||||||
"image-type": "^3.0.0",
|
"image-type": "^3.0.0",
|
||||||
"joplin-turndown": "^4.0.23",
|
"joplin-turndown": "^4.0.24",
|
||||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||||
"json-stringify-safe": "^5.0.1",
|
"json-stringify-safe": "^5.0.1",
|
||||||
"jssha": "^2.3.0",
|
"jssha": "^2.3.0",
|
||||||
|
|
|
@ -12,6 +12,7 @@ const BaseModel = require('lib/BaseModel.js');
|
||||||
const { shim } = require('lib/shim');
|
const { shim } = require('lib/shim');
|
||||||
const MdToHtml = require('lib/joplin-renderer/MdToHtml');
|
const MdToHtml = require('lib/joplin-renderer/MdToHtml');
|
||||||
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
|
const { enexXmlToMd } = require('lib/import-enex-md-gen.js');
|
||||||
|
const { themeStyle } = require('../../ElectronClient/theme.js');
|
||||||
|
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60 * 60 * 1000; // Can run for a while since everything is in the same test unit
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60 * 60 * 1000; // Can run for a while since everything is in the same test unit
|
||||||
|
|
||||||
|
@ -19,6 +20,18 @@ process.on('unhandledRejection', (reason, p) => {
|
||||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function newTestMdToHtml(options = null) {
|
||||||
|
options = {
|
||||||
|
ResourceModel: {
|
||||||
|
isResourceUrl: () => false,
|
||||||
|
},
|
||||||
|
fsDriver: shim.fsDriver(),
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
return new MdToHtml(options);
|
||||||
|
}
|
||||||
|
|
||||||
describe('MdToHtml', function() {
|
describe('MdToHtml', function() {
|
||||||
|
|
||||||
beforeEach(async (done) => {
|
beforeEach(async (done) => {
|
||||||
|
@ -30,11 +43,7 @@ describe('MdToHtml', function() {
|
||||||
it('should convert from Markdown to Html', asyncTest(async () => {
|
it('should convert from Markdown to Html', asyncTest(async () => {
|
||||||
const basePath = `${__dirname}/md_to_html`;
|
const basePath = `${__dirname}/md_to_html`;
|
||||||
const files = await shim.fsDriver().readDirStats(basePath);
|
const files = await shim.fsDriver().readDirStats(basePath);
|
||||||
const mdToHtml = new MdToHtml({
|
const mdToHtml = newTestMdToHtml();
|
||||||
ResourceModel: {
|
|
||||||
isResourceUrl: () => false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let i = 0; i < files.length; i++) {
|
for (let i = 0; i < files.length; i++) {
|
||||||
const mdFilename = files[i].path;
|
const mdFilename = files[i].path;
|
||||||
|
@ -49,6 +58,14 @@ describe('MdToHtml', function() {
|
||||||
bodyOnly: true,
|
bodyOnly: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (mdFilename === 'checkbox_alternative.md') {
|
||||||
|
mdToHtmlOptions.plugins = {
|
||||||
|
checkbox: {
|
||||||
|
renderingType: 2,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const markdown = await shim.fsDriver().readFile(mdFilePath);
|
const markdown = await shim.fsDriver().readFile(mdFilePath);
|
||||||
let expectedHtml = await shim.fsDriver().readFile(htmlPath);
|
let expectedHtml = await shim.fsDriver().readFile(htmlPath);
|
||||||
|
|
||||||
|
@ -80,12 +97,61 @@ describe('MdToHtml', function() {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// it('should write CSS to an external file', asyncTest(async () => {
|
it('should return enabled plugin assets', asyncTest(async () => {
|
||||||
// const mdToHtml = new MdToHtml({
|
const pluginOptions = {};
|
||||||
// fsDriver: shim.fsDriver(),
|
const pluginNames = MdToHtml.pluginNames();
|
||||||
// tempDir: Setting.value('tempDir'),
|
|
||||||
// });
|
for (const n of pluginNames) pluginOptions[n] = { enabled: false };
|
||||||
|
|
||||||
|
{
|
||||||
|
const mdToHtml = newTestMdToHtml({ pluginOptions: pluginOptions });
|
||||||
|
const assets = await mdToHtml.allAssets(themeStyle(1));
|
||||||
|
expect(assets.length).toBe(1); // Base note style should always be returned
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
pluginOptions['checkbox'].enabled = true;
|
||||||
|
const mdToHtml = newTestMdToHtml({ pluginOptions: pluginOptions });
|
||||||
|
|
||||||
|
const assets = await mdToHtml.allAssets(themeStyle(1));
|
||||||
|
expect(assets.length).toBe(2);
|
||||||
|
expect(assets[1].mime).toBe('text/css');
|
||||||
|
|
||||||
|
const content = await shim.fsDriver().readFile(assets[1].path);
|
||||||
|
expect(content.indexOf('joplin-checklist') >= 0).toBe(true);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should wrapped the rendered Markdown', asyncTest(async () => {
|
||||||
|
const mdToHtml = newTestMdToHtml();
|
||||||
|
|
||||||
|
// In this case, the HTML contains both the style and
|
||||||
|
// the rendered markdown wrapped in a DIV.
|
||||||
|
const result = await mdToHtml.render('just **testing**');
|
||||||
|
expect(result.cssStrings.length).toBe(0);
|
||||||
|
expect(result.html.indexOf('rendered-md') >= 0).toBe(true);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should return the rendered body only', asyncTest(async () => {
|
||||||
|
const mdToHtml = newTestMdToHtml();
|
||||||
|
|
||||||
|
// In this case, the HTML contains only the rendered markdown,
|
||||||
|
// with no wrapper and no style.
|
||||||
|
// The style is instead in the cssStrings property.
|
||||||
|
const result = await mdToHtml.render('just **testing**', null, { bodyOnly: true });
|
||||||
|
expect(result.cssStrings.length).toBe(1);
|
||||||
|
expect(result.html.trim()).toBe('<p>just <strong>testing</strong></p>');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should split HTML and CSS', asyncTest(async () => {
|
||||||
|
const mdToHtml = newTestMdToHtml();
|
||||||
|
|
||||||
|
// It is similar to the bodyOnly option, excepts that
|
||||||
|
// the rendered Markdown is wrapped in a DIV
|
||||||
|
const result = await mdToHtml.render('just **testing**', null, { splitted: true });
|
||||||
|
expect(result.cssStrings.length).toBe(1);
|
||||||
|
expect(result.html.trim()).toBe('<div id="rendered-md"><p>just <strong>testing</strong></p>\n</div>');
|
||||||
|
}));
|
||||||
|
|
||||||
// }));
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
<ul>
|
|
||||||
<li class="md-checkbox joplin-checkbox"><div class="checkbox-wrapper"><input type="checkbox" id="md-checkbox-7" onclick="
|
|
||||||
try {
|
|
||||||
if (this.checked) {
|
|
||||||
this.setAttribute('checked', 'checked');
|
|
||||||
} else {
|
|
||||||
this.removeAttribute('checked');
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcProxySendToHost('checkboxclick:checked:0');
|
|
||||||
const label = document.getElementById("cb-label-md-checkbox-7");
|
|
||||||
label.classList.remove(this.checked ? 'checkbox-label-unchecked' : 'checkbox-label-checked');
|
|
||||||
label.classList.add(this.checked ? 'checkbox-label-checked' : 'checkbox-label-unchecked');
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Checkbox checked:0 error', error);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
" checked="checked"><label id="cb-label-md-checkbox-7" for="md-checkbox-7" class="checkbox-label-checked">one</label></div></li>
|
|
||||||
<li class="md-checkbox joplin-checkbox"><div class="checkbox-wrapper"><input type="checkbox" id="md-checkbox-8" onclick="
|
|
||||||
try {
|
|
||||||
if (this.checked) {
|
|
||||||
this.setAttribute('checked', 'checked');
|
|
||||||
} else {
|
|
||||||
this.removeAttribute('checked');
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcProxySendToHost('checkboxclick:unchecked:1');
|
|
||||||
const label = document.getElementById("cb-label-md-checkbox-8");
|
|
||||||
label.classList.remove(this.checked ? 'checkbox-label-unchecked' : 'checkbox-label-checked');
|
|
||||||
label.classList.add(this.checked ? 'checkbox-label-checked' : 'checkbox-label-unchecked');
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Checkbox unchecked:1 error', error);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
"><label id="cb-label-md-checkbox-8" for="md-checkbox-8" class="checkbox-label-unchecked">two</label></div></li>
|
|
||||||
<li class="md-checkbox joplin-checkbox"><div class="checkbox-wrapper"><input type="checkbox" id="md-checkbox-9" onclick="
|
|
||||||
try {
|
|
||||||
if (this.checked) {
|
|
||||||
this.setAttribute('checked', 'checked');
|
|
||||||
} else {
|
|
||||||
this.removeAttribute('checked');
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcProxySendToHost('checkboxclick:unchecked:2');
|
|
||||||
const label = document.getElementById("cb-label-md-checkbox-9");
|
|
||||||
label.classList.remove(this.checked ? 'checkbox-label-unchecked' : 'checkbox-label-checked');
|
|
||||||
label.classList.add(this.checked ? 'checkbox-label-checked' : 'checkbox-label-unchecked');
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Checkbox unchecked:2 error', error);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
"><label id="cb-label-md-checkbox-9" for="md-checkbox-9" class="checkbox-label-unchecked">with <strong>bold</strong> text</label></div></li>
|
|
||||||
</ul>
|
|
|
@ -1,3 +0,0 @@
|
||||||
- [x] one
|
|
||||||
- [ ] two
|
|
||||||
- [ ] with **bold** text
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
<ul class="joplin-checklist">
|
||||||
|
<li>Not checked</li>
|
||||||
|
<li class="checked">Checked!!
|
||||||
|
<ul class="joplin-checklist">
|
||||||
|
<li class="checked">Indented, with <strong>bold</strong></li>
|
||||||
|
<li>Indented, not checked</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
|
@ -0,0 +1,4 @@
|
||||||
|
- [ ] Not checked
|
||||||
|
- [x] Checked!!
|
||||||
|
- [x] Indented, with **bold**
|
||||||
|
- [ ] Indented, not checked
|
|
@ -1,4 +1,4 @@
|
||||||
_Block formulas_ are surrounded by double dollar signs. For example, `$$x = \frac{-b \pm \sqrt{b^2 - 4ac} }{2a}$$` renders, _on a separate line_, as
|
*Block formulas* are surrounded by double dollar signs. For example, `$$x = \frac{-b \pm \sqrt{b^2 - 4ac} }{2a}$$` renders, *on a separate line*, as
|
||||||
|
|
||||||
$$
|
$$
|
||||||
x = \frac{-b \pm \sqrt{b^2 - 4ac} }{2a}.
|
x = \frac{-b \pm \sqrt{b^2 - 4ac} }{2a}.
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
_Inline formulas_ are surrounded by single dollar signs. For example, `$f(x) = ax^2 + bx + c$` renders as $f(x)=ax^2+bx+c$.
|
*Inline formulas* are surrounded by single dollar signs. For example, `$f(x) = ax^2 + bx + c$` renders as $f(x)=ax^2+bx+c$.
|
|
@ -0,0 +1,9 @@
|
||||||
|
<ul class="joplin-checklist">
|
||||||
|
<li>Not checked</li>
|
||||||
|
<li class="checked">Checked!!
|
||||||
|
<ul class="joplin-checklist">
|
||||||
|
<li class="checked">Indented, with <strong>bold</strong></li>
|
||||||
|
<li>Indented, not checked</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
|
@ -0,0 +1,4 @@
|
||||||
|
- [ ] Not checked
|
||||||
|
- [x] Checked!!
|
||||||
|
- [x] Indented, with **bold**
|
||||||
|
- [ ] Indented, not checked
|
|
@ -348,6 +348,32 @@ function NoteText2(props:NoteTextProps) {
|
||||||
return result;
|
return result;
|
||||||
}, [props.theme]);
|
}, [props.theme]);
|
||||||
|
|
||||||
|
const allAssets = useCallback(async (markupLanguage:number):Promise<any[]> => {
|
||||||
|
const theme = themeStyle(props.theme);
|
||||||
|
|
||||||
|
const markupToHtml = markupLanguageUtils.newMarkupToHtml({
|
||||||
|
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return markupToHtml.allAssets(markupLanguage, theme);
|
||||||
|
}, [props.theme]);
|
||||||
|
|
||||||
|
const joplinHtml = useCallback(async (type:string) => {
|
||||||
|
if (type === 'checkbox') {
|
||||||
|
const result = await markupToHtml(MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN, '- [ ] xxxxxREMOVExxxxx', {
|
||||||
|
bodyOnly: true,
|
||||||
|
externalAssetsOnly: true,
|
||||||
|
});
|
||||||
|
const html = result.html
|
||||||
|
.replace(/xxxxxREMOVExxxxx/m, ' ')
|
||||||
|
.replace(/<ul.*?>/, '')
|
||||||
|
.replace(/<\/ul>/, '');
|
||||||
|
return { ...result, html: html };
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Invalid type:${type}`);
|
||||||
|
}, [markupToHtml]);
|
||||||
|
|
||||||
const handleProvisionalFlag = useCallback(() => {
|
const handleProvisionalFlag = useCallback(() => {
|
||||||
if (props.isProvisional) {
|
if (props.isProvisional) {
|
||||||
props.dispatch({
|
props.dispatch({
|
||||||
|
@ -501,8 +527,10 @@ function NoteText2(props:NoteTextProps) {
|
||||||
onWillChange: onBodyWillChange,
|
onWillChange: onBodyWillChange,
|
||||||
defaultEditorState: defaultEditorState,
|
defaultEditorState: defaultEditorState,
|
||||||
markupToHtml: markupToHtml,
|
markupToHtml: markupToHtml,
|
||||||
|
allAssets: allAssets,
|
||||||
attachResources: attachResources,
|
attachResources: attachResources,
|
||||||
disabled: waitingToSaveNote,
|
disabled: waitingToSaveNote,
|
||||||
|
joplinHtml: joplinHtml,
|
||||||
};
|
};
|
||||||
|
|
||||||
let editor = null;
|
let editor = null;
|
||||||
|
|
|
@ -14,7 +14,9 @@ interface TinyMCEProps {
|
||||||
onWillChange(event:any): void,
|
onWillChange(event:any): void,
|
||||||
defaultEditorState: DefaultEditorState,
|
defaultEditorState: DefaultEditorState,
|
||||||
markupToHtml: Function,
|
markupToHtml: Function,
|
||||||
|
allAssets: Function,
|
||||||
attachResources: Function,
|
attachResources: Function,
|
||||||
|
joplinHtml: Function,
|
||||||
disabled: boolean,
|
disabled: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +108,9 @@ const TinyMCE = (props:TinyMCEProps, ref:any) => {
|
||||||
const markupToHtml = useRef(null);
|
const markupToHtml = useRef(null);
|
||||||
markupToHtml.current = props.markupToHtml;
|
markupToHtml.current = props.markupToHtml;
|
||||||
|
|
||||||
|
const joplinHtml = useRef(null);
|
||||||
|
joplinHtml.current = props.joplinHtml;
|
||||||
|
|
||||||
const rootIdRef = useRef<string>(`tinymce-${Date.now()}${Math.round(Math.random() * 10000)}`);
|
const rootIdRef = useRef<string>(`tinymce-${Date.now()}${Math.round(Math.random() * 10000)}`);
|
||||||
|
|
||||||
const dispatchDidUpdate = (editor:any) => {
|
const dispatchDidUpdate = (editor:any) => {
|
||||||
|
@ -166,21 +171,69 @@ const TinyMCE = (props:TinyMCEProps, ref:any) => {
|
||||||
// module would not load these extra files.
|
// module would not load these extra files.
|
||||||
// -----------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
useEffect(() => {
|
const loadScript = async (script:any) => {
|
||||||
if (document.getElementById('tinyMceScript')) {
|
return new Promise((resolve) => {
|
||||||
setScriptLoaded(true);
|
let element:any = document.createElement('script');
|
||||||
return () => {};
|
if (script.src.indexOf('.css') >= 0) {
|
||||||
|
element = document.createElement('link');
|
||||||
|
element.rel = 'stylesheet';
|
||||||
|
element.href = script.src;
|
||||||
|
} else {
|
||||||
|
element.src = script.src;
|
||||||
|
|
||||||
|
if (script.attrs) {
|
||||||
|
for (const attr in script.attrs) {
|
||||||
|
element[attr] = script.attrs[attr];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let cancelled = false;
|
element.id = script.id;
|
||||||
const script = document.createElement('script');
|
|
||||||
script.src = 'node_modules/tinymce/tinymce.min.js';
|
element.onload = () => {
|
||||||
script.id = 'tinyMceScript';
|
resolve();
|
||||||
script.onload = () => {
|
|
||||||
if (cancelled) return;
|
|
||||||
setScriptLoaded(true);
|
|
||||||
};
|
};
|
||||||
document.getElementsByTagName('head')[0].appendChild(script);
|
|
||||||
|
document.getElementsByTagName('head')[0].appendChild(element);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
async function loadScripts() {
|
||||||
|
const scriptsToLoad:any[] = [
|
||||||
|
{
|
||||||
|
src: 'node_modules/tinymce/tinymce.min.js',
|
||||||
|
id: 'tinyMceScript',
|
||||||
|
loaded: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: 'gui/editors/TinyMCE/plugins/lists.js',
|
||||||
|
id: 'tinyMceListsPluginScript',
|
||||||
|
loaded: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const s of scriptsToLoad) {
|
||||||
|
if (document.getElementById(s.id)) {
|
||||||
|
s.loaded = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.info('Loading script', s.src);
|
||||||
|
|
||||||
|
await loadScript(s);
|
||||||
|
if (cancelled) return;
|
||||||
|
|
||||||
|
s.loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
setScriptLoaded(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadScripts();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
};
|
};
|
||||||
|
@ -210,12 +263,12 @@ const TinyMCE = (props:TinyMCEProps, ref:any) => {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
resize: false,
|
resize: false,
|
||||||
plugins: 'noneditable link lists hr searchreplace',
|
plugins: 'noneditable link joplinLists hr searchreplace codesample',
|
||||||
noneditable_noneditable_class: 'joplin-editable', // Can be a regex too
|
noneditable_noneditable_class: 'joplin-editable', // Can be a regex too
|
||||||
valid_elements: '*[*]', // We already filter in sanitize_html
|
valid_elements: '*[*]', // We already filter in sanitize_html
|
||||||
menubar: false,
|
menubar: false,
|
||||||
branding: false,
|
branding: false,
|
||||||
toolbar: 'bold italic | link codeformat customAttach | numlist bullist h1 h2 h3 hr blockquote',
|
toolbar: 'bold italic | link codeformat codesample joplinAttach | numlist bullist joplinChecklist | h1 h2 h3 hr blockquote',
|
||||||
setup: (editor:any) => {
|
setup: (editor:any) => {
|
||||||
|
|
||||||
function openEditDialog(editable:any) {
|
function openEditDialog(editable:any) {
|
||||||
|
@ -266,7 +319,7 @@ const TinyMCE = (props:TinyMCEProps, ref:any) => {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.ui.registry.addButton('customAttach', {
|
editor.ui.registry.addButton('joplinAttach', {
|
||||||
tooltip: 'Attach...',
|
tooltip: 'Attach...',
|
||||||
icon: 'upload',
|
icon: 'upload',
|
||||||
onAction: async function() {
|
onAction: async function() {
|
||||||
|
@ -310,28 +363,20 @@ const TinyMCE = (props:TinyMCEProps, ref:any) => {
|
||||||
// Set the initial content and load the plugin CSS and JS files
|
// Set the initial content and load the plugin CSS and JS files
|
||||||
// -----------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
useEffect(() => {
|
const loadDocumentAssets = (editor:any, pluginAssets:any[]) => {
|
||||||
if (!editor) return () => {};
|
const cssFiles = ['css/fork-awesome.min.css'].concat(pluginAssets
|
||||||
|
|
||||||
let cancelled = false;
|
|
||||||
|
|
||||||
const loadContent = async () => {
|
|
||||||
const result = await props.markupToHtml(props.defaultEditorState.markupLanguage, props.defaultEditorState.value);
|
|
||||||
if (cancelled) return;
|
|
||||||
|
|
||||||
editor.setContent(result.html);
|
|
||||||
|
|
||||||
const cssFiles = result.pluginAssets
|
|
||||||
.filter((a:any) => a.mime === 'text/css' && !loadedAssetFiles_.includes(a.path))
|
.filter((a:any) => a.mime === 'text/css' && !loadedAssetFiles_.includes(a.path))
|
||||||
.map((a:any) => a.path);
|
.map((a:any) => a.path));
|
||||||
|
|
||||||
const jsFiles = result.pluginAssets
|
const jsFiles = pluginAssets
|
||||||
.filter((a:any) => a.mime === 'application/javascript' && !loadedAssetFiles_.includes(a.path))
|
.filter((a:any) => a.mime === 'application/javascript' && !loadedAssetFiles_.includes(a.path))
|
||||||
.map((a:any) => a.path);
|
.map((a:any) => a.path);
|
||||||
|
|
||||||
for (const cssFile of cssFiles) loadedAssetFiles_.push(cssFile);
|
for (const cssFile of cssFiles) loadedAssetFiles_.push(cssFile);
|
||||||
for (const jsFile of jsFiles) loadedAssetFiles_.push(jsFile);
|
for (const jsFile of jsFiles) loadedAssetFiles_.push(jsFile);
|
||||||
|
|
||||||
|
console.info('loadDocumentAssets: files to load', cssFiles, jsFiles);
|
||||||
|
|
||||||
if (cssFiles.length) editor.dom.loadCSS(cssFiles.join(','));
|
if (cssFiles.length) editor.dom.loadCSS(cssFiles.join(','));
|
||||||
|
|
||||||
if (jsFiles.length) {
|
if (jsFiles.length) {
|
||||||
|
@ -347,6 +392,26 @@ const TinyMCE = (props:TinyMCEProps, ref:any) => {
|
||||||
editor.getDoc().getElementsByTagName('head')[0].appendChild(script);
|
editor.getDoc().getElementsByTagName('head')[0].appendChild(script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!editor) return () => {};
|
||||||
|
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
const loadContent = async () => {
|
||||||
|
const result = await props.markupToHtml(props.defaultEditorState.markupLanguage, props.defaultEditorState.value, {
|
||||||
|
plugins: {
|
||||||
|
checkbox: {
|
||||||
|
renderingType: 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (cancelled) return;
|
||||||
|
|
||||||
|
editor.setContent(result.html);
|
||||||
|
|
||||||
|
await loadDocumentAssets(editor, await props.allAssets(props.defaultEditorState.markupLanguage));
|
||||||
|
|
||||||
editor.getDoc().addEventListener('click', onEditorContentClick);
|
editor.getDoc().addEventListener('click', onEditorContentClick);
|
||||||
|
|
||||||
|
@ -359,7 +424,7 @@ const TinyMCE = (props:TinyMCEProps, ref:any) => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
editor.getDoc().removeEventListener('click', onEditorContentClick);
|
editor.getDoc().removeEventListener('click', onEditorContentClick);
|
||||||
};
|
};
|
||||||
}, [editor, props.markupToHtml, props.defaultEditorState, onEditorContentClick]);
|
}, [editor, props.markupToHtml, props.allAssets, props.defaultEditorState, onEditorContentClick]);
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------------
|
||||||
// Handle onChange event
|
// Handle onChange event
|
||||||
|
@ -410,7 +475,7 @@ const TinyMCE = (props:TinyMCEProps, ref:any) => {
|
||||||
//
|
//
|
||||||
// Any maybe others, so to catch them all we only check the prefix
|
// Any maybe others, so to catch them all we only check the prefix
|
||||||
|
|
||||||
const changeCommands = ['mceBlockQuote'];
|
const changeCommands = ['mceBlockQuote', 'ToggleJoplinChecklistItem'];
|
||||||
|
|
||||||
if (changeCommands.includes(c) || c.indexOf('Insert') === 0 || c.indexOf('mceToggle') === 0 || c.indexOf('mceInsert') === 0) {
|
if (changeCommands.includes(c) || c.indexOf('Insert') === 0 || c.indexOf('mceToggle') === 0 || c.indexOf('mceInsert') === 0) {
|
||||||
onChangeHandler();
|
onChangeHandler();
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -15,14 +15,27 @@ const tasks = {
|
||||||
fn: require('./tools/electronRebuild.js'),
|
fn: require('./tools/electronRebuild.js'),
|
||||||
},
|
},
|
||||||
copyLib: require('../Tools/gulp/tasks/copyLib'),
|
copyLib: require('../Tools/gulp/tasks/copyLib'),
|
||||||
|
tsc: require('../Tools/gulp/tasks/tsc'),
|
||||||
};
|
};
|
||||||
|
|
||||||
utils.registerGulpTasks(gulp, tasks);
|
utils.registerGulpTasks(gulp, tasks);
|
||||||
|
|
||||||
gulp.task('build', gulp.series(
|
const buildSeries = [
|
||||||
'copyLib',
|
'copyLib',
|
||||||
|
];
|
||||||
|
|
||||||
|
// On Windows also run tsc because `npm run watch` locks some folders
|
||||||
|
// which makes the copyPluginAssets command fail. For that reason,
|
||||||
|
// it's not possible to run watch on Windows while testing the desktop app.
|
||||||
|
if (require('os').platform() === 'win32') {
|
||||||
|
buildSeries.push('tsc');
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildParallel = [
|
||||||
|
gulp.series(...buildSeries),
|
||||||
'compileScripts',
|
'compileScripts',
|
||||||
'compilePackageInfo',
|
'compilePackageInfo',
|
||||||
'copyPluginAssets',
|
'copyPluginAssets',
|
||||||
// 'electronRebuild'
|
];
|
||||||
));
|
|
||||||
|
gulp.task('build', gulp.parallel(...buildParallel));
|
||||||
|
|
|
@ -252,6 +252,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
|
||||||
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
|
||||||
},
|
},
|
||||||
|
"acorn": {
|
||||||
|
"version": "7.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
|
||||||
|
"integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg=="
|
||||||
|
},
|
||||||
"acorn-globals": {
|
"acorn-globals": {
|
||||||
"version": "4.3.4",
|
"version": "4.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
|
||||||
|
@ -262,9 +267,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"acorn": {
|
"acorn": {
|
||||||
"version": "6.4.0",
|
"version": "6.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
|
||||||
"integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw=="
|
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA=="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -681,11 +686,6 @@
|
||||||
"integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==",
|
"integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"async-limiter": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ=="
|
|
||||||
},
|
|
||||||
"async-mutex": {
|
"async-mutex": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.1.3.tgz",
|
||||||
|
@ -843,7 +843,8 @@
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"aproba": {
|
"aproba": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
|
@ -867,13 +868,15 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"brace-expansion": {
|
"brace-expansion": {
|
||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
|
@ -890,19 +893,22 @@
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"console-control-strings": {
|
"console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"core-util-is": {
|
"core-util-is": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
@ -1033,7 +1039,8 @@
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"ini": {
|
"ini": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
|
@ -1047,6 +1054,7 @@
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"number-is-nan": "^1.0.0"
|
"number-is-nan": "^1.0.0"
|
||||||
}
|
}
|
||||||
|
@ -1063,6 +1071,7 @@
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
|
@ -1071,13 +1080,15 @@
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.3.5",
|
"version": "2.3.5",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
|
"integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"safe-buffer": "^5.1.2",
|
"safe-buffer": "^5.1.2",
|
||||||
"yallist": "^3.0.0"
|
"yallist": "^3.0.0"
|
||||||
|
@ -1098,6 +1109,7 @@
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"minimist": "0.0.8"
|
"minimist": "0.0.8"
|
||||||
}
|
}
|
||||||
|
@ -1186,7 +1198,8 @@
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
|
@ -1200,6 +1213,7 @@
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
|
@ -1337,6 +1351,7 @@
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"code-point-at": "^1.0.0",
|
"code-point-at": "^1.0.0",
|
||||||
"is-fullwidth-code-point": "^1.0.0",
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
@ -1358,6 +1373,7 @@
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
|
"optional": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"ansi-regex": "^2.0.0"
|
"ansi-regex": "^2.0.0"
|
||||||
}
|
}
|
||||||
|
@ -1406,7 +1422,8 @@
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"optional": true
|
||||||
},
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
|
@ -2992,6 +3009,26 @@
|
||||||
"resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/css-b64-images/-/css-b64-images-0.2.5.tgz",
|
||||||
"integrity": "sha1-QgBdgyBLK0pdk7axpWRBM7WSegI="
|
"integrity": "sha1-QgBdgyBLK0pdk7axpWRBM7WSegI="
|
||||||
},
|
},
|
||||||
|
"cssom": {
|
||||||
|
"version": "0.4.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz",
|
||||||
|
"integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw=="
|
||||||
|
},
|
||||||
|
"cssstyle": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-sEb3XFPx3jNnCAMtqrXPDeSgQr+jojtCeNf8cvMNMh1cG970+lljssvQDzPq6lmmJu2Vhqood/gtEomBiHOGnA==",
|
||||||
|
"requires": {
|
||||||
|
"cssom": "~0.3.6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"cssom": {
|
||||||
|
"version": "0.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
|
||||||
|
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"csstype": {
|
"csstype": {
|
||||||
"version": "2.6.4",
|
"version": "2.6.4",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.4.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.4.tgz",
|
||||||
|
@ -3304,6 +3341,16 @@
|
||||||
"assert-plus": "^1.0.0"
|
"assert-plus": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"data-urls": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
|
||||||
|
"requires": {
|
||||||
|
"abab": "^2.0.0",
|
||||||
|
"whatwg-mimetype": "^2.2.0",
|
||||||
|
"whatwg-url": "^7.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
@ -3560,6 +3607,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz",
|
||||||
"integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ=="
|
"integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ=="
|
||||||
},
|
},
|
||||||
|
"domexception": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
|
||||||
|
"requires": {
|
||||||
|
"webidl-conversions": "^4.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"domhandler": {
|
"domhandler": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-3.0.0.tgz",
|
||||||
|
@ -4840,9 +4895,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.2",
|
"version": "7.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||||
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
|
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
|
@ -5275,7 +5330,6 @@
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
|
@ -5284,8 +5338,7 @@
|
||||||
"version": "0.0.8",
|
"version": "0.0.8",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||||
"dev": true,
|
"dev": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"minipass": {
|
"minipass": {
|
||||||
"version": "2.9.0",
|
"version": "2.9.0",
|
||||||
|
@ -5410,8 +5463,7 @@
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": false,
|
"resolved": false,
|
||||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||||
"dev": true,
|
"dev": true
|
||||||
"optional": true
|
|
||||||
},
|
},
|
||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
|
@ -6152,6 +6204,14 @@
|
||||||
"lru-cache": "^5.1.1"
|
"lru-cache": "^5.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"html-encoding-sniffer": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
|
||||||
|
"requires": {
|
||||||
|
"whatwg-encoding": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"html-entities": {
|
"html-entities": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
|
||||||
|
@ -6305,6 +6365,11 @@
|
||||||
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
|
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"ip-regex": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
|
||||||
|
"integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
|
||||||
|
},
|
||||||
"is-absolute": {
|
"is-absolute": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz",
|
||||||
|
@ -6646,140 +6711,13 @@
|
||||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||||
},
|
},
|
||||||
"joplin-turndown": {
|
"joplin-turndown": {
|
||||||
"version": "4.0.23",
|
"version": "4.0.24",
|
||||||
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.23.tgz",
|
"resolved": "https://registry.npmjs.org/joplin-turndown/-/joplin-turndown-4.0.24.tgz",
|
||||||
"integrity": "sha512-Dh93R7G/S/KRbOu4/+FIxoUcUDcoUL4QDsqGhperOi/cUxUeg8fngrmEzdP8kEpQzqm5+8jkq9Cc1w6695owpQ==",
|
"integrity": "sha512-mKd2rAFzJKnhTVjEpHomG+T01//uz5rXVSAOYRh3/JKXpY7QUhVp8jCmFfO+kaadrLABTz04mvTmyyoOadxdTA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"css": "^2.2.4",
|
"css": "^2.2.4",
|
||||||
"html-entities": "^1.2.1",
|
"html-entities": "^1.2.1",
|
||||||
"jsdom": "^11.9.0"
|
"jsdom": "^15.2.1"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"acorn": {
|
|
||||||
"version": "5.7.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
|
|
||||||
"integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw=="
|
|
||||||
},
|
|
||||||
"cssom": {
|
|
||||||
"version": "0.3.8",
|
|
||||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
|
|
||||||
"integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg=="
|
|
||||||
},
|
|
||||||
"cssstyle": {
|
|
||||||
"version": "1.4.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz",
|
|
||||||
"integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==",
|
|
||||||
"requires": {
|
|
||||||
"cssom": "0.3.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"data-urls": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
|
|
||||||
"requires": {
|
|
||||||
"abab": "^2.0.0",
|
|
||||||
"whatwg-mimetype": "^2.2.0",
|
|
||||||
"whatwg-url": "^7.0.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"whatwg-url": {
|
|
||||||
"version": "7.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
|
|
||||||
"integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
|
|
||||||
"requires": {
|
|
||||||
"lodash.sortby": "^4.7.0",
|
|
||||||
"tr46": "^1.0.1",
|
|
||||||
"webidl-conversions": "^4.0.2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domexception": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
|
|
||||||
"requires": {
|
|
||||||
"webidl-conversions": "^4.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"html-encoding-sniffer": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
|
|
||||||
"requires": {
|
|
||||||
"whatwg-encoding": "^1.0.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"jsdom": {
|
|
||||||
"version": "11.12.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz",
|
|
||||||
"integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==",
|
|
||||||
"requires": {
|
|
||||||
"abab": "^2.0.0",
|
|
||||||
"acorn": "^5.5.3",
|
|
||||||
"acorn-globals": "^4.1.0",
|
|
||||||
"array-equal": "^1.0.0",
|
|
||||||
"cssom": ">= 0.3.2 < 0.4.0",
|
|
||||||
"cssstyle": "^1.0.0",
|
|
||||||
"data-urls": "^1.0.0",
|
|
||||||
"domexception": "^1.0.1",
|
|
||||||
"escodegen": "^1.9.1",
|
|
||||||
"html-encoding-sniffer": "^1.0.2",
|
|
||||||
"left-pad": "^1.3.0",
|
|
||||||
"nwsapi": "^2.0.7",
|
|
||||||
"parse5": "4.0.0",
|
|
||||||
"pn": "^1.1.0",
|
|
||||||
"request": "^2.87.0",
|
|
||||||
"request-promise-native": "^1.0.5",
|
|
||||||
"sax": "^1.2.4",
|
|
||||||
"symbol-tree": "^3.2.2",
|
|
||||||
"tough-cookie": "^2.3.4",
|
|
||||||
"w3c-hr-time": "^1.0.1",
|
|
||||||
"webidl-conversions": "^4.0.2",
|
|
||||||
"whatwg-encoding": "^1.0.3",
|
|
||||||
"whatwg-mimetype": "^2.1.0",
|
|
||||||
"whatwg-url": "^6.4.1",
|
|
||||||
"ws": "^5.2.0",
|
|
||||||
"xml-name-validator": "^3.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"parse5": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA=="
|
|
||||||
},
|
|
||||||
"tr46": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
|
|
||||||
"integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
|
|
||||||
"requires": {
|
|
||||||
"punycode": "^2.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"webidl-conversions": {
|
|
||||||
"version": "4.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
|
||||||
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
|
|
||||||
},
|
|
||||||
"whatwg-url": {
|
|
||||||
"version": "6.5.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz",
|
|
||||||
"integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==",
|
|
||||||
"requires": {
|
|
||||||
"lodash.sortby": "^4.7.0",
|
|
||||||
"tr46": "^1.0.1",
|
|
||||||
"webidl-conversions": "^4.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ws": {
|
|
||||||
"version": "5.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz",
|
|
||||||
"integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==",
|
|
||||||
"requires": {
|
|
||||||
"async-limiter": "~1.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"joplin-turndown-plugin-gfm": {
|
"joplin-turndown-plugin-gfm": {
|
||||||
|
@ -6808,6 +6746,152 @@
|
||||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
|
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"jsdom": {
|
||||||
|
"version": "15.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-15.2.1.tgz",
|
||||||
|
"integrity": "sha512-fAl1W0/7T2G5vURSyxBzrJ1LSdQn6Tr5UX/xD4PXDx/PDgwygedfW6El/KIj3xJ7FU61TTYnc/l/B7P49Eqt6g==",
|
||||||
|
"requires": {
|
||||||
|
"abab": "^2.0.0",
|
||||||
|
"acorn": "^7.1.0",
|
||||||
|
"acorn-globals": "^4.3.2",
|
||||||
|
"array-equal": "^1.0.0",
|
||||||
|
"cssom": "^0.4.1",
|
||||||
|
"cssstyle": "^2.0.0",
|
||||||
|
"data-urls": "^1.1.0",
|
||||||
|
"domexception": "^1.0.1",
|
||||||
|
"escodegen": "^1.11.1",
|
||||||
|
"html-encoding-sniffer": "^1.0.2",
|
||||||
|
"nwsapi": "^2.2.0",
|
||||||
|
"parse5": "5.1.0",
|
||||||
|
"pn": "^1.1.0",
|
||||||
|
"request": "^2.88.0",
|
||||||
|
"request-promise-native": "^1.0.7",
|
||||||
|
"saxes": "^3.1.9",
|
||||||
|
"symbol-tree": "^3.2.2",
|
||||||
|
"tough-cookie": "^3.0.1",
|
||||||
|
"w3c-hr-time": "^1.0.1",
|
||||||
|
"w3c-xmlserializer": "^1.1.2",
|
||||||
|
"webidl-conversions": "^4.0.2",
|
||||||
|
"whatwg-encoding": "^1.0.5",
|
||||||
|
"whatwg-mimetype": "^2.3.0",
|
||||||
|
"whatwg-url": "^7.0.0",
|
||||||
|
"ws": "^7.0.0",
|
||||||
|
"xml-name-validator": "^3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"ajv": {
|
||||||
|
"version": "6.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
|
||||||
|
"integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
|
||||||
|
"requires": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
"json-schema-traverse": "^0.4.1",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"aws4": {
|
||||||
|
"version": "1.9.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
|
||||||
|
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
|
||||||
|
},
|
||||||
|
"fast-deep-equal": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA=="
|
||||||
|
},
|
||||||
|
"har-validator": {
|
||||||
|
"version": "5.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
|
||||||
|
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
|
||||||
|
"requires": {
|
||||||
|
"ajv": "^6.5.5",
|
||||||
|
"har-schema": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"json-schema-traverse": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||||
|
},
|
||||||
|
"mime-db": {
|
||||||
|
"version": "1.43.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
|
||||||
|
"integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ=="
|
||||||
|
},
|
||||||
|
"mime-types": {
|
||||||
|
"version": "2.1.26",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
|
||||||
|
"integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
|
||||||
|
"requires": {
|
||||||
|
"mime-db": "1.43.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oauth-sign": {
|
||||||
|
"version": "0.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||||
|
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"version": "2.88.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
||||||
|
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
|
||||||
|
"requires": {
|
||||||
|
"aws-sign2": "~0.7.0",
|
||||||
|
"aws4": "^1.8.0",
|
||||||
|
"caseless": "~0.12.0",
|
||||||
|
"combined-stream": "~1.0.6",
|
||||||
|
"extend": "~3.0.2",
|
||||||
|
"forever-agent": "~0.6.1",
|
||||||
|
"form-data": "~2.3.2",
|
||||||
|
"har-validator": "~5.1.3",
|
||||||
|
"http-signature": "~1.2.0",
|
||||||
|
"is-typedarray": "~1.0.0",
|
||||||
|
"isstream": "~0.1.2",
|
||||||
|
"json-stringify-safe": "~5.0.1",
|
||||||
|
"mime-types": "~2.1.19",
|
||||||
|
"oauth-sign": "~0.9.0",
|
||||||
|
"performance-now": "^2.1.0",
|
||||||
|
"qs": "~6.5.2",
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"tough-cookie": "~2.5.0",
|
||||||
|
"tunnel-agent": "^0.6.0",
|
||||||
|
"uuid": "^3.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tough-cookie": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||||
|
"requires": {
|
||||||
|
"psl": "^1.1.28",
|
||||||
|
"punycode": "^2.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
|
||||||
|
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
|
||||||
|
},
|
||||||
|
"tough-cookie": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
|
||||||
|
"integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
|
||||||
|
"requires": {
|
||||||
|
"ip-regex": "^2.1.0",
|
||||||
|
"psl": "^1.1.28",
|
||||||
|
"punycode": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"jsesc": {
|
"jsesc": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
|
||||||
|
@ -6973,11 +7057,6 @@
|
||||||
"flush-write-stream": "^1.0.2"
|
"flush-write-stream": "^1.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"left-pad": {
|
|
||||||
"version": "1.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz",
|
|
||||||
"integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA=="
|
|
||||||
},
|
|
||||||
"levenshtein": {
|
"levenshtein": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/levenshtein/-/levenshtein-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/levenshtein/-/levenshtein-1.0.5.tgz",
|
||||||
|
@ -8577,6 +8656,11 @@
|
||||||
"integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
|
"integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"parse5": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz",
|
||||||
|
"integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ=="
|
||||||
|
},
|
||||||
"pascalcase": {
|
"pascalcase": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
|
||||||
|
@ -9164,8 +9248,7 @@
|
||||||
"psl": {
|
"psl": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.6.0.tgz",
|
||||||
"integrity": "sha512-SYKKmVel98NCOYXpkwUqZqh0ahZeeKfmisiLIcEZdsb+WbLv02g/dI5BUmZnIyOe7RzZtLax81nnb2HbvC2tzA==",
|
"integrity": "sha512-SYKKmVel98NCOYXpkwUqZqh0ahZeeKfmisiLIcEZdsb+WbLv02g/dI5BUmZnIyOe7RzZtLax81nnb2HbvC2tzA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"pump": {
|
"pump": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
|
@ -9985,6 +10068,14 @@
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
|
||||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
|
||||||
},
|
},
|
||||||
|
"saxes": {
|
||||||
|
"version": "3.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.11.tgz",
|
||||||
|
"integrity": "sha512-Ydydq3zC+WYDJK1+gRxRapLIED9PWeSuuS41wqyoRmzvhhh9nc+QQrVMKJYzJFULazeGhzSV0QleN2wD3boh2g==",
|
||||||
|
"requires": {
|
||||||
|
"xmlchars": "^2.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"scheduler": {
|
"scheduler": {
|
||||||
"version": "0.15.0",
|
"version": "0.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.15.0.tgz",
|
||||||
|
@ -10912,6 +11003,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"tr46": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=",
|
||||||
|
"requires": {
|
||||||
|
"punycode": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"trim-right": {
|
"trim-right": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
|
||||||
|
@ -11254,7 +11353,6 @@
|
||||||
"version": "4.2.2",
|
"version": "4.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
||||||
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
|
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"punycode": "^2.1.0"
|
"punycode": "^2.1.0"
|
||||||
}
|
}
|
||||||
|
@ -11428,6 +11526,16 @@
|
||||||
"browser-process-hrtime": "^1.0.0"
|
"browser-process-hrtime": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"w3c-xmlserializer": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==",
|
||||||
|
"requires": {
|
||||||
|
"domexception": "^1.0.1",
|
||||||
|
"webidl-conversions": "^4.0.2",
|
||||||
|
"xml-name-validator": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"wcwidth": {
|
"wcwidth": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||||
|
@ -11437,6 +11545,11 @@
|
||||||
"defaults": "^1.0.3"
|
"defaults": "^1.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"webidl-conversions": {
|
||||||
|
"version": "4.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
||||||
|
"integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="
|
||||||
|
},
|
||||||
"whatwg-encoding": {
|
"whatwg-encoding": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz",
|
||||||
|
@ -11465,6 +11578,16 @@
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz",
|
||||||
"integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="
|
"integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g=="
|
||||||
},
|
},
|
||||||
|
"whatwg-url": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
|
||||||
|
"requires": {
|
||||||
|
"lodash.sortby": "^4.7.0",
|
||||||
|
"tr46": "^1.0.1",
|
||||||
|
"webidl-conversions": "^4.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"which": {
|
"which": {
|
||||||
"version": "1.3.1",
|
"version": "1.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
|
||||||
|
@ -11634,6 +11757,11 @@
|
||||||
"typedarray-to-buffer": "^3.1.5"
|
"typedarray-to-buffer": "^3.1.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ws": {
|
||||||
|
"version": "7.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.2.3.tgz",
|
||||||
|
"integrity": "sha512-HTDl9G9hbkNDk98naoR/cHDws7+EyYMOdL1BmjsZXRUjf7d+MficC4B7HLUPlSiho0vg+CWKrGIt/VJBd1xunQ=="
|
||||||
|
},
|
||||||
"xdg-basedir": {
|
"xdg-basedir": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
|
||||||
|
@ -11661,6 +11789,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"xmlchars": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
||||||
|
},
|
||||||
"xtend": {
|
"xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
"dist": "node_modules/.bin/electron-builder",
|
"dist": "node_modules/.bin/electron-builder",
|
||||||
"build": "patch-package --patch-dir ../patches && gulp build",
|
"build": "patch-package --patch-dir ../patches && gulp build",
|
||||||
"postinstall": "npm run build && gulp electronRebuild",
|
"postinstall": "npm run build && gulp electronRebuild",
|
||||||
"start": "gulp build -L && electron . --env dev --log-level debug --no-welcome --open-dev-tools"
|
"start": "gulp build && electron . --env dev --log-level debug --no-welcome --open-dev-tools"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -75,6 +75,7 @@
|
||||||
"electron": "^7.1.12",
|
"electron": "^7.1.12",
|
||||||
"electron-builder": "22.3.2",
|
"electron-builder": "22.3.2",
|
||||||
"electron-rebuild": "^1.8.8",
|
"electron-rebuild": "^1.8.8",
|
||||||
|
"glob": "^7.1.6",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^4.0.2",
|
||||||
"patch-package": "^6.2.0"
|
"patch-package": "^6.2.0"
|
||||||
},
|
},
|
||||||
|
@ -109,7 +110,7 @@
|
||||||
"html-minifier": "^4.0.0",
|
"html-minifier": "^4.0.0",
|
||||||
"htmlparser2": "^4.1.0",
|
"htmlparser2": "^4.1.0",
|
||||||
"image-type": "^3.0.0",
|
"image-type": "^3.0.0",
|
||||||
"joplin-turndown": "^4.0.23",
|
"joplin-turndown": "^4.0.24",
|
||||||
"joplin-turndown-plugin-gfm": "^1.0.12",
|
"joplin-turndown-plugin-gfm": "^1.0.12",
|
||||||
"json-stringify-safe": "^5.0.1",
|
"json-stringify-safe": "^5.0.1",
|
||||||
"jssha": "^2.3.1",
|
"jssha": "^2.3.1",
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
.DS_Store
|
||||||
|
*~
|
||||||
|
*.iws
|
||||||
|
*.sublime-workspace
|
||||||
|
.cache
|
||||||
|
/lib
|
||||||
|
/dist
|
||||||
|
/scratch
|
||||||
|
node_modules
|
||||||
|
/config/repl
|
||||||
|
/*.log
|
||||||
|
ephox-*.tgz
|
||||||
|
package-lock.json
|
||||||
|
jenkins-plumbing
|
|
@ -0,0 +1,196 @@
|
||||||
|
const { CheckerPlugin } = require('awesome-typescript-loader');
|
||||||
|
const LiveReloadPlugin = require('webpack-livereload-plugin');
|
||||||
|
const path = require('path');
|
||||||
|
const swag = require('@ephox/swag');
|
||||||
|
|
||||||
|
module.exports = function(grunt) {
|
||||||
|
const packageData = grunt.file.readJSON('package.json');
|
||||||
|
const BUILD_VERSION = `${packageData.version}-${process.env.BUILD_NUMBER ? process.env.BUILD_NUMBER : '0'}`;
|
||||||
|
const libPluginPath = 'lib/Main.js';
|
||||||
|
const scratchPluginPath = 'scratch/compiled/joplinLists.js';
|
||||||
|
const scratchPluginMinPath = 'scratch/compiled/joplinLists.min.js';
|
||||||
|
const tsDemoSourceFile = path.resolve('src/demo/ts/Demo.ts');
|
||||||
|
const jsDemoDestFile = path.resolve('scratch/compiled/demo.js');
|
||||||
|
|
||||||
|
grunt.initConfig({
|
||||||
|
pkg: packageData,
|
||||||
|
|
||||||
|
clean: {
|
||||||
|
dirs: ['dist', 'scratch'],
|
||||||
|
},
|
||||||
|
|
||||||
|
// tslint: {
|
||||||
|
// options: {
|
||||||
|
// configuration: 'tslint.json'
|
||||||
|
// },
|
||||||
|
// plugin: ['src/**/*.ts']
|
||||||
|
// },
|
||||||
|
|
||||||
|
shell: {
|
||||||
|
command: 'tsc',
|
||||||
|
},
|
||||||
|
|
||||||
|
rollup: {
|
||||||
|
options: {
|
||||||
|
treeshake: true,
|
||||||
|
external: [
|
||||||
|
'tinymce/core/api/PluginManager',
|
||||||
|
'tinymce/core/api/util/Tools',
|
||||||
|
'tinymce/core/api/dom/BookmarkManager',
|
||||||
|
'tinymce/core/api/Editor',
|
||||||
|
'tinymce/core/api/dom/DOMUtils',
|
||||||
|
'tinymce/core/api/dom/RangeUtils',
|
||||||
|
'tinymce/core/api/dom/TreeWalker',
|
||||||
|
'tinymce/core/api/util/VK',
|
||||||
|
'tinymce/core/api/dom/DomQuery',
|
||||||
|
],
|
||||||
|
globals: {
|
||||||
|
'tinymce/core/api/PluginManager': 'tinymce.PluginManager',
|
||||||
|
'tinymce/core/api/util/Tools': 'tinymce.util.Tools',
|
||||||
|
'tinymce/core/api/dom/BookmarkManager': 'tinymce.dom.BookmarkManager',
|
||||||
|
'tinymce/core/api/Editor': 'tinymce.Editor',
|
||||||
|
'tinymce/core/api/dom/DOMUtils': 'tinymce.dom.DOMUtils',
|
||||||
|
'tinymce/core/api/dom/RangeUtils': 'tinymce.dom.RangeUtils',
|
||||||
|
'tinymce/core/api/dom/TreeWalker': 'tinymce.dom.TreeWalker',
|
||||||
|
'tinymce/core/api/util/VK': 'tinymce.util.VK',
|
||||||
|
'tinymce/core/api/dom/DomQuery': 'tinymce.dom.DomQuery',
|
||||||
|
},
|
||||||
|
format: 'iife',
|
||||||
|
onwarn: swag.onwarn,
|
||||||
|
plugins: [
|
||||||
|
swag.nodeResolve({
|
||||||
|
basedir: __dirname,
|
||||||
|
prefixes: {},
|
||||||
|
}),
|
||||||
|
swag.remapImports(),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugin: {
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
src: libPluginPath,
|
||||||
|
dest: scratchPluginPath,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
uglify: {
|
||||||
|
plugin: {
|
||||||
|
files: [
|
||||||
|
{
|
||||||
|
src: scratchPluginPath,
|
||||||
|
dest: scratchPluginMinPath,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
concat: {
|
||||||
|
license: {
|
||||||
|
options: {
|
||||||
|
process: function(src) {
|
||||||
|
const buildSuffix = process.env.BUILD_NUMBER
|
||||||
|
? `-${process.env.BUILD_NUMBER}`
|
||||||
|
: '';
|
||||||
|
return src.replace(
|
||||||
|
/@BUILD_NUMBER@/g,
|
||||||
|
packageData.version + buildSuffix
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// scratchPluginMinPath is used twice on purpose, all outputs will be minified for premium plugins
|
||||||
|
files: {
|
||||||
|
'dist/joplinLists.js': [
|
||||||
|
'src/text/license-header.js',
|
||||||
|
scratchPluginPath,
|
||||||
|
],
|
||||||
|
'dist/joplinLists.min.js': [
|
||||||
|
'src/text/license-header.js',
|
||||||
|
scratchPluginMinPath,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
copy: {
|
||||||
|
css: {
|
||||||
|
files: [
|
||||||
|
// {
|
||||||
|
// cwd: 'src/text',
|
||||||
|
// src: ['license.txt'],
|
||||||
|
// dest: 'dist',
|
||||||
|
// expand: true,
|
||||||
|
// },
|
||||||
|
// { src: ['changelog.txt'], dest: 'dist', expand: true },
|
||||||
|
{
|
||||||
|
src: ['dist/joplinLists.js'],
|
||||||
|
dest: '../../../ElectronClient/gui/editors/TinyMCE/plugins/lists.js',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
webpack: {
|
||||||
|
options: {
|
||||||
|
mode: 'development',
|
||||||
|
watch: true,
|
||||||
|
},
|
||||||
|
dev: {
|
||||||
|
entry: tsDemoSourceFile,
|
||||||
|
devtool: 'source-map',
|
||||||
|
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.js'],
|
||||||
|
},
|
||||||
|
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
use: ['source-map-loader'],
|
||||||
|
enforce: 'pre',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.ts$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'ts-loader',
|
||||||
|
options: {
|
||||||
|
transpileOnly: true,
|
||||||
|
experimentalWatchApi: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
plugins: [new LiveReloadPlugin(), new CheckerPlugin()],
|
||||||
|
|
||||||
|
output: {
|
||||||
|
filename: path.basename(jsDemoDestFile),
|
||||||
|
path: path.dirname(jsDemoDestFile),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
require('load-grunt-tasks')(grunt);
|
||||||
|
grunt.loadNpmTasks('@ephox/swag');
|
||||||
|
|
||||||
|
// grunt.registerTask('version', 'Creates a version file', function() {
|
||||||
|
// grunt.file.write('dist/version.txt', BUILD_VERSION);
|
||||||
|
// });
|
||||||
|
|
||||||
|
grunt.registerTask('default', [
|
||||||
|
'clean',
|
||||||
|
// 'tslint',
|
||||||
|
'shell',
|
||||||
|
'rollup',
|
||||||
|
'uglify',
|
||||||
|
'concat',
|
||||||
|
'copy',
|
||||||
|
// 'version',
|
||||||
|
]);
|
||||||
|
};
|
|
@ -0,0 +1,502 @@
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 2.1, February 1999
|
||||||
|
|
||||||
|
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||||
|
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
[This is the first released version of the Lesser GPL. It also counts
|
||||||
|
as the successor of the GNU Library Public License, version 2, hence
|
||||||
|
the version number 2.1.]
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The licenses for most software are designed to take away your
|
||||||
|
freedom to share and change it. By contrast, the GNU General Public
|
||||||
|
Licenses are intended to guarantee your freedom to share and change
|
||||||
|
free software--to make sure the software is free for all its users.
|
||||||
|
|
||||||
|
This license, the Lesser General Public License, applies to some
|
||||||
|
specially designated software packages--typically libraries--of the
|
||||||
|
Free Software Foundation and other authors who decide to use it. You
|
||||||
|
can use it too, but we suggest you first think carefully about whether
|
||||||
|
this license or the ordinary General Public License is the better
|
||||||
|
strategy to use in any particular case, based on the explanations below.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom of use,
|
||||||
|
not price. Our General Public Licenses are designed to make sure that
|
||||||
|
you have the freedom to distribute copies of free software (and charge
|
||||||
|
for this service if you wish); that you receive source code or can get
|
||||||
|
it if you want it; that you can change the software and use pieces of
|
||||||
|
it in new free programs; and that you are informed that you can do
|
||||||
|
these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to make restrictions that forbid
|
||||||
|
distributors to deny you these rights or to ask you to surrender these
|
||||||
|
rights. These restrictions translate to certain responsibilities for
|
||||||
|
you if you distribute copies of the library or if you modify it.
|
||||||
|
|
||||||
|
For example, if you distribute copies of the library, whether gratis
|
||||||
|
or for a fee, you must give the recipients all the rights that we gave
|
||||||
|
you. You must make sure that they, too, receive or can get the source
|
||||||
|
code. If you link other code with the library, you must provide
|
||||||
|
complete object files to the recipients, so that they can relink them
|
||||||
|
with the library after making changes to the library and recompiling
|
||||||
|
it. And you must show them these terms so they know their rights.
|
||||||
|
|
||||||
|
We protect your rights with a two-step method: (1) we copyright the
|
||||||
|
library, and (2) we offer you this license, which gives you legal
|
||||||
|
permission to copy, distribute and/or modify the library.
|
||||||
|
|
||||||
|
To protect each distributor, we want to make it very clear that
|
||||||
|
there is no warranty for the free library. Also, if the library is
|
||||||
|
modified by someone else and passed on, the recipients should know
|
||||||
|
that what they have is not the original version, so that the original
|
||||||
|
author's reputation will not be affected by problems that might be
|
||||||
|
introduced by others.
|
||||||
|
|
||||||
|
Finally, software patents pose a constant threat to the existence of
|
||||||
|
any free program. We wish to make sure that a company cannot
|
||||||
|
effectively restrict the users of a free program by obtaining a
|
||||||
|
restrictive license from a patent holder. Therefore, we insist that
|
||||||
|
any patent license obtained for a version of the library must be
|
||||||
|
consistent with the full freedom of use specified in this license.
|
||||||
|
|
||||||
|
Most GNU software, including some libraries, is covered by the
|
||||||
|
ordinary GNU General Public License. This license, the GNU Lesser
|
||||||
|
General Public License, applies to certain designated libraries, and
|
||||||
|
is quite different from the ordinary General Public License. We use
|
||||||
|
this license for certain libraries in order to permit linking those
|
||||||
|
libraries into non-free programs.
|
||||||
|
|
||||||
|
When a program is linked with a library, whether statically or using
|
||||||
|
a shared library, the combination of the two is legally speaking a
|
||||||
|
combined work, a derivative of the original library. The ordinary
|
||||||
|
General Public License therefore permits such linking only if the
|
||||||
|
entire combination fits its criteria of freedom. The Lesser General
|
||||||
|
Public License permits more lax criteria for linking other code with
|
||||||
|
the library.
|
||||||
|
|
||||||
|
We call this license the "Lesser" General Public License because it
|
||||||
|
does Less to protect the user's freedom than the ordinary General
|
||||||
|
Public License. It also provides other free software developers Less
|
||||||
|
of an advantage over competing non-free programs. These disadvantages
|
||||||
|
are the reason we use the ordinary General Public License for many
|
||||||
|
libraries. However, the Lesser license provides advantages in certain
|
||||||
|
special circumstances.
|
||||||
|
|
||||||
|
For example, on rare occasions, there may be a special need to
|
||||||
|
encourage the widest possible use of a certain library, so that it becomes
|
||||||
|
a de-facto standard. To achieve this, non-free programs must be
|
||||||
|
allowed to use the library. A more frequent case is that a free
|
||||||
|
library does the same job as widely used non-free libraries. In this
|
||||||
|
case, there is little to gain by limiting the free library to free
|
||||||
|
software only, so we use the Lesser General Public License.
|
||||||
|
|
||||||
|
In other cases, permission to use a particular library in non-free
|
||||||
|
programs enables a greater number of people to use a large body of
|
||||||
|
free software. For example, permission to use the GNU C Library in
|
||||||
|
non-free programs enables many more people to use the whole GNU
|
||||||
|
operating system, as well as its variant, the GNU/Linux operating
|
||||||
|
system.
|
||||||
|
|
||||||
|
Although the Lesser General Public License is Less protective of the
|
||||||
|
users' freedom, it does ensure that the user of a program that is
|
||||||
|
linked with the Library has the freedom and the wherewithal to run
|
||||||
|
that program using a modified version of the Library.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow. Pay close attention to the difference between a
|
||||||
|
"work based on the library" and a "work that uses the library". The
|
||||||
|
former contains code derived from the library, whereas the latter must
|
||||||
|
be combined with the library in order to run.
|
||||||
|
|
||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||||
|
|
||||||
|
0. This License Agreement applies to any software library or other
|
||||||
|
program which contains a notice placed by the copyright holder or
|
||||||
|
other authorized party saying it may be distributed under the terms of
|
||||||
|
this Lesser General Public License (also called "this License").
|
||||||
|
Each licensee is addressed as "you".
|
||||||
|
|
||||||
|
A "library" means a collection of software functions and/or data
|
||||||
|
prepared so as to be conveniently linked with application programs
|
||||||
|
(which use some of those functions and data) to form executables.
|
||||||
|
|
||||||
|
The "Library", below, refers to any such software library or work
|
||||||
|
which has been distributed under these terms. A "work based on the
|
||||||
|
Library" means either the Library or any derivative work under
|
||||||
|
copyright law: that is to say, a work containing the Library or a
|
||||||
|
portion of it, either verbatim or with modifications and/or translated
|
||||||
|
straightforwardly into another language. (Hereinafter, translation is
|
||||||
|
included without limitation in the term "modification".)
|
||||||
|
|
||||||
|
"Source code" for a work means the preferred form of the work for
|
||||||
|
making modifications to it. For a library, complete source code means
|
||||||
|
all the source code for all modules it contains, plus any associated
|
||||||
|
interface definition files, plus the scripts used to control compilation
|
||||||
|
and installation of the library.
|
||||||
|
|
||||||
|
Activities other than copying, distribution and modification are not
|
||||||
|
covered by this License; they are outside its scope. The act of
|
||||||
|
running a program using the Library is not restricted, and output from
|
||||||
|
such a program is covered only if its contents constitute a work based
|
||||||
|
on the Library (independent of the use of the Library in a tool for
|
||||||
|
writing it). Whether that is true depends on what the Library does
|
||||||
|
and what the program that uses the Library does.
|
||||||
|
|
||||||
|
1. You may copy and distribute verbatim copies of the Library's
|
||||||
|
complete source code as you receive it, in any medium, provided that
|
||||||
|
you conspicuously and appropriately publish on each copy an
|
||||||
|
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||||
|
all the notices that refer to this License and to the absence of any
|
||||||
|
warranty; and distribute a copy of this License along with the
|
||||||
|
Library.
|
||||||
|
|
||||||
|
You may charge a fee for the physical act of transferring a copy,
|
||||||
|
and you may at your option offer warranty protection in exchange for a
|
||||||
|
fee.
|
||||||
|
|
||||||
|
2. You may modify your copy or copies of the Library or any portion
|
||||||
|
of it, thus forming a work based on the Library, and copy and
|
||||||
|
distribute such modifications or work under the terms of Section 1
|
||||||
|
above, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The modified work must itself be a software library.
|
||||||
|
|
||||||
|
b) You must cause the files modified to carry prominent notices
|
||||||
|
stating that you changed the files and the date of any change.
|
||||||
|
|
||||||
|
c) You must cause the whole of the work to be licensed at no
|
||||||
|
charge to all third parties under the terms of this License.
|
||||||
|
|
||||||
|
d) If a facility in the modified Library refers to a function or a
|
||||||
|
table of data to be supplied by an application program that uses
|
||||||
|
the facility, other than as an argument passed when the facility
|
||||||
|
is invoked, then you must make a good faith effort to ensure that,
|
||||||
|
in the event an application does not supply such function or
|
||||||
|
table, the facility still operates, and performs whatever part of
|
||||||
|
its purpose remains meaningful.
|
||||||
|
|
||||||
|
(For example, a function in a library to compute square roots has
|
||||||
|
a purpose that is entirely well-defined independent of the
|
||||||
|
application. Therefore, Subsection 2d requires that any
|
||||||
|
application-supplied function or table used by this function must
|
||||||
|
be optional: if the application does not supply it, the square
|
||||||
|
root function must still compute square roots.)
|
||||||
|
|
||||||
|
These requirements apply to the modified work as a whole. If
|
||||||
|
identifiable sections of that work are not derived from the Library,
|
||||||
|
and can be reasonably considered independent and separate works in
|
||||||
|
themselves, then this License, and its terms, do not apply to those
|
||||||
|
sections when you distribute them as separate works. But when you
|
||||||
|
distribute the same sections as part of a whole which is a work based
|
||||||
|
on the Library, the distribution of the whole must be on the terms of
|
||||||
|
this License, whose permissions for other licensees extend to the
|
||||||
|
entire whole, and thus to each and every part regardless of who wrote
|
||||||
|
it.
|
||||||
|
|
||||||
|
Thus, it is not the intent of this section to claim rights or contest
|
||||||
|
your rights to work written entirely by you; rather, the intent is to
|
||||||
|
exercise the right to control the distribution of derivative or
|
||||||
|
collective works based on the Library.
|
||||||
|
|
||||||
|
In addition, mere aggregation of another work not based on the Library
|
||||||
|
with the Library (or with a work based on the Library) on a volume of
|
||||||
|
a storage or distribution medium does not bring the other work under
|
||||||
|
the scope of this License.
|
||||||
|
|
||||||
|
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||||
|
License instead of this License to a given copy of the Library. To do
|
||||||
|
this, you must alter all the notices that refer to this License, so
|
||||||
|
that they refer to the ordinary GNU General Public License, version 2,
|
||||||
|
instead of to this License. (If a newer version than version 2 of the
|
||||||
|
ordinary GNU General Public License has appeared, then you can specify
|
||||||
|
that version instead if you wish.) Do not make any other change in
|
||||||
|
these notices.
|
||||||
|
|
||||||
|
Once this change is made in a given copy, it is irreversible for
|
||||||
|
that copy, so the ordinary GNU General Public License applies to all
|
||||||
|
subsequent copies and derivative works made from that copy.
|
||||||
|
|
||||||
|
This option is useful when you wish to copy part of the code of
|
||||||
|
the Library into a program that is not a library.
|
||||||
|
|
||||||
|
4. You may copy and distribute the Library (or a portion or
|
||||||
|
derivative of it, under Section 2) in object code or executable form
|
||||||
|
under the terms of Sections 1 and 2 above provided that you accompany
|
||||||
|
it with the complete corresponding machine-readable source code, which
|
||||||
|
must be distributed under the terms of Sections 1 and 2 above on a
|
||||||
|
medium customarily used for software interchange.
|
||||||
|
|
||||||
|
If distribution of object code is made by offering access to copy
|
||||||
|
from a designated place, then offering equivalent access to copy the
|
||||||
|
source code from the same place satisfies the requirement to
|
||||||
|
distribute the source code, even though third parties are not
|
||||||
|
compelled to copy the source along with the object code.
|
||||||
|
|
||||||
|
5. A program that contains no derivative of any portion of the
|
||||||
|
Library, but is designed to work with the Library by being compiled or
|
||||||
|
linked with it, is called a "work that uses the Library". Such a
|
||||||
|
work, in isolation, is not a derivative work of the Library, and
|
||||||
|
therefore falls outside the scope of this License.
|
||||||
|
|
||||||
|
However, linking a "work that uses the Library" with the Library
|
||||||
|
creates an executable that is a derivative of the Library (because it
|
||||||
|
contains portions of the Library), rather than a "work that uses the
|
||||||
|
library". The executable is therefore covered by this License.
|
||||||
|
Section 6 states terms for distribution of such executables.
|
||||||
|
|
||||||
|
When a "work that uses the Library" uses material from a header file
|
||||||
|
that is part of the Library, the object code for the work may be a
|
||||||
|
derivative work of the Library even though the source code is not.
|
||||||
|
Whether this is true is especially significant if the work can be
|
||||||
|
linked without the Library, or if the work is itself a library. The
|
||||||
|
threshold for this to be true is not precisely defined by law.
|
||||||
|
|
||||||
|
If such an object file uses only numerical parameters, data
|
||||||
|
structure layouts and accessors, and small macros and small inline
|
||||||
|
functions (ten lines or less in length), then the use of the object
|
||||||
|
file is unrestricted, regardless of whether it is legally a derivative
|
||||||
|
work. (Executables containing this object code plus portions of the
|
||||||
|
Library will still fall under Section 6.)
|
||||||
|
|
||||||
|
Otherwise, if the work is a derivative of the Library, you may
|
||||||
|
distribute the object code for the work under the terms of Section 6.
|
||||||
|
Any executables containing that work also fall under Section 6,
|
||||||
|
whether or not they are linked directly with the Library itself.
|
||||||
|
|
||||||
|
6. As an exception to the Sections above, you may also combine or
|
||||||
|
link a "work that uses the Library" with the Library to produce a
|
||||||
|
work containing portions of the Library, and distribute that work
|
||||||
|
under terms of your choice, provided that the terms permit
|
||||||
|
modification of the work for the customer's own use and reverse
|
||||||
|
engineering for debugging such modifications.
|
||||||
|
|
||||||
|
You must give prominent notice with each copy of the work that the
|
||||||
|
Library is used in it and that the Library and its use are covered by
|
||||||
|
this License. You must supply a copy of this License. If the work
|
||||||
|
during execution displays copyright notices, you must include the
|
||||||
|
copyright notice for the Library among them, as well as a reference
|
||||||
|
directing the user to the copy of this License. Also, you must do one
|
||||||
|
of these things:
|
||||||
|
|
||||||
|
a) Accompany the work with the complete corresponding
|
||||||
|
machine-readable source code for the Library including whatever
|
||||||
|
changes were used in the work (which must be distributed under
|
||||||
|
Sections 1 and 2 above); and, if the work is an executable linked
|
||||||
|
with the Library, with the complete machine-readable "work that
|
||||||
|
uses the Library", as object code and/or source code, so that the
|
||||||
|
user can modify the Library and then relink to produce a modified
|
||||||
|
executable containing the modified Library. (It is understood
|
||||||
|
that the user who changes the contents of definitions files in the
|
||||||
|
Library will not necessarily be able to recompile the application
|
||||||
|
to use the modified definitions.)
|
||||||
|
|
||||||
|
b) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (1) uses at run time a
|
||||||
|
copy of the library already present on the user's computer system,
|
||||||
|
rather than copying library functions into the executable, and (2)
|
||||||
|
will operate properly with a modified version of the library, if
|
||||||
|
the user installs one, as long as the modified version is
|
||||||
|
interface-compatible with the version that the work was made with.
|
||||||
|
|
||||||
|
c) Accompany the work with a written offer, valid for at
|
||||||
|
least three years, to give the same user the materials
|
||||||
|
specified in Subsection 6a, above, for a charge no more
|
||||||
|
than the cost of performing this distribution.
|
||||||
|
|
||||||
|
d) If distribution of the work is made by offering access to copy
|
||||||
|
from a designated place, offer equivalent access to copy the above
|
||||||
|
specified materials from the same place.
|
||||||
|
|
||||||
|
e) Verify that the user has already received a copy of these
|
||||||
|
materials or that you have already sent this user a copy.
|
||||||
|
|
||||||
|
For an executable, the required form of the "work that uses the
|
||||||
|
Library" must include any data and utility programs needed for
|
||||||
|
reproducing the executable from it. However, as a special exception,
|
||||||
|
the materials to be distributed need not include anything that is
|
||||||
|
normally distributed (in either source or binary form) with the major
|
||||||
|
components (compiler, kernel, and so on) of the operating system on
|
||||||
|
which the executable runs, unless that component itself accompanies
|
||||||
|
the executable.
|
||||||
|
|
||||||
|
It may happen that this requirement contradicts the license
|
||||||
|
restrictions of other proprietary libraries that do not normally
|
||||||
|
accompany the operating system. Such a contradiction means you cannot
|
||||||
|
use both them and the Library together in an executable that you
|
||||||
|
distribute.
|
||||||
|
|
||||||
|
7. You may place library facilities that are a work based on the
|
||||||
|
Library side-by-side in a single library together with other library
|
||||||
|
facilities not covered by this License, and distribute such a combined
|
||||||
|
library, provided that the separate distribution of the work based on
|
||||||
|
the Library and of the other library facilities is otherwise
|
||||||
|
permitted, and provided that you do these two things:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work
|
||||||
|
based on the Library, uncombined with any other library
|
||||||
|
facilities. This must be distributed under the terms of the
|
||||||
|
Sections above.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library of the fact
|
||||||
|
that part of it is a work based on the Library, and explaining
|
||||||
|
where to find the accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
8. You may not copy, modify, sublicense, link with, or distribute
|
||||||
|
the Library except as expressly provided under this License. Any
|
||||||
|
attempt otherwise to copy, modify, sublicense, link with, or
|
||||||
|
distribute the Library is void, and will automatically terminate your
|
||||||
|
rights under this License. However, parties who have received copies,
|
||||||
|
or rights, from you under this License will not have their licenses
|
||||||
|
terminated so long as such parties remain in full compliance.
|
||||||
|
|
||||||
|
9. You are not required to accept this License, since you have not
|
||||||
|
signed it. However, nothing else grants you permission to modify or
|
||||||
|
distribute the Library or its derivative works. These actions are
|
||||||
|
prohibited by law if you do not accept this License. Therefore, by
|
||||||
|
modifying or distributing the Library (or any work based on the
|
||||||
|
Library), you indicate your acceptance of this License to do so, and
|
||||||
|
all its terms and conditions for copying, distributing or modifying
|
||||||
|
the Library or works based on it.
|
||||||
|
|
||||||
|
10. Each time you redistribute the Library (or any work based on the
|
||||||
|
Library), the recipient automatically receives a license from the
|
||||||
|
original licensor to copy, distribute, link with or modify the Library
|
||||||
|
subject to these terms and conditions. You may not impose any further
|
||||||
|
restrictions on the recipients' exercise of the rights granted herein.
|
||||||
|
You are not responsible for enforcing compliance by third parties with
|
||||||
|
this License.
|
||||||
|
|
||||||
|
11. If, as a consequence of a court judgment or allegation of patent
|
||||||
|
infringement or for any other reason (not limited to patent issues),
|
||||||
|
conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot
|
||||||
|
distribute so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you
|
||||||
|
may not distribute the Library at all. For example, if a patent
|
||||||
|
license would not permit royalty-free redistribution of the Library by
|
||||||
|
all those who receive copies directly or indirectly through you, then
|
||||||
|
the only way you could satisfy both it and this License would be to
|
||||||
|
refrain entirely from distribution of the Library.
|
||||||
|
|
||||||
|
If any portion of this section is held invalid or unenforceable under any
|
||||||
|
particular circumstance, the balance of the section is intended to apply,
|
||||||
|
and the section as a whole is intended to apply in other circumstances.
|
||||||
|
|
||||||
|
It is not the purpose of this section to induce you to infringe any
|
||||||
|
patents or other property right claims or to contest validity of any
|
||||||
|
such claims; this section has the sole purpose of protecting the
|
||||||
|
integrity of the free software distribution system which is
|
||||||
|
implemented by public license practices. Many people have made
|
||||||
|
generous contributions to the wide range of software distributed
|
||||||
|
through that system in reliance on consistent application of that
|
||||||
|
system; it is up to the author/donor to decide if he or she is willing
|
||||||
|
to distribute software through any other system and a licensee cannot
|
||||||
|
impose that choice.
|
||||||
|
|
||||||
|
This section is intended to make thoroughly clear what is believed to
|
||||||
|
be a consequence of the rest of this License.
|
||||||
|
|
||||||
|
12. If the distribution and/or use of the Library is restricted in
|
||||||
|
certain countries either by patents or by copyrighted interfaces, the
|
||||||
|
original copyright holder who places the Library under this License may add
|
||||||
|
an explicit geographical distribution limitation excluding those countries,
|
||||||
|
so that distribution is permitted only in or among countries not thus
|
||||||
|
excluded. In such case, this License incorporates the limitation as if
|
||||||
|
written in the body of this License.
|
||||||
|
|
||||||
|
13. The Free Software Foundation may publish revised and/or new
|
||||||
|
versions of the Lesser General Public License from time to time.
|
||||||
|
Such new versions will be similar in spirit to the present version,
|
||||||
|
but may differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the Library
|
||||||
|
specifies a version number of this License which applies to it and
|
||||||
|
"any later version", you have the option of following the terms and
|
||||||
|
conditions either of that version or of any later version published by
|
||||||
|
the Free Software Foundation. If the Library does not specify a
|
||||||
|
license version number, you may choose any version ever published by
|
||||||
|
the Free Software Foundation.
|
||||||
|
|
||||||
|
14. If you wish to incorporate parts of the Library into other free
|
||||||
|
programs whose distribution conditions are incompatible with these,
|
||||||
|
write to the author to ask for permission. For software which is
|
||||||
|
copyrighted by the Free Software Foundation, write to the Free
|
||||||
|
Software Foundation; we sometimes make exceptions for this. Our
|
||||||
|
decision will be guided by the two goals of preserving the free status
|
||||||
|
of all derivatives of our free software and of promoting the sharing
|
||||||
|
and reuse of software generally.
|
||||||
|
|
||||||
|
NO WARRANTY
|
||||||
|
|
||||||
|
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||||
|
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||||
|
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||||
|
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||||
|
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||||
|
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||||
|
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||||
|
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||||
|
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||||
|
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||||
|
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||||
|
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||||
|
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||||
|
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||||
|
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||||
|
DAMAGES.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Libraries
|
||||||
|
|
||||||
|
If you develop a new library, and you want it to be of the greatest
|
||||||
|
possible use to the public, we recommend making it free software that
|
||||||
|
everyone can redistribute and change. You can do so by permitting
|
||||||
|
redistribution under these terms (or, alternatively, under the terms of the
|
||||||
|
ordinary General Public License).
|
||||||
|
|
||||||
|
To apply these terms, attach the following notices to the library. It is
|
||||||
|
safest to attach them to the start of each source file to most effectively
|
||||||
|
convey the exclusion of warranty; and each file should have at least the
|
||||||
|
"copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
<one line to give the library's name and a brief idea of what it does.>
|
||||||
|
Copyright (C) <year> <name of author>
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2.1 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or your
|
||||||
|
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||||
|
necessary. Here is a sample; alter the names:
|
||||||
|
|
||||||
|
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||||
|
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||||
|
|
||||||
|
<signature of Ty Coon>, 1 April 1990
|
||||||
|
Ty Coon, President of Vice
|
||||||
|
|
||||||
|
That's all there is to it!
|
|
@ -0,0 +1,5 @@
|
||||||
|
# TinyMCE Joplin Lists Plugin
|
||||||
|
|
||||||
|
This is based on https://github.com/tinymce/tinymce/tree/59748a11303fb7cf00fdb8c9392dcb082ee9d965/modules/tinymce/src/plugins/lists
|
||||||
|
|
||||||
|
But with support for Joplin checkboxes.
|
|
@ -0,0 +1,57 @@
|
||||||
|
declare module 'tinymce/core/api/util/Tools' {
|
||||||
|
const Tools:any;
|
||||||
|
export default Tools;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'tinymce/core/api/Editor' {
|
||||||
|
export default interface Editor {
|
||||||
|
on: Function,
|
||||||
|
off: Function,
|
||||||
|
execCommand: Function,
|
||||||
|
getBody: Function,
|
||||||
|
getParam: Function,
|
||||||
|
fire: Function,
|
||||||
|
nodeChanged: Function,
|
||||||
|
selection: any,
|
||||||
|
contentDocument: any,
|
||||||
|
dom: any,
|
||||||
|
schema: any,
|
||||||
|
undoManager: any,
|
||||||
|
ui: any,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'tinymce/core/api/dom/BookmarkManager' {
|
||||||
|
const BookmarkManager:any;
|
||||||
|
export default BookmarkManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'tinymce/core/api/dom/DOMUtils' {
|
||||||
|
const DOMUtils:any;
|
||||||
|
export default DOMUtils;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'tinymce/core/api/dom/RangeUtils' {
|
||||||
|
const RangeUtils:any;
|
||||||
|
export default RangeUtils;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'tinymce/core/api/dom/TreeWalker' {
|
||||||
|
const TreeWalker:any;
|
||||||
|
export default TreeWalker;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'tinymce/core/api/util/VK' {
|
||||||
|
const VK:any;
|
||||||
|
export default VK;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'tinymce/core/api/dom/DomQuery' {
|
||||||
|
const DomQuery:any;
|
||||||
|
export default DomQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'tinymce/core/api/PluginManager' {
|
||||||
|
const PluginManager:any;
|
||||||
|
export default PluginManager;
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
{
|
||||||
|
"name": "joplin-tinymce-lists",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"scripts": {
|
||||||
|
"prepublishOnly": "npm run lint && npm run build",
|
||||||
|
"lint": "tslint src/**/*.ts",
|
||||||
|
"build": "grunt",
|
||||||
|
"test": "bedrock-auto -b phantomjs -d src/test/ts/",
|
||||||
|
"test-manual": "bedrock -d src/test/ts/",
|
||||||
|
"start": "grunt webpack",
|
||||||
|
"buildAndStart": "yarn build && cd .. && cd .. && cd .. && cd ElectronClient && npm start"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "Tiny Technologies Inc.",
|
||||||
|
"devDependencies": {
|
||||||
|
"@ephox/agar": "latest",
|
||||||
|
"@ephox/bedrock": "latest",
|
||||||
|
"@ephox/mcagar": "latest",
|
||||||
|
"@ephox/swag": "latest",
|
||||||
|
"@ephox/tslint-rules": "latest",
|
||||||
|
"awesome-typescript-loader": "^5.2.1",
|
||||||
|
"grunt": "^1.0.4",
|
||||||
|
"grunt-contrib-clean": "^2.0.0",
|
||||||
|
"grunt-contrib-concat": "^1.0.1",
|
||||||
|
"grunt-contrib-copy": "^1.0.0",
|
||||||
|
"grunt-contrib-uglify": "^4.0.0",
|
||||||
|
"grunt-shell": "^2.1.0",
|
||||||
|
"grunt-tslint": "^5.0.2",
|
||||||
|
"grunt-webpack": "^3.1.3",
|
||||||
|
"load-grunt-tasks": "^4.0.0",
|
||||||
|
"tinymce": "latest",
|
||||||
|
"ts-loader": "^5.3.0",
|
||||||
|
"tslib": "^1.9.3",
|
||||||
|
"tslint": "^5.11.0",
|
||||||
|
"typescript": "^3.1.6",
|
||||||
|
"webpack": "^4.25.1",
|
||||||
|
"webpack-livereload-plugin": "^2.1.1"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib/main",
|
||||||
|
"lib/demo",
|
||||||
|
"lib/test",
|
||||||
|
"src",
|
||||||
|
"tsconfig.json",
|
||||||
|
"readme.md",
|
||||||
|
"LEGAL.txt",
|
||||||
|
"LICENSE.txt"
|
||||||
|
],
|
||||||
|
"main": "./lib/main/ts/api/Main.js",
|
||||||
|
"module": "./lib/main/ts/api/Main.js",
|
||||||
|
"types": "./lib/main/ts/api/Main.d.ts",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Plugin from './Plugin';
|
||||||
|
|
||||||
|
Plugin();
|
||||||
|
|
||||||
|
/*******
|
||||||
|
* DO NOT EXPORT ANYTHING
|
||||||
|
*
|
||||||
|
* IF YOU DO ROLLUP WILL LEAVE A GLOBAL ON THE PAGE
|
||||||
|
*******/
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import PluginManager from 'tinymce/core/api/PluginManager';
|
||||||
|
import * as Api from './api/Api';
|
||||||
|
import * as Commands from './api/Commands';
|
||||||
|
import * as Keyboard from './core/Keyboard';
|
||||||
|
import * as Buttons from './ui/Buttons';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
PluginManager.add('joplinLists', function (editor) {
|
||||||
|
Keyboard.setup(editor);
|
||||||
|
Buttons.register(editor);
|
||||||
|
Commands.register(editor);
|
||||||
|
|
||||||
|
return Api.get(editor);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Arr } from '@ephox/katamari';
|
||||||
|
import { Element} from '@ephox/sugar';
|
||||||
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
|
import { Indentation } from '../listModel/Indentation';
|
||||||
|
import { listIndentation } from '../listModel/ListsIndendation';
|
||||||
|
import { dlIndentation } from '../core/DlIndentation';
|
||||||
|
import * as Range from '../core/Range';
|
||||||
|
import * as Selection from '../core/Selection';
|
||||||
|
|
||||||
|
const selectionIndentation = (editor: Editor, indentation: Indentation): boolean => {
|
||||||
|
const lists = Arr.map(Selection.getSelectedListRoots(editor), Element.fromDom);
|
||||||
|
const dlItems = Arr.map(Selection.getSelectedDlItems(editor), Element.fromDom);
|
||||||
|
let isHandled = false;
|
||||||
|
|
||||||
|
if (lists.length || dlItems.length) {
|
||||||
|
const bookmark = editor.selection.getBookmark();
|
||||||
|
|
||||||
|
listIndentation(editor, lists, indentation);
|
||||||
|
dlIndentation(editor, indentation, dlItems);
|
||||||
|
|
||||||
|
editor.selection.moveToBookmark(bookmark);
|
||||||
|
editor.selection.setRng(Range.normalizeRange(editor.selection.getRng()));
|
||||||
|
editor.nodeChanged();
|
||||||
|
isHandled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isHandled;
|
||||||
|
};
|
||||||
|
|
||||||
|
const indentListSelection = (editor: Editor): boolean => {
|
||||||
|
return selectionIndentation(editor, Indentation.Indent);
|
||||||
|
};
|
||||||
|
|
||||||
|
const outdentListSelection = (editor: Editor): boolean => {
|
||||||
|
return selectionIndentation(editor, Indentation.Outdent);
|
||||||
|
};
|
||||||
|
|
||||||
|
const flattenListSelection = (editor: Editor): boolean => {
|
||||||
|
return selectionIndentation(editor, Indentation.Flatten);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
indentListSelection,
|
||||||
|
outdentListSelection,
|
||||||
|
flattenListSelection
|
||||||
|
};
|
|
@ -0,0 +1,307 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import BookmarkManager from 'tinymce/core/api/dom/BookmarkManager';
|
||||||
|
import Tools from 'tinymce/core/api/util/Tools';
|
||||||
|
import * as Bookmark from '../core/Bookmark';
|
||||||
|
import * as NodeType from '../core/NodeType';
|
||||||
|
import * as Selection from '../core/Selection';
|
||||||
|
import { HTMLElement } from '@ephox/dom-globals';
|
||||||
|
import { flattenListSelection } from './Indendation';
|
||||||
|
import { fireListEvent } from '../api/Events';
|
||||||
|
import { isCustomList } from '../core/Util';
|
||||||
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
|
import { listToggleActionFromListName } from '../core/ListAction';
|
||||||
|
import { findContainerListTypeFromElement } from '../listModel/JoplinListUtil';
|
||||||
|
|
||||||
|
const updateListStyle = function (dom, el, detail) {
|
||||||
|
const type = detail['list-style-type'] ? detail['list-style-type'] : null;
|
||||||
|
dom.setStyle(el, 'list-style-type', type);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setAttribs = function (elm, attrs) {
|
||||||
|
Tools.each(attrs, function (value, key) {
|
||||||
|
elm.setAttribute(key, value);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateListAttrs = function (dom, el, detail) {
|
||||||
|
setAttribs(el, detail['list-attributes']);
|
||||||
|
Tools.each(dom.select('li', el), function (li) {
|
||||||
|
setAttribs(li, detail['list-item-attributes']);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateListWithDetails = function (dom, el, detail) {
|
||||||
|
updateListStyle(dom, el, detail);
|
||||||
|
updateListAttrs(dom, el, detail);
|
||||||
|
|
||||||
|
if (detail.listType === 'joplinChecklist') {
|
||||||
|
el.classList.add('joplin-checklist');
|
||||||
|
} else {
|
||||||
|
el.classList.remove('joplin-checklist');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeStyles = (dom, element: HTMLElement, styles: string[]) => {
|
||||||
|
Tools.each(styles, (style) => dom.setStyle(element, { [style]: '' }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getEndPointNode = function (editor, rng, start, root) {
|
||||||
|
let container, offset;
|
||||||
|
|
||||||
|
container = rng[start ? 'startContainer' : 'endContainer'];
|
||||||
|
offset = rng[start ? 'startOffset' : 'endOffset'];
|
||||||
|
|
||||||
|
// Resolve node index
|
||||||
|
if (container.nodeType === 1) {
|
||||||
|
container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!start && NodeType.isBr(container.nextSibling)) {
|
||||||
|
container = container.nextSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (container.parentNode !== root) {
|
||||||
|
if (NodeType.isTextBlock(editor, container)) {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^(TD|TH)$/.test(container.parentNode.nodeName)) {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
container = container.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return container;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectedTextBlocks = function (editor, rng, root) {
|
||||||
|
const textBlocks = [], dom = editor.dom;
|
||||||
|
|
||||||
|
const startNode = getEndPointNode(editor, rng, true, root);
|
||||||
|
const endNode = getEndPointNode(editor, rng, false, root);
|
||||||
|
let block;
|
||||||
|
const siblings = [];
|
||||||
|
|
||||||
|
for (let node = startNode; node; node = node.nextSibling) {
|
||||||
|
siblings.push(node);
|
||||||
|
|
||||||
|
if (node === endNode) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Tools.each(siblings, function (node) {
|
||||||
|
if (NodeType.isTextBlock(editor, node)) {
|
||||||
|
textBlocks.push(node);
|
||||||
|
block = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dom.isBlock(node) || NodeType.isBr(node)) {
|
||||||
|
if (NodeType.isBr(node)) {
|
||||||
|
dom.remove(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
block = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextSibling = node.nextSibling;
|
||||||
|
if (BookmarkManager.isBookmarkNode(node)) {
|
||||||
|
if (NodeType.isTextBlock(editor, nextSibling) || (!nextSibling && node.parentNode === root)) {
|
||||||
|
block = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!block) {
|
||||||
|
block = dom.create('p');
|
||||||
|
node.parentNode.insertBefore(block, node);
|
||||||
|
textBlocks.push(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.appendChild(node);
|
||||||
|
});
|
||||||
|
|
||||||
|
return textBlocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasCompatibleStyle = function (dom, sib, detail) {
|
||||||
|
const sibStyle = dom.getStyle(sib, 'list-style-type');
|
||||||
|
let detailStyle = detail ? detail['list-style-type'] : '';
|
||||||
|
|
||||||
|
detailStyle = detailStyle === null ? '' : detailStyle;
|
||||||
|
|
||||||
|
return sibStyle === detailStyle;
|
||||||
|
};
|
||||||
|
|
||||||
|
const applyList = function (editor, listName: string, detail:any = {}) {
|
||||||
|
const rng = editor.selection.getRng(true);
|
||||||
|
let bookmark;
|
||||||
|
let listItemName = 'LI';
|
||||||
|
const root = Selection.getClosestListRootElm(editor, editor.selection.getStart(true));
|
||||||
|
const dom = editor.dom;
|
||||||
|
|
||||||
|
if (dom.getContentEditable(editor.selection.getNode()) === 'false') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
listName = listName.toUpperCase();
|
||||||
|
|
||||||
|
if (listName === 'DL') {
|
||||||
|
listItemName = 'DT';
|
||||||
|
}
|
||||||
|
|
||||||
|
bookmark = Bookmark.createBookmark(rng);
|
||||||
|
|
||||||
|
Tools.each(getSelectedTextBlocks(editor, rng, root), function (block) {
|
||||||
|
let listBlock, sibling;
|
||||||
|
|
||||||
|
sibling = block.previousSibling;
|
||||||
|
if (sibling && NodeType.isListNode(sibling) && sibling.nodeName === listName && hasCompatibleStyle(dom, sibling, detail)) {
|
||||||
|
listBlock = sibling;
|
||||||
|
block = dom.rename(block, listItemName);
|
||||||
|
sibling.appendChild(block);
|
||||||
|
} else {
|
||||||
|
listBlock = dom.create(listName);
|
||||||
|
if (detail.listType === 'joplinChecklist') {
|
||||||
|
listBlock.classList.add('joplin-checklist');
|
||||||
|
} else {
|
||||||
|
listBlock.classList.remove('joplin-checklist');
|
||||||
|
}
|
||||||
|
block.parentNode.insertBefore(listBlock, block);
|
||||||
|
listBlock.appendChild(block);
|
||||||
|
block = dom.rename(block, listItemName);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeStyles(dom, block, [
|
||||||
|
'margin', 'margin-right', 'margin-bottom', 'margin-left', 'margin-top',
|
||||||
|
'padding', 'padding-right', 'padding-bottom', 'padding-left', 'padding-top',
|
||||||
|
]);
|
||||||
|
|
||||||
|
updateListWithDetails(dom, listBlock, detail);
|
||||||
|
mergeWithAdjacentLists(editor.dom, listBlock);
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.selection.setRng(Bookmark.resolveBookmark(bookmark));
|
||||||
|
};
|
||||||
|
|
||||||
|
const isValidLists = function (list1, list2) {
|
||||||
|
return list1 && list2 && NodeType.isListNode(list1) && list1.nodeName === list2.nodeName;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasSameListStyle = function (dom, list1, list2) {
|
||||||
|
const targetStyle = dom.getStyle(list1, 'list-style-type', true);
|
||||||
|
const style = dom.getStyle(list2, 'list-style-type', true);
|
||||||
|
return targetStyle === style;
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasSameClasses = function (elm1, elm2) {
|
||||||
|
return elm1.className === elm2.className;
|
||||||
|
};
|
||||||
|
|
||||||
|
const shouldMerge = function (dom, list1, list2) {
|
||||||
|
return isValidLists(list1, list2) && hasSameListStyle(dom, list1, list2) && hasSameClasses(list1, list2);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeWithAdjacentLists = function (dom, listBlock) {
|
||||||
|
let sibling, node;
|
||||||
|
|
||||||
|
sibling = listBlock.nextSibling;
|
||||||
|
if (shouldMerge(dom, listBlock, sibling)) {
|
||||||
|
while ((node = sibling.firstChild)) {
|
||||||
|
listBlock.appendChild(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
dom.remove(sibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
sibling = listBlock.previousSibling;
|
||||||
|
if (shouldMerge(dom, listBlock, sibling)) {
|
||||||
|
while ((node = sibling.lastChild)) {
|
||||||
|
listBlock.insertBefore(node, listBlock.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
dom.remove(sibling);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateList = function (editor: Editor, list, listName, detail) {
|
||||||
|
if (list.nodeName !== listName) {
|
||||||
|
const newList = editor.dom.rename(list, listName);
|
||||||
|
updateListWithDetails(editor.dom, newList, detail);
|
||||||
|
fireListEvent(editor, listToggleActionFromListName(listName), newList);
|
||||||
|
} else {
|
||||||
|
updateListWithDetails(editor.dom, list, detail);
|
||||||
|
fireListEvent(editor, listToggleActionFromListName(listName), list);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleMultipleLists = function (editor, parentList, lists, listName, detail) {
|
||||||
|
if (parentList.nodeName === listName && !hasListStyleDetail(detail)) {
|
||||||
|
flattenListSelection(editor);
|
||||||
|
} else {
|
||||||
|
const bookmark = Bookmark.createBookmark(editor.selection.getRng(true));
|
||||||
|
|
||||||
|
Tools.each([parentList].concat(lists), function (elm) {
|
||||||
|
updateList(editor, elm, listName, detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.selection.setRng(Bookmark.resolveBookmark(bookmark));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasListStyleDetail = function (detail) {
|
||||||
|
return 'list-style-type' in detail;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleSingleList = function (editor, parentList, listName, detail) {
|
||||||
|
if (parentList === editor.getBody()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentList) {
|
||||||
|
const listType = findContainerListTypeFromElement(parentList);
|
||||||
|
if (parentList.nodeName === listName && !hasListStyleDetail(detail) && !isCustomList(parentList) && listType === detail.listType) {
|
||||||
|
flattenListSelection(editor);
|
||||||
|
} else {
|
||||||
|
const bookmark = Bookmark.createBookmark(editor.selection.getRng(true));
|
||||||
|
updateListWithDetails(editor.dom, parentList, detail);
|
||||||
|
const newList = editor.dom.rename(parentList, listName);
|
||||||
|
mergeWithAdjacentLists(editor.dom, newList);
|
||||||
|
editor.selection.setRng(Bookmark.resolveBookmark(bookmark));
|
||||||
|
fireListEvent(editor, listToggleActionFromListName(listName), newList);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
applyList(editor, listName, detail);
|
||||||
|
fireListEvent(editor, listToggleActionFromListName(listName), parentList);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleList = function (editor, listName, detail) {
|
||||||
|
const parentList = Selection.getParentList(editor);
|
||||||
|
const selectedSubLists = Selection.getSelectedSubLists(editor);
|
||||||
|
|
||||||
|
detail = {
|
||||||
|
listType: 'regular',
|
||||||
|
...detail,
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentList && selectedSubLists.length > 0) {
|
||||||
|
toggleMultipleLists(editor, parentList, selectedSubLists, listName, detail);
|
||||||
|
} else {
|
||||||
|
toggleSingleList(editor, parentList, listName, detail);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
toggleList,
|
||||||
|
mergeWithAdjacentLists
|
||||||
|
};
|
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as Delete from '../core/Delete';
|
||||||
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
|
|
||||||
|
const get = function (editor: Editor) {
|
||||||
|
return {
|
||||||
|
backspaceDelete (isForward: boolean) {
|
||||||
|
Delete.backspaceDelete(editor, isForward);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
get
|
||||||
|
};
|
|
@ -0,0 +1,55 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as ToggleList from '../actions/ToggleList';
|
||||||
|
import { indentListSelection, outdentListSelection, flattenListSelection } from '../actions/Indendation';
|
||||||
|
import { addJoplinChecklistCommands } from '../listModel/JoplinListUtil';
|
||||||
|
|
||||||
|
const queryListCommandState = function (editor, listName) {
|
||||||
|
return function () {
|
||||||
|
const parentList = editor.dom.getParent(editor.selection.getStart(), 'UL,OL,DL');
|
||||||
|
return parentList && parentList.nodeName === listName;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const register = function (editor) {
|
||||||
|
editor.on('BeforeExecCommand', function (e) {
|
||||||
|
const cmd = e.command.toLowerCase();
|
||||||
|
|
||||||
|
if (cmd === 'indent') {
|
||||||
|
indentListSelection(editor);
|
||||||
|
} else if (cmd === 'outdent') {
|
||||||
|
outdentListSelection(editor);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.addCommand('InsertUnorderedList', function (ui, detail) {
|
||||||
|
ToggleList.toggleList(editor, 'UL', detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.addCommand('InsertOrderedList', function (ui, detail) {
|
||||||
|
ToggleList.toggleList(editor, 'OL', detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.addCommand('InsertDefinitionList', function (ui, detail) {
|
||||||
|
ToggleList.toggleList(editor, 'DL', detail);
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.addCommand('RemoveList', () => {
|
||||||
|
flattenListSelection(editor);
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.addQueryStateHandler('InsertUnorderedList', queryListCommandState(editor, 'UL'));
|
||||||
|
editor.addQueryStateHandler('InsertOrderedList', queryListCommandState(editor, 'OL'));
|
||||||
|
editor.addQueryStateHandler('InsertDefinitionList', queryListCommandState(editor, 'DL'));
|
||||||
|
|
||||||
|
addJoplinChecklistCommands(editor, ToggleList);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
register
|
||||||
|
};
|
|
@ -0,0 +1,4 @@
|
||||||
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
|
import { ListAction } from '../core/ListAction';
|
||||||
|
|
||||||
|
export const fireListEvent = (editor: Editor, action: ListAction, element) => editor.fire('ListMutation', { action, element });
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
|
|
||||||
|
const shouldIndentOnTab = function (editor: Editor) {
|
||||||
|
return editor.getParam('lists_indent_on_tab', true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getForcedRootBlock = (editor: Editor): string => {
|
||||||
|
const block = editor.getParam('forced_root_block', 'p');
|
||||||
|
if (block === false) {
|
||||||
|
return '';
|
||||||
|
} else if (block === true) {
|
||||||
|
return 'p';
|
||||||
|
} else {
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getForcedRootBlockAttrs = (editor: Editor): Record<string, string> => {
|
||||||
|
return editor.getParam('forced_root_block_attrs', {});
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
shouldIndentOnTab,
|
||||||
|
getForcedRootBlock,
|
||||||
|
getForcedRootBlockAttrs
|
||||||
|
};
|
|
@ -0,0 +1,126 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import DOMUtils from 'tinymce/core/api/dom/DOMUtils';
|
||||||
|
import * as Range from './Range';
|
||||||
|
|
||||||
|
const DOM = DOMUtils.DOM;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a range bookmark. This will convert indexed bookmarks into temporary span elements with
|
||||||
|
* index 0 so that they can be restored properly after the DOM has been modified. Text bookmarks will not have spans
|
||||||
|
* added to them since they can be restored after a dom operation.
|
||||||
|
*
|
||||||
|
* So this: <p><b>|</b><b>|</b></p>
|
||||||
|
* becomes: <p><b><span data-mce-type="bookmark">|</span></b><b data-mce-type="bookmark">|</span></b></p>
|
||||||
|
*
|
||||||
|
* @param {DOMRange} rng DOM Range to get bookmark on.
|
||||||
|
* @return {Object} Bookmark object.
|
||||||
|
*/
|
||||||
|
const createBookmark = function (rng) {
|
||||||
|
const bookmark = {};
|
||||||
|
|
||||||
|
const setupEndPoint = function (start?) {
|
||||||
|
let offsetNode, container, offset;
|
||||||
|
|
||||||
|
container = rng[start ? 'startContainer' : 'endContainer'];
|
||||||
|
offset = rng[start ? 'startOffset' : 'endOffset'];
|
||||||
|
|
||||||
|
if (container.nodeType === 1) {
|
||||||
|
offsetNode = DOM.create('span', { 'data-mce-type': 'bookmark' });
|
||||||
|
|
||||||
|
if (container.hasChildNodes()) {
|
||||||
|
offset = Math.min(offset, container.childNodes.length - 1);
|
||||||
|
|
||||||
|
if (start) {
|
||||||
|
container.insertBefore(offsetNode, container.childNodes[offset]);
|
||||||
|
} else {
|
||||||
|
DOM.insertAfter(offsetNode, container.childNodes[offset]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
container.appendChild(offsetNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
container = offsetNode;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bookmark[start ? 'startContainer' : 'endContainer'] = container;
|
||||||
|
bookmark[start ? 'startOffset' : 'endOffset'] = offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
setupEndPoint(true);
|
||||||
|
|
||||||
|
if (!rng.collapsed) {
|
||||||
|
setupEndPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
return bookmark;
|
||||||
|
};
|
||||||
|
|
||||||
|
const resolveBookmark = function (bookmark) {
|
||||||
|
function restoreEndPoint(start?) {
|
||||||
|
let container, offset, node;
|
||||||
|
|
||||||
|
const nodeIndex = function (container) {
|
||||||
|
let node = container.parentNode.firstChild, idx = 0;
|
||||||
|
|
||||||
|
while (node) {
|
||||||
|
if (node === container) {
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip data-mce-type=bookmark nodes
|
||||||
|
if (node.nodeType !== 1 || node.getAttribute('data-mce-type') !== 'bookmark') {
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
node = node.nextSibling;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
container = node = bookmark[start ? 'startContainer' : 'endContainer'];
|
||||||
|
offset = bookmark[start ? 'startOffset' : 'endOffset'];
|
||||||
|
|
||||||
|
if (!container) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (container.nodeType === 1) {
|
||||||
|
offset = nodeIndex(container);
|
||||||
|
container = container.parentNode;
|
||||||
|
DOM.remove(node);
|
||||||
|
|
||||||
|
if (!container.hasChildNodes() && DOM.isBlock(container)) {
|
||||||
|
container.appendChild(DOM.create('br'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bookmark[start ? 'startContainer' : 'endContainer'] = container;
|
||||||
|
bookmark[start ? 'startOffset' : 'endOffset'] = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreEndPoint(true);
|
||||||
|
restoreEndPoint();
|
||||||
|
|
||||||
|
const rng = DOM.createRng();
|
||||||
|
|
||||||
|
rng.setStart(bookmark.startContainer, bookmark.startOffset);
|
||||||
|
|
||||||
|
if (bookmark.endContainer) {
|
||||||
|
rng.setEnd(bookmark.endContainer, bookmark.endOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Range.normalizeRange(rng);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
createBookmark,
|
||||||
|
resolveBookmark
|
||||||
|
};
|
|
@ -0,0 +1,285 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface DOMUtils {
|
||||||
|
isBlock: Function,
|
||||||
|
remove: Function,
|
||||||
|
$: Function,
|
||||||
|
getParent: Function,
|
||||||
|
getParents: Function,
|
||||||
|
getRoot: Function,
|
||||||
|
isEmpty: Function,
|
||||||
|
}
|
||||||
|
|
||||||
|
import { Element, HTMLLIElement, Node, Range as DomRange } from '@ephox/dom-globals';
|
||||||
|
import { Arr } from '@ephox/katamari';
|
||||||
|
import { Compare, Element as SugarElement } from '@ephox/sugar';
|
||||||
|
// import DOMUtils from 'tinymce/core/api/dom/DOMUtils';
|
||||||
|
import RangeUtils from 'tinymce/core/api/dom/RangeUtils';
|
||||||
|
import TreeWalker from 'tinymce/core/api/dom/TreeWalker';
|
||||||
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
|
import VK from 'tinymce/core/api/util/VK';
|
||||||
|
import { flattenListSelection, outdentListSelection } from '../actions/Indendation';
|
||||||
|
import * as ToggleList from '../actions/ToggleList';
|
||||||
|
import * as Bookmark from './Bookmark';
|
||||||
|
import * as NodeType from './NodeType';
|
||||||
|
import * as NormalizeLists from './NormalizeLists';
|
||||||
|
import * as Range from './Range';
|
||||||
|
import * as Selection from './Selection';
|
||||||
|
|
||||||
|
const findNextCaretContainer = function (editor: Editor, rng: DomRange, isForward: Boolean, root: Node): Node {
|
||||||
|
let node = rng.startContainer;
|
||||||
|
const offset = rng.startOffset;
|
||||||
|
|
||||||
|
if (NodeType.isTextNode(node) && (isForward ? offset < node.data.length : offset > 0)) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nonEmptyBlocks = editor.schema.getNonEmptyElements();
|
||||||
|
if (node.nodeType === 1) {
|
||||||
|
node = RangeUtils.getNode(node, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
const walker = new TreeWalker(node, root);
|
||||||
|
|
||||||
|
// Delete at <li>|<br></li> then jump over the bogus br
|
||||||
|
if (isForward) {
|
||||||
|
if (NodeType.isBogusBr(editor.dom, node)) {
|
||||||
|
walker.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((node = walker[isForward ? 'next' : 'prev2']())) {
|
||||||
|
if (node.nodeName === 'LI' && !node.hasChildNodes()) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nonEmptyBlocks[node.nodeName]) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NodeType.isTextNode(node) && node.data.length > 0) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasOnlyOneBlockChild = function (dom: DOMUtils, elm: Element): boolean {
|
||||||
|
const childNodes = elm.childNodes;
|
||||||
|
return childNodes.length === 1 && !NodeType.isListNode(childNodes[0]) && dom.isBlock(childNodes[0]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const unwrapSingleBlockChild = function (dom: DOMUtils, elm: Element) {
|
||||||
|
if (hasOnlyOneBlockChild(dom, elm)) {
|
||||||
|
dom.remove(elm.firstChild, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const moveChildren = function (dom: DOMUtils, fromElm: Element, toElm: Element) {
|
||||||
|
let node, targetElm;
|
||||||
|
|
||||||
|
targetElm = hasOnlyOneBlockChild(dom, toElm) ? toElm.firstChild : toElm;
|
||||||
|
unwrapSingleBlockChild(dom, fromElm);
|
||||||
|
|
||||||
|
if (!NodeType.isEmpty(dom, fromElm, true)) {
|
||||||
|
while ((node = fromElm.firstChild)) {
|
||||||
|
targetElm.appendChild(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeLiElements = function (dom: DOMUtils, fromElm: Element, toElm: Element) {
|
||||||
|
let node, listNode;
|
||||||
|
const ul = fromElm.parentNode;
|
||||||
|
|
||||||
|
if (!NodeType.isChildOfBody(dom, fromElm) || !NodeType.isChildOfBody(dom, toElm)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NodeType.isListNode(toElm.lastChild)) {
|
||||||
|
listNode = toElm.lastChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ul === toElm.lastChild) {
|
||||||
|
if (NodeType.isBr(ul.previousSibling)) {
|
||||||
|
dom.remove(ul.previousSibling);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node = toElm.lastChild;
|
||||||
|
if (node && NodeType.isBr(node) && fromElm.hasChildNodes()) {
|
||||||
|
dom.remove(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NodeType.isEmpty(dom, toElm, true)) {
|
||||||
|
dom.$(toElm).empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
moveChildren(dom, fromElm, toElm);
|
||||||
|
|
||||||
|
if (listNode) {
|
||||||
|
toElm.appendChild(listNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
const contains = Compare.contains(SugarElement.fromDom(toElm), SugarElement.fromDom(fromElm));
|
||||||
|
|
||||||
|
const nestedLists = contains ? dom.getParents(fromElm, NodeType.isListNode, toElm) : [];
|
||||||
|
|
||||||
|
dom.remove(fromElm);
|
||||||
|
|
||||||
|
Arr.each(nestedLists, (list) => {
|
||||||
|
if (NodeType.isEmpty(dom, list) && list !== dom.getRoot()) {
|
||||||
|
dom.remove(list);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeIntoEmptyLi = function (editor: Editor, fromLi: HTMLLIElement, toLi: HTMLLIElement) {
|
||||||
|
editor.dom.$(toLi).empty();
|
||||||
|
mergeLiElements(editor.dom, fromLi, toLi);
|
||||||
|
editor.selection.setCursorLocation(toLi);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeForward = function (editor: Editor, rng: DomRange, fromLi: HTMLLIElement, toLi: HTMLLIElement) {
|
||||||
|
const dom = editor.dom;
|
||||||
|
|
||||||
|
if (dom.isEmpty(toLi)) {
|
||||||
|
mergeIntoEmptyLi(editor, fromLi, toLi);
|
||||||
|
} else {
|
||||||
|
const bookmark = Bookmark.createBookmark(rng);
|
||||||
|
mergeLiElements(dom, fromLi, toLi);
|
||||||
|
editor.selection.setRng(Bookmark.resolveBookmark(bookmark));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const mergeBackward = function (editor: Editor, rng: DomRange, fromLi: HTMLLIElement, toLi: HTMLLIElement) {
|
||||||
|
const bookmark = Bookmark.createBookmark(rng);
|
||||||
|
mergeLiElements(editor.dom, fromLi, toLi);
|
||||||
|
const resolvedBookmark = Bookmark.resolveBookmark(bookmark);
|
||||||
|
editor.selection.setRng(resolvedBookmark);
|
||||||
|
};
|
||||||
|
|
||||||
|
const backspaceDeleteFromListToListCaret = function (editor: Editor, isForward: boolean) {
|
||||||
|
const dom = editor.dom, selection = editor.selection;
|
||||||
|
const selectionStartElm = selection.getStart();
|
||||||
|
const root = Selection.getClosestListRootElm(editor, selectionStartElm);
|
||||||
|
const li = dom.getParent(selection.getStart(), 'LI', root) as HTMLLIElement;
|
||||||
|
|
||||||
|
if (li) {
|
||||||
|
const ul = li.parentNode;
|
||||||
|
if (ul === editor.getBody() && NodeType.isEmpty(dom, ul)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rng = Range.normalizeRange(selection.getRng());
|
||||||
|
const otherLi = dom.getParent(findNextCaretContainer(editor, rng, isForward, root), 'LI', root) as HTMLLIElement;
|
||||||
|
|
||||||
|
if (otherLi && otherLi !== li) {
|
||||||
|
editor.undoManager.transact(() => {
|
||||||
|
if (isForward) {
|
||||||
|
mergeForward(editor, rng, otherLi, li);
|
||||||
|
} else {
|
||||||
|
if (NodeType.isFirstChild(li)) {
|
||||||
|
outdentListSelection(editor);
|
||||||
|
} else {
|
||||||
|
mergeBackward(editor, rng, li, otherLi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else if (!otherLi) {
|
||||||
|
if (!isForward && rng.startOffset === 0 && rng.endOffset === 0) {
|
||||||
|
editor.undoManager.transact(() => {
|
||||||
|
flattenListSelection(editor);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeBlock = function (dom: DOMUtils, block: Element, root: Node) {
|
||||||
|
const parentBlock = dom.getParent(block.parentNode, dom.isBlock, root);
|
||||||
|
|
||||||
|
dom.remove(block);
|
||||||
|
if (parentBlock && dom.isEmpty(parentBlock)) {
|
||||||
|
dom.remove(parentBlock);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const backspaceDeleteIntoListCaret = function (editor: Editor, isForward: boolean) {
|
||||||
|
const dom = editor.dom;
|
||||||
|
const selectionStartElm = editor.selection.getStart();
|
||||||
|
const root = Selection.getClosestListRootElm(editor, selectionStartElm);
|
||||||
|
const block = dom.getParent(selectionStartElm, dom.isBlock, root);
|
||||||
|
|
||||||
|
if (block && dom.isEmpty(block)) {
|
||||||
|
const rng = Range.normalizeRange(editor.selection.getRng());
|
||||||
|
const otherLi = dom.getParent(findNextCaretContainer(editor, rng, isForward, root), 'LI', root);
|
||||||
|
|
||||||
|
if (otherLi) {
|
||||||
|
editor.undoManager.transact(function () {
|
||||||
|
removeBlock(dom, block, root);
|
||||||
|
ToggleList.mergeWithAdjacentLists(dom, otherLi.parentNode);
|
||||||
|
editor.selection.select(otherLi, true);
|
||||||
|
editor.selection.collapse(isForward);
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const backspaceDeleteCaret = function (editor: Editor, isForward: boolean): boolean {
|
||||||
|
return backspaceDeleteFromListToListCaret(editor, isForward) || backspaceDeleteIntoListCaret(editor, isForward);
|
||||||
|
};
|
||||||
|
|
||||||
|
const backspaceDeleteRange = function (editor: Editor): boolean {
|
||||||
|
const selectionStartElm = editor.selection.getStart();
|
||||||
|
const root = Selection.getClosestListRootElm(editor, selectionStartElm);
|
||||||
|
const startListParent = editor.dom.getParent(selectionStartElm, 'LI,DT,DD', root);
|
||||||
|
|
||||||
|
if (startListParent || Selection.getSelectedListItems(editor).length > 0) {
|
||||||
|
editor.undoManager.transact(function () {
|
||||||
|
editor.execCommand('Delete');
|
||||||
|
NormalizeLists.normalizeLists(editor.dom, editor.getBody());
|
||||||
|
});
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const backspaceDelete = function (editor: Editor, isForward: boolean): boolean {
|
||||||
|
return editor.selection.isCollapsed() ? backspaceDeleteCaret(editor, isForward) : backspaceDeleteRange(editor);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setup = function (editor: Editor) {
|
||||||
|
editor.on('keydown', function (e) {
|
||||||
|
if (e.keyCode === VK.BACKSPACE) {
|
||||||
|
if (backspaceDelete(editor, false)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
} else if (e.keyCode === VK.DELETE) {
|
||||||
|
if (backspaceDelete(editor, true)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
setup,
|
||||||
|
backspaceDelete
|
||||||
|
};
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
|
import { Compare, Replication, Element, Traverse } from '@ephox/sugar';
|
||||||
|
import * as SplitList from './SplitList';
|
||||||
|
import { Indentation } from '../listModel/Indentation';
|
||||||
|
import { Arr } from '@ephox/katamari';
|
||||||
|
|
||||||
|
const outdentDlItem = (editor: Editor, item: Element): void => {
|
||||||
|
if (Compare.is(item, 'dd')) {
|
||||||
|
Replication.mutate(item, 'dt');
|
||||||
|
} else if (Compare.is(item, 'dt')) {
|
||||||
|
Traverse.parent(item).each((dl) => SplitList.splitList(editor, dl.dom(), item.dom()));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const indentDlItem = (item: Element): void => {
|
||||||
|
if (Compare.is(item, 'dt')) {
|
||||||
|
Replication.mutate(item, 'dd');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const dlIndentation = (editor: Editor, indentation: Indentation, dlItems: Element[]) => {
|
||||||
|
if (indentation === Indentation.Indent) {
|
||||||
|
Arr.each(dlItems, indentDlItem);
|
||||||
|
} else {
|
||||||
|
Arr.each(dlItems, (item) => outdentDlItem(editor, item));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
dlIndentation
|
||||||
|
};
|
|
@ -0,0 +1,38 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import VK from 'tinymce/core/api/util/VK';
|
||||||
|
import * as Settings from '../api/Settings';
|
||||||
|
import * as Delete from './Delete';
|
||||||
|
import { outdentListSelection, indentListSelection } from '../actions/Indendation';
|
||||||
|
|
||||||
|
const setupTabKey = function (editor) {
|
||||||
|
editor.on('keydown', function (e) {
|
||||||
|
// Check for tab but not ctrl/cmd+tab since it switches browser tabs
|
||||||
|
if (e.keyCode !== VK.TAB || VK.metaKeyPressed(e)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.undoManager.transact(() => {
|
||||||
|
if (e.shiftKey ? outdentListSelection(editor) : indentListSelection(editor)) {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setup = function (editor) {
|
||||||
|
if (Settings.shouldIndentOnTab(editor)) {
|
||||||
|
setupTabKey(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Delete.setup(editor);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
setup
|
||||||
|
};
|
|
@ -0,0 +1,15 @@
|
||||||
|
export const enum ListAction {
|
||||||
|
ToggleUlList = 'ToggleUlList',
|
||||||
|
ToggleOlList = 'ToggleOlList',
|
||||||
|
ToggleDLList = 'ToggleDLList',
|
||||||
|
IndentList = 'IndentList',
|
||||||
|
OutdentList = 'OutdentList'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const listToggleActionFromListName = (listName: 'UL' | 'OL' | 'DL'): ListAction => {
|
||||||
|
switch (listName) {
|
||||||
|
case 'UL': return ListAction.ToggleUlList;
|
||||||
|
case 'OL': return ListAction.ToggleOlList;
|
||||||
|
case 'DL': return ListAction.ToggleDLList;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,95 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Node, Text } from '@ephox/dom-globals';
|
||||||
|
|
||||||
|
const isTextNode = function (node: Node): node is Text {
|
||||||
|
return node && node.nodeType === 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isListNode = function (node: Node) {
|
||||||
|
return node && (/^(OL|UL|DL)$/).test(node.nodeName);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isOlUlNode = function (node: Node) {
|
||||||
|
return node && (/^(OL|UL)$/).test(node.nodeName);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isListItemNode = function (node: Node) {
|
||||||
|
return node && /^(LI|DT|DD)$/.test(node.nodeName);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isDlItemNode = function (node: Node) {
|
||||||
|
return node && /^(DT|DD)$/.test(node.nodeName);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isTableCellNode = function (node: Node) {
|
||||||
|
return node && /^(TH|TD)$/.test(node.nodeName);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isBr = function (node: Node) {
|
||||||
|
return node && node.nodeName === 'BR';
|
||||||
|
};
|
||||||
|
|
||||||
|
const isFirstChild = function (node: Node) {
|
||||||
|
return node.parentNode.firstChild === node;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isLastChild = function (node: Node) {
|
||||||
|
return node.parentNode.lastChild === node;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isTextBlock = function (editor, node: Node) {
|
||||||
|
return node && !!editor.schema.getTextBlockElements()[node.nodeName];
|
||||||
|
};
|
||||||
|
|
||||||
|
const isBlock = function (node: Node, blockElements) {
|
||||||
|
return node && node.nodeName in blockElements;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isBogusBr = function (dom, node: Node) {
|
||||||
|
if (!isBr(node)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dom.isBlock(node.nextSibling) && !isBr(node.previousSibling)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isEmpty = function (dom, elm, keepBookmarks?) {
|
||||||
|
const empty = dom.isEmpty(elm);
|
||||||
|
|
||||||
|
if (keepBookmarks && dom.select('span[data-mce-type=bookmark]', elm).length > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return empty;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isChildOfBody = function (dom, elm) {
|
||||||
|
return dom.isChildOf(elm, dom.getRoot());
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
isTextNode,
|
||||||
|
isListNode,
|
||||||
|
isOlUlNode,
|
||||||
|
isDlItemNode,
|
||||||
|
isListItemNode,
|
||||||
|
isTableCellNode,
|
||||||
|
isBr,
|
||||||
|
isFirstChild,
|
||||||
|
isLastChild,
|
||||||
|
isTextBlock,
|
||||||
|
isBlock,
|
||||||
|
isBogusBr,
|
||||||
|
isEmpty,
|
||||||
|
isChildOfBody
|
||||||
|
};
|
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import DOMUtils from 'tinymce/core/api/dom/DOMUtils';
|
||||||
|
import Tools from 'tinymce/core/api/util/Tools';
|
||||||
|
import * as NodeType from './NodeType';
|
||||||
|
|
||||||
|
const DOM = DOMUtils.DOM;
|
||||||
|
|
||||||
|
const normalizeList = function (dom, ul) {
|
||||||
|
let sibling;
|
||||||
|
const parentNode = ul.parentNode;
|
||||||
|
|
||||||
|
// Move UL/OL to previous LI if it's the only child of a LI
|
||||||
|
if (parentNode.nodeName === 'LI' && parentNode.firstChild === ul) {
|
||||||
|
sibling = parentNode.previousSibling;
|
||||||
|
if (sibling && sibling.nodeName === 'LI') {
|
||||||
|
sibling.appendChild(ul);
|
||||||
|
|
||||||
|
if (NodeType.isEmpty(dom, parentNode)) {
|
||||||
|
DOM.remove(parentNode);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DOM.setStyle(parentNode, 'listStyleType', 'none');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append OL/UL to previous LI if it's in a parent OL/UL i.e. old HTML4
|
||||||
|
if (NodeType.isListNode(parentNode)) {
|
||||||
|
sibling = parentNode.previousSibling;
|
||||||
|
if (sibling && sibling.nodeName === 'LI') {
|
||||||
|
sibling.appendChild(ul);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeLists = function (dom, element) {
|
||||||
|
Tools.each(Tools.grep(dom.select('ol,ul', element)), function (ul) {
|
||||||
|
normalizeList(dom, ul);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
normalizeList,
|
||||||
|
normalizeLists
|
||||||
|
};
|
|
@ -0,0 +1,58 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import RangeUtils from 'tinymce/core/api/dom/RangeUtils';
|
||||||
|
import * as NodeType from './NodeType';
|
||||||
|
import { Range, Node } from '@ephox/dom-globals';
|
||||||
|
|
||||||
|
interface Point {
|
||||||
|
container: Node;
|
||||||
|
offset: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const getNormalizedPoint = (container: Node, offset: number): Point => {
|
||||||
|
if (NodeType.isTextNode(container)) {
|
||||||
|
return { container, offset };
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = RangeUtils.getNode(container, offset);
|
||||||
|
if (NodeType.isTextNode(node)) {
|
||||||
|
return {
|
||||||
|
container: node,
|
||||||
|
offset: offset >= container.childNodes.length ? node.data.length : 0
|
||||||
|
};
|
||||||
|
} else if (node.previousSibling && NodeType.isTextNode(node.previousSibling)) {
|
||||||
|
return {
|
||||||
|
container: node.previousSibling,
|
||||||
|
offset: node.previousSibling.data.length
|
||||||
|
};
|
||||||
|
} else if (node.nextSibling && NodeType.isTextNode(node.nextSibling)) {
|
||||||
|
return {
|
||||||
|
container: node.nextSibling,
|
||||||
|
offset: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { container, offset };
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeRange = (rng: Range): Range => {
|
||||||
|
const outRng = rng.cloneRange();
|
||||||
|
|
||||||
|
const rangeStart = getNormalizedPoint(rng.startContainer, rng.startOffset);
|
||||||
|
outRng.setStart(rangeStart.container, rangeStart.offset);
|
||||||
|
|
||||||
|
const rangeEnd = getNormalizedPoint(rng.endContainer, rng.endOffset);
|
||||||
|
outRng.setEnd(rangeEnd.container, rangeEnd.offset);
|
||||||
|
|
||||||
|
return outRng;
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
getNormalizedPoint,
|
||||||
|
normalizeRange
|
||||||
|
};
|
|
@ -0,0 +1,108 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Node } from '@ephox/dom-globals';
|
||||||
|
import { Arr, Option } from '@ephox/katamari';
|
||||||
|
import { HTMLElement } from '@ephox/sand';
|
||||||
|
import DomQuery from 'tinymce/core/api/dom/DomQuery';
|
||||||
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
|
import Tools from 'tinymce/core/api/util/Tools';
|
||||||
|
import * as NodeType from './NodeType';
|
||||||
|
|
||||||
|
const getParentList = function (editor) {
|
||||||
|
const selectionStart = editor.selection.getStart(true);
|
||||||
|
|
||||||
|
return editor.dom.getParent(selectionStart, 'OL,UL,DL', getClosestListRootElm(editor, selectionStart));
|
||||||
|
};
|
||||||
|
|
||||||
|
const isParentListSelected = function (parentList, selectedBlocks) {
|
||||||
|
return parentList && selectedBlocks.length === 1 && selectedBlocks[0] === parentList;
|
||||||
|
};
|
||||||
|
|
||||||
|
const findSubLists = function (parentList) {
|
||||||
|
return Tools.grep(parentList.querySelectorAll('ol,ul,dl'), function (elm: Node) {
|
||||||
|
return NodeType.isListNode(elm);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectedSubLists = function (editor) {
|
||||||
|
const parentList = getParentList(editor);
|
||||||
|
const selectedBlocks = editor.selection.getSelectedBlocks();
|
||||||
|
|
||||||
|
if (isParentListSelected(parentList, selectedBlocks)) {
|
||||||
|
return findSubLists(parentList);
|
||||||
|
} else {
|
||||||
|
return Tools.grep(selectedBlocks, function (elm: Node) {
|
||||||
|
return NodeType.isListNode(elm) && parentList !== elm;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const findParentListItemsNodes = function (editor, elms) {
|
||||||
|
const listItemsElms = Tools.map(elms, function (elm) {
|
||||||
|
const parentLi = editor.dom.getParent(elm, 'li,dd,dt', getClosestListRootElm(editor, elm));
|
||||||
|
|
||||||
|
return parentLi ? parentLi : elm;
|
||||||
|
});
|
||||||
|
|
||||||
|
return DomQuery.unique(listItemsElms);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectedListItems = function (editor) {
|
||||||
|
const selectedBlocks = editor.selection.getSelectedBlocks();
|
||||||
|
return Tools.grep(findParentListItemsNodes(editor, selectedBlocks), function (block) {
|
||||||
|
return NodeType.isListItemNode(block);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectedDlItems = (editor: Editor): Node[] => {
|
||||||
|
return Arr.filter(getSelectedListItems(editor), NodeType.isDlItemNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getClosestListRootElm = function (editor, elm) {
|
||||||
|
const parentTableCell = editor.dom.getParents(elm, 'TD,TH');
|
||||||
|
const root = parentTableCell.length > 0 ? parentTableCell[0] : editor.getBody();
|
||||||
|
|
||||||
|
return root;
|
||||||
|
};
|
||||||
|
|
||||||
|
const findLastParentListNode = (editor: Editor, elm: Node): Option<Node> => {
|
||||||
|
const parentLists = editor.dom.getParents(elm, 'ol,ul', getClosestListRootElm(editor, elm));
|
||||||
|
return Arr.last(parentLists);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectedLists = (editor: Editor): Node[] => {
|
||||||
|
const firstList = findLastParentListNode(editor, editor.selection.getStart());
|
||||||
|
const subsequentLists = Arr.filter(editor.selection.getSelectedBlocks(), NodeType.isOlUlNode);
|
||||||
|
|
||||||
|
return firstList.toArray().concat(subsequentLists);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSelectedListRoots = (editor: Editor): Node[] => {
|
||||||
|
const selectedLists = getSelectedLists(editor);
|
||||||
|
return getUniqueListRoots(editor, selectedLists);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getUniqueListRoots = (editor: Editor, lists: Node[]): Node[] => {
|
||||||
|
const listRoots = Arr.map(lists, (list) => findLastParentListNode(editor, list).getOr(list));
|
||||||
|
return DomQuery.unique(listRoots);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isList = (editor: Editor): boolean => {
|
||||||
|
const list = getParentList(editor);
|
||||||
|
return HTMLElement.isPrototypeOf(list);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
isList,
|
||||||
|
getParentList,
|
||||||
|
getSelectedSubLists,
|
||||||
|
getSelectedListItems,
|
||||||
|
getClosestListRootElm,
|
||||||
|
getSelectedDlItems,
|
||||||
|
getSelectedListRoots
|
||||||
|
};
|
|
@ -0,0 +1,59 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import DOMUtils from 'tinymce/core/api/dom/DOMUtils';
|
||||||
|
import * as NodeType from './NodeType';
|
||||||
|
import { createTextBlock } from './TextBlock';
|
||||||
|
import Tools from 'tinymce/core/api/util/Tools';
|
||||||
|
|
||||||
|
const DOM = DOMUtils.DOM;
|
||||||
|
|
||||||
|
const splitList = function (editor, ul, li) {
|
||||||
|
let tmpRng, fragment, bookmarks, node, newBlock;
|
||||||
|
|
||||||
|
const removeAndKeepBookmarks = function (targetNode) {
|
||||||
|
Tools.each(bookmarks, function (node) {
|
||||||
|
targetNode.parentNode.insertBefore(node, li.parentNode);
|
||||||
|
});
|
||||||
|
|
||||||
|
DOM.remove(targetNode);
|
||||||
|
};
|
||||||
|
|
||||||
|
bookmarks = DOM.select('span[data-mce-type="bookmark"]', ul);
|
||||||
|
newBlock = createTextBlock(editor, li);
|
||||||
|
tmpRng = DOM.createRng();
|
||||||
|
tmpRng.setStartAfter(li);
|
||||||
|
tmpRng.setEndAfter(ul);
|
||||||
|
fragment = tmpRng.extractContents();
|
||||||
|
|
||||||
|
for (node = fragment.firstChild; node; node = node.firstChild) {
|
||||||
|
if (node.nodeName === 'LI' && editor.dom.isEmpty(node)) {
|
||||||
|
DOM.remove(node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!editor.dom.isEmpty(fragment)) {
|
||||||
|
DOM.insertAfter(fragment, ul);
|
||||||
|
}
|
||||||
|
|
||||||
|
DOM.insertAfter(newBlock, ul);
|
||||||
|
|
||||||
|
if (NodeType.isEmpty(editor.dom, li.parentNode)) {
|
||||||
|
removeAndKeepBookmarks(li.parentNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
DOM.remove(li);
|
||||||
|
|
||||||
|
if (NodeType.isEmpty(editor.dom, ul)) {
|
||||||
|
DOM.remove(ul);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
splitList
|
||||||
|
};
|
|
@ -0,0 +1,72 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as NodeType from './NodeType';
|
||||||
|
import { DocumentFragment, Node } from '@ephox/dom-globals';
|
||||||
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
|
import * as Settings from '../api/Settings';
|
||||||
|
|
||||||
|
const createTextBlock = (editor: Editor, contentNode: Node): DocumentFragment => {
|
||||||
|
const dom = editor.dom;
|
||||||
|
const blockElements = editor.schema.getBlockElements();
|
||||||
|
const fragment = dom.createFragment();
|
||||||
|
const blockName = Settings.getForcedRootBlock(editor);
|
||||||
|
let node, textBlock, hasContentNode;
|
||||||
|
|
||||||
|
if (blockName) {
|
||||||
|
textBlock = dom.create(blockName);
|
||||||
|
|
||||||
|
if (textBlock.tagName === blockName.toUpperCase()) {
|
||||||
|
dom.setAttribs(textBlock, Settings.getForcedRootBlockAttrs(editor));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NodeType.isBlock(contentNode.firstChild, blockElements)) {
|
||||||
|
fragment.appendChild(textBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentNode) {
|
||||||
|
while ((node = contentNode.firstChild)) {
|
||||||
|
const nodeName = node.nodeName;
|
||||||
|
|
||||||
|
if (!hasContentNode && (nodeName !== 'SPAN' || node.getAttribute('data-mce-type') !== 'bookmark')) {
|
||||||
|
hasContentNode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NodeType.isBlock(node, blockElements)) {
|
||||||
|
fragment.appendChild(node);
|
||||||
|
textBlock = null;
|
||||||
|
} else {
|
||||||
|
if (blockName) {
|
||||||
|
if (!textBlock) {
|
||||||
|
textBlock = dom.create(blockName);
|
||||||
|
fragment.appendChild(textBlock);
|
||||||
|
}
|
||||||
|
|
||||||
|
textBlock.appendChild(node);
|
||||||
|
} else {
|
||||||
|
fragment.appendChild(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!blockName) {
|
||||||
|
fragment.appendChild(dom.create('br'));
|
||||||
|
} else {
|
||||||
|
// BR is needed in empty blocks
|
||||||
|
if (!hasContentNode) {
|
||||||
|
textBlock.appendChild(dom.create('br', { 'data-mce-bogus': '1' }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragment;
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
createTextBlock
|
||||||
|
};
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { HTMLElement } from '@ephox/dom-globals';
|
||||||
|
|
||||||
|
export const isCustomList = (list: HTMLElement) => /\btox\-/.test(list.className);
|
|
@ -0,0 +1,109 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Document } from '@ephox/dom-globals';
|
||||||
|
import { Arr, Option, Options } from '@ephox/katamari';
|
||||||
|
import { Attr, Css, Element, Insert, InsertAll, Node, Replication } from '@ephox/sugar';
|
||||||
|
import { Entry } from './Entry';
|
||||||
|
import { ListType } from './Util';
|
||||||
|
|
||||||
|
interface Segment {
|
||||||
|
list: Element;
|
||||||
|
item: Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
const joinSegment = (parent: Segment, child: Segment): void => {
|
||||||
|
Insert.append(parent.item, child.list);
|
||||||
|
};
|
||||||
|
|
||||||
|
const joinSegments = (segments: Segment[]): void => {
|
||||||
|
for (let i = 1; i < segments.length; i++) {
|
||||||
|
joinSegment(segments[i - 1], segments[i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const appendSegments = (head: Segment[], tail: Segment[]): void => {
|
||||||
|
Options.lift2(Arr.last(head), Arr.head(tail), joinSegment);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createSegment = (scope: Document, listType: ListType): Segment => {
|
||||||
|
const segment: Segment = {
|
||||||
|
list: Element.fromTag(listType, scope),
|
||||||
|
item: Element.fromTag('li', scope)
|
||||||
|
};
|
||||||
|
Insert.append(segment.list, segment.item);
|
||||||
|
return segment;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createSegments = (scope: Document, entry: Entry, size: number): Segment[] => {
|
||||||
|
const segments: Segment[] = [];
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
segments.push(createSegment(scope, entry.listType));
|
||||||
|
}
|
||||||
|
return segments;
|
||||||
|
};
|
||||||
|
|
||||||
|
const populateSegments = (segments: Segment[], entry: Entry): void => {
|
||||||
|
for (let i = 0; i < segments.length - 1; i++) {
|
||||||
|
Css.set(segments[i].item, 'list-style-type', 'none');
|
||||||
|
}
|
||||||
|
Arr.last(segments).each((segment) => {
|
||||||
|
Attr.setAll(segment.list, entry.listAttributes);
|
||||||
|
Attr.setAll(segment.item, entry.itemAttributes);
|
||||||
|
InsertAll.append(segment.item, entry.content);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeSegment = (segment: Segment, entry: Entry): void => {
|
||||||
|
if (Node.name(segment.list) !== entry.listType) {
|
||||||
|
segment.list = Replication.mutate(segment.list, entry.listType);
|
||||||
|
}
|
||||||
|
Attr.setAll(segment.list, entry.listAttributes);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createItem = (scope: Document, attr: Record<string, any>, content: Element[]): Element => {
|
||||||
|
const item = Element.fromTag('li', scope);
|
||||||
|
Attr.setAll(item, attr);
|
||||||
|
InsertAll.append(item, content);
|
||||||
|
return item;
|
||||||
|
};
|
||||||
|
|
||||||
|
const appendItem = (segment: Segment, item: Element): void => {
|
||||||
|
Insert.append(segment.list, item);
|
||||||
|
segment.item = item;
|
||||||
|
};
|
||||||
|
|
||||||
|
const writeShallow = (scope: Document, cast: Segment[], entry: Entry): Segment[] => {
|
||||||
|
const newCast = cast.slice(0, entry.depth);
|
||||||
|
|
||||||
|
Arr.last(newCast).each((segment) => {
|
||||||
|
const item = createItem(scope, entry.itemAttributes, entry.content);
|
||||||
|
appendItem(segment, item);
|
||||||
|
normalizeSegment(segment, entry);
|
||||||
|
});
|
||||||
|
|
||||||
|
return newCast;
|
||||||
|
};
|
||||||
|
|
||||||
|
const writeDeep = (scope: Document, cast: Segment[], entry: Entry): Segment[] => {
|
||||||
|
const segments = createSegments(scope, entry, entry.depth - cast.length);
|
||||||
|
joinSegments(segments);
|
||||||
|
populateSegments(segments, entry);
|
||||||
|
appendSegments(cast, segments);
|
||||||
|
|
||||||
|
return cast.concat(segments);
|
||||||
|
};
|
||||||
|
|
||||||
|
const composeList = (scope: Document, entries: Entry[]): Option<Element> => {
|
||||||
|
const cast: Segment[] = Arr.foldl(entries, (cast, entry) => {
|
||||||
|
return entry.depth > cast.length ? writeDeep(scope, cast, entry) : writeShallow(scope, cast, entry);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return Arr.head(cast).map((segment) => segment.list);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { composeList };
|
|
@ -0,0 +1,67 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Element, Traverse, Replication, Attr, Node } from '@ephox/sugar';
|
||||||
|
import { Arr, Option } from '@ephox/katamari';
|
||||||
|
import { hasLastChildList, ListType } from './Util';
|
||||||
|
|
||||||
|
/*
|
||||||
|
General workflow: Parse lists to entries -> Manipulate entries -> Compose entries to lists
|
||||||
|
|
||||||
|
0-------1---2--------->Depth
|
||||||
|
<ol> |
|
||||||
|
<li>a</li> | Entry { depth: 1, content: [a], listType: ListType.OL, ... }
|
||||||
|
<li>b | Entry { depth: 1, content: [b], listType: ListType.OL, ... }
|
||||||
|
<ul> |
|
||||||
|
<li>c</li> | Entry { depth: 2, content: [c], listType: ListType.UL, ... }
|
||||||
|
</ul> |
|
||||||
|
</li> |
|
||||||
|
</ol> |
|
||||||
|
0-------1---2--------->Depth
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface Entry {
|
||||||
|
depth: number;
|
||||||
|
content: Element[];
|
||||||
|
isSelected: boolean;
|
||||||
|
listType: ListType;
|
||||||
|
listAttributes: Record<string, any>;
|
||||||
|
itemAttributes: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isIndented = (entry: Entry) => {
|
||||||
|
return entry.depth > 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isSelected = (entry: Entry) => {
|
||||||
|
return entry.isSelected;
|
||||||
|
};
|
||||||
|
|
||||||
|
const cloneItemContent = (li: Element): Element[] => {
|
||||||
|
const children = Traverse.children(li);
|
||||||
|
const content = hasLastChildList(li) ? children.slice(0, -1) : children;
|
||||||
|
return Arr.map(content, Replication.deep);
|
||||||
|
};
|
||||||
|
|
||||||
|
const createEntry = (li: Element, depth: number, isSelected: boolean): Option<Entry> => {
|
||||||
|
return Traverse.parent(li).filter(Node.isElement).map((list) => {
|
||||||
|
return {
|
||||||
|
depth,
|
||||||
|
isSelected,
|
||||||
|
content: cloneItemContent(li),
|
||||||
|
itemAttributes: Attr.clone(li),
|
||||||
|
listAttributes: Attr.clone(list),
|
||||||
|
listType: Node.name(list) as ListType
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
createEntry,
|
||||||
|
isIndented,
|
||||||
|
isSelected
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Entry } from './Entry';
|
||||||
|
|
||||||
|
export const enum Indentation {
|
||||||
|
Indent = 'Indent',
|
||||||
|
Outdent = 'Outdent',
|
||||||
|
Flatten = 'Flatten'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const indentEntry = (indentation: Indentation, entry: Entry): void => {
|
||||||
|
switch (indentation) {
|
||||||
|
case Indentation.Indent:
|
||||||
|
entry.depth ++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Indentation.Outdent:
|
||||||
|
entry.depth --;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Indentation.Flatten:
|
||||||
|
entry.depth = 0;
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,44 @@
|
||||||
|
export function isCheckboxListItem(element) {
|
||||||
|
return element.classList && element.classList.contains('joplin-checklist');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findContainerListTypeFromEvent(event) {
|
||||||
|
if (isCheckboxListItem(event.element)) return 'joplinChecklist';
|
||||||
|
|
||||||
|
for (const parent of event.parents) {
|
||||||
|
if (isCheckboxListItem(parent)) return 'joplinChecklist';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'regular';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function findContainerListTypeFromElement(element) {
|
||||||
|
while (element) {
|
||||||
|
if (element.nodeName === 'UL' || element.nodName === 'OL') {
|
||||||
|
return isCheckboxListItem(element) ? 'joplinChecklist' : 'regular';
|
||||||
|
}
|
||||||
|
element = element.parentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'regular';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addJoplinChecklistCommands(editor, ToggleList) {
|
||||||
|
editor.addCommand('ToggleJoplinChecklistItem', function (ui, detail) {
|
||||||
|
const element = detail.element;
|
||||||
|
if (element.nodeName !== 'LI') return;
|
||||||
|
const listType = findContainerListTypeFromElement(element);
|
||||||
|
if (listType === 'joplinChecklist') {
|
||||||
|
if (!element.classList || !element.classList.contains('checked')) {
|
||||||
|
element.classList.add('checked');
|
||||||
|
} else {
|
||||||
|
element.classList.remove('checked');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.addCommand('InsertJoplinChecklist', function (ui, detail) {
|
||||||
|
detail = Object.assign({}, detail, { listType: 'joplinChecklist' });
|
||||||
|
ToggleList.toggleList(editor, 'UL', detail);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Arr, Fun, Option, Options } from '@ephox/katamari';
|
||||||
|
import { Element, Fragment, InsertAll, Remove } from '@ephox/sugar';
|
||||||
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
|
import { fireListEvent } from '../api/Events';
|
||||||
|
import { ListAction } from '../core/ListAction';
|
||||||
|
import * as Selection from '../core/Selection';
|
||||||
|
import { createTextBlock } from '../core/TextBlock';
|
||||||
|
import { composeList } from './ComposeList';
|
||||||
|
import { Entry, isIndented, isSelected } from './Entry';
|
||||||
|
import { Indentation, indentEntry } from './Indentation';
|
||||||
|
import { normalizeEntries } from './NormalizeEntries';
|
||||||
|
import { EntrySet, ItemSelection, parseLists } from './ParseLists';
|
||||||
|
import { hasFirstChildList } from './Util';
|
||||||
|
|
||||||
|
const outdentedComposer = (editor: Editor, entries: Entry[]): Element[] => {
|
||||||
|
return Arr.map(entries, (entry) => {
|
||||||
|
const content = Fragment.fromElements(entry.content);
|
||||||
|
return Element.fromDom(createTextBlock(editor, content.dom()));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const indentedComposer = (editor: Editor, entries: Entry[]): Element[] => {
|
||||||
|
normalizeEntries(entries);
|
||||||
|
return composeList(editor.contentDocument, entries).toArray();
|
||||||
|
};
|
||||||
|
|
||||||
|
const composeEntries = (editor, entries: Entry[]): Element[] => {
|
||||||
|
return Arr.bind(Arr.groupBy(entries, isIndented), (entries) => {
|
||||||
|
const groupIsIndented = Arr.head(entries).map(isIndented).getOr(false);
|
||||||
|
return groupIsIndented ? indentedComposer(editor, entries) : outdentedComposer(editor, entries);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const indentSelectedEntries = (entries: Entry[], indentation: Indentation): void => {
|
||||||
|
Arr.each(Arr.filter(entries, isSelected), (entry) => indentEntry(indentation, entry));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getItemSelection = (editor: Editor): Option<ItemSelection> => {
|
||||||
|
const selectedListItems = Arr.map(Selection.getSelectedListItems(editor), Element.fromDom);
|
||||||
|
|
||||||
|
return Options.lift2(
|
||||||
|
Arr.find(selectedListItems, Fun.not(hasFirstChildList)),
|
||||||
|
Arr.find(Arr.reverse(selectedListItems), Fun.not(hasFirstChildList)),
|
||||||
|
(start, end) => ({ start, end }));
|
||||||
|
};
|
||||||
|
|
||||||
|
const listIndentation = (editor: Editor, lists: Element[], indentation: Indentation) => {
|
||||||
|
const entrySets: EntrySet[] = parseLists(lists, getItemSelection(editor));
|
||||||
|
|
||||||
|
Arr.each(entrySets, (entrySet) => {
|
||||||
|
indentSelectedEntries(entrySet.entries, indentation);
|
||||||
|
const composedLists = composeEntries(editor, entrySet.entries);
|
||||||
|
Arr.each(composedLists, (composedList) => {
|
||||||
|
fireListEvent(editor, indentation === Indentation.Indent ? ListAction.IndentList : ListAction.OutdentList, composedList.dom());
|
||||||
|
});
|
||||||
|
InsertAll.before(entrySet.sourceList, composedLists);
|
||||||
|
Remove.remove(entrySet.sourceList);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export { listIndentation };
|
|
@ -0,0 +1,40 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Arr, Option } from '@ephox/katamari';
|
||||||
|
import { Entry } from './Entry';
|
||||||
|
|
||||||
|
const cloneListProperties = (target: Entry, source: Entry): void => {
|
||||||
|
target.listType = source.listType;
|
||||||
|
target.listAttributes = { ...source.listAttributes };
|
||||||
|
};
|
||||||
|
|
||||||
|
// Closest entry above in the same list
|
||||||
|
const previousSiblingEntry = (entries: Entry[], start: number): Option<Entry> => {
|
||||||
|
const depth = entries[start].depth;
|
||||||
|
for (let i = start - 1; i >= 0; i--) {
|
||||||
|
if (entries[i].depth === depth) {
|
||||||
|
return Option.some(entries[i]);
|
||||||
|
}
|
||||||
|
if (entries[i].depth < depth) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Option.none();
|
||||||
|
};
|
||||||
|
|
||||||
|
const normalizeEntries = (entries: Entry[]): void => {
|
||||||
|
Arr.each(entries, (entry, i) => {
|
||||||
|
previousSiblingEntry(entries, i).each((matchingEntry) => {
|
||||||
|
cloneListProperties(entry, matchingEntry);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
normalizeEntries
|
||||||
|
};
|
|
@ -0,0 +1,71 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Arr, Cell, Option } from '@ephox/katamari';
|
||||||
|
import { Compare, Element, Traverse } from '@ephox/sugar';
|
||||||
|
import { createEntry, Entry } from './Entry';
|
||||||
|
import { isList } from './Util';
|
||||||
|
|
||||||
|
type Parser = (depth: number, itemSelection: Option<ItemSelection>, selectionState: Cell<boolean>, element: Element) => Entry[];
|
||||||
|
|
||||||
|
export interface ItemSelection {
|
||||||
|
start: Element;
|
||||||
|
end: Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EntrySet {
|
||||||
|
entries: Entry[];
|
||||||
|
sourceList: Element;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parseItem: Parser = (depth: number, itemSelection: Option<ItemSelection>, selectionState: Cell<boolean>, item: Element): Entry[] => {
|
||||||
|
return Traverse.firstChild(item).filter(isList).fold(() => {
|
||||||
|
|
||||||
|
// Update selectionState (start)
|
||||||
|
itemSelection.each((selection) => {
|
||||||
|
if (Compare.eq(selection.start, item)) {
|
||||||
|
selectionState.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentItemEntry = createEntry(item, depth, selectionState.get());
|
||||||
|
|
||||||
|
// Update selectionState (end)
|
||||||
|
itemSelection.each((selection) => {
|
||||||
|
if (Compare.eq(selection.end, item)) {
|
||||||
|
selectionState.set(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const childListEntries: Entry[] = Traverse.lastChild(item)
|
||||||
|
.filter(isList)
|
||||||
|
.map((list) => parseList(depth, itemSelection, selectionState, list))
|
||||||
|
.getOr([]);
|
||||||
|
|
||||||
|
return currentItemEntry.toArray().concat(childListEntries);
|
||||||
|
}, (list) => parseList(depth, itemSelection, selectionState, list));
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseList: Parser = (depth: number, itemSelection: Option<ItemSelection>, selectionState: Cell<boolean>, list: Element): Entry[] => {
|
||||||
|
return Arr.bind(Traverse.children(list), (element) => {
|
||||||
|
const parser = isList(element) ? parseList : parseItem;
|
||||||
|
const newDepth = depth + 1;
|
||||||
|
return parser(newDepth, itemSelection, selectionState, element);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const parseLists = (lists: Element[], itemSelection: Option<ItemSelection>): EntrySet[] => {
|
||||||
|
const selectionState = Cell(false);
|
||||||
|
const initialDepth = 0;
|
||||||
|
|
||||||
|
return Arr.map(lists, (list) => ({
|
||||||
|
sourceList: list,
|
||||||
|
entries: parseList(initialDepth, itemSelection, selectionState, list)
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
export { parseLists };
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Element, Traverse, Compare } from '@ephox/sugar';
|
||||||
|
|
||||||
|
export const enum ListType {
|
||||||
|
OL = 'ol',
|
||||||
|
UL = 'ul'
|
||||||
|
}
|
||||||
|
|
||||||
|
const isList = (el: Element) => {
|
||||||
|
return Compare.is(el, 'OL,UL');
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasFirstChildList = (el: Element) => {
|
||||||
|
return Traverse.firstChild(el).map(isList).getOr(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const hasLastChildList = (el: Element) => {
|
||||||
|
return Traverse.lastChild(el).map(isList).getOr(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
isList,
|
||||||
|
hasFirstChildList,
|
||||||
|
hasLastChildList
|
||||||
|
};
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "amd",
|
||||||
|
"target": "es6",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outFile": "list.js"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"./**/*.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"**/node_modules"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) Tiny Technologies, Inc. All rights reserved.
|
||||||
|
* Licensed under the LGPL or a commercial license.
|
||||||
|
* For LGPL see License.txt in the project root for license information.
|
||||||
|
* For commercial licenses see https://www.tiny.cloud/
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Tools from 'tinymce/core/api/util/Tools';
|
||||||
|
import * as NodeType from '../core/NodeType';
|
||||||
|
import Editor from 'tinymce/core/api/Editor';
|
||||||
|
import { isCustomList } from '../core/Util';
|
||||||
|
import { findContainerListTypeFromEvent } from '../listModel/JoplinListUtil';
|
||||||
|
|
||||||
|
const findIndex = function (list, predicate) {
|
||||||
|
for (let index = 0; index < list.length; index++) {
|
||||||
|
const element = list[index];
|
||||||
|
|
||||||
|
if (predicate(element)) {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const listState = function (editor: Editor, listName, options:any = {}) {
|
||||||
|
options = {
|
||||||
|
listType: 'regular',
|
||||||
|
...options,
|
||||||
|
};
|
||||||
|
|
||||||
|
return function (buttonApi) {
|
||||||
|
const nodeChangeHandler = (e) => {
|
||||||
|
const tableCellIndex = findIndex(e.parents, NodeType.isTableCellNode);
|
||||||
|
const parents = tableCellIndex !== -1 ? e.parents.slice(0, tableCellIndex) : e.parents;
|
||||||
|
const lists = Tools.grep(parents, NodeType.isListNode);
|
||||||
|
const listType = findContainerListTypeFromEvent(e);
|
||||||
|
buttonApi.setActive(listType === options.listType && lists.length > 0 && lists[0].nodeName === listName && !isCustomList(lists[0]));
|
||||||
|
};
|
||||||
|
|
||||||
|
const editorClickHandler = (event) => {
|
||||||
|
editor.execCommand('ToggleJoplinChecklistItem', false, { element: event.target });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.listType === 'joplinChecklist') {
|
||||||
|
editor.on('click', editorClickHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.on('NodeChange', nodeChangeHandler);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (options.listType === 'joplinChecklist') {
|
||||||
|
editor.off('click', editorClickHandler);
|
||||||
|
}
|
||||||
|
editor.off('NodeChange', nodeChangeHandler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const register = function (editor: Editor) {
|
||||||
|
const hasPlugin = function (editor, plugin) {
|
||||||
|
const plugins = editor.settings.plugins ? editor.settings.plugins : '';
|
||||||
|
return Tools.inArray(plugins.split(/[ ,]/), plugin) !== -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const exec = (command) => () => editor.execCommand(command);
|
||||||
|
|
||||||
|
if (!hasPlugin(editor, 'advlist')) {
|
||||||
|
editor.ui.registry.addToggleButton('numlist', {
|
||||||
|
icon: 'ordered-list',
|
||||||
|
active: false,
|
||||||
|
tooltip: 'Numbered list',
|
||||||
|
onAction: exec('InsertOrderedList'),
|
||||||
|
onSetup: listState(editor, 'OL')
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.ui.registry.addToggleButton('bullist', {
|
||||||
|
icon: 'unordered-list',
|
||||||
|
active: false,
|
||||||
|
tooltip: 'Bullet list',
|
||||||
|
onAction: exec('InsertUnorderedList'),
|
||||||
|
onSetup: listState(editor, 'UL')
|
||||||
|
});
|
||||||
|
|
||||||
|
editor.ui.registry.addToggleButton('joplinChecklist', {
|
||||||
|
icon: 'checklist',
|
||||||
|
active: false,
|
||||||
|
tooltip: 'Checkbox list',
|
||||||
|
onAction: exec('InsertJoplinChecklist'),
|
||||||
|
onSetup: listState(editor, 'UL', { listType: 'joplinChecklist' })
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
register
|
||||||
|
};
|
|
@ -0,0 +1,23 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"target": "es5",
|
||||||
|
"module": "es2015",
|
||||||
|
"importHelpers": true,
|
||||||
|
"lib": ["es2015"],
|
||||||
|
"declaration": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "lib",
|
||||||
|
"baseUrl": "."
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/demo/ts",
|
||||||
|
"src/main/ts",
|
||||||
|
"src/test/ts",
|
||||||
|
"node_modules/@ephox/**/*/api/Main.d.ts"
|
||||||
|
],
|
||||||
|
"files": [
|
||||||
|
"global.d.ts",
|
||||||
|
],
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"defaultSeverity": "error",
|
||||||
|
"extends": [
|
||||||
|
"@ephox/tslint-rules/tslint.json"
|
||||||
|
],
|
||||||
|
"jsRules": {},
|
||||||
|
"rules": {},
|
||||||
|
"rulesDirectory": []
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -40,6 +40,7 @@ const BaseService = require('lib/services/BaseService');
|
||||||
const SearchEngine = require('lib/services/SearchEngine');
|
const SearchEngine = require('lib/services/SearchEngine');
|
||||||
const KvStore = require('lib/services/KvStore');
|
const KvStore = require('lib/services/KvStore');
|
||||||
const MigrationService = require('lib/services/MigrationService');
|
const MigrationService = require('lib/services/MigrationService');
|
||||||
|
const { toSystemSlashes } = require('lib/path-utils.js');
|
||||||
|
|
||||||
class BaseApplication {
|
class BaseApplication {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -580,7 +581,7 @@ class BaseApplication {
|
||||||
|
|
||||||
if (process && process.env && process.env.PORTABLE_EXECUTABLE_DIR) return `${process.env.PORTABLE_EXECUTABLE_DIR}/JoplinProfile`;
|
if (process && process.env && process.env.PORTABLE_EXECUTABLE_DIR) return `${process.env.PORTABLE_EXECUTABLE_DIR}/JoplinProfile`;
|
||||||
|
|
||||||
return `${os.homedir()}/.config/${Setting.value('appName')}`;
|
return toSystemSlashes(`${os.homedir()}/.config/${Setting.value('appName')}`, 'linux');
|
||||||
}
|
}
|
||||||
|
|
||||||
async start(argv) {
|
async start(argv) {
|
||||||
|
|
|
@ -9,6 +9,9 @@ class HtmlToMd {
|
||||||
anchorNames: options.anchorNames ? options.anchorNames.map(n => n.trim().toLowerCase()) : [],
|
anchorNames: options.anchorNames ? options.anchorNames.map(n => n.trim().toLowerCase()) : [],
|
||||||
codeBlockStyle: 'fenced',
|
codeBlockStyle: 'fenced',
|
||||||
preserveImageTagsWithSize: !!options.preserveImageTagsWithSize,
|
preserveImageTagsWithSize: !!options.preserveImageTagsWithSize,
|
||||||
|
bulletListMarker: '-',
|
||||||
|
emDelimiter: '*',
|
||||||
|
strongDelimiter: '**',
|
||||||
});
|
});
|
||||||
turndown.use(turndownPluginGfm);
|
turndown.use(turndownPluginGfm);
|
||||||
turndown.remove('script');
|
turndown.remove('script');
|
||||||
|
|
|
@ -55,14 +55,21 @@ class NoteBodyViewer extends Component {
|
||||||
this.forceUpdate();
|
this.forceUpdate();
|
||||||
}, 100);
|
}, 100);
|
||||||
},
|
},
|
||||||
paddingBottom: '3.8em', // Extra bottom padding to make it possible to scroll past the action button (so that it doesn't overlap the text)
|
|
||||||
highlightedKeywords: this.props.highlightedKeywords,
|
highlightedKeywords: this.props.highlightedKeywords,
|
||||||
resources: this.props.noteResources, // await shared.attachedResources(bodyToRender),
|
resources: this.props.noteResources, // await shared.attachedResources(bodyToRender),
|
||||||
codeTheme: theme.codeThemeCss,
|
codeTheme: theme.codeThemeCss,
|
||||||
postMessageSyntax: 'window.ReactNativeWebView.postMessage',
|
postMessageSyntax: 'window.ReactNativeWebView.postMessage',
|
||||||
};
|
};
|
||||||
|
|
||||||
const result = await this.markupToHtml_.render(note.markup_language, bodyToRender, this.props.webViewStyle, mdOptions);
|
const result = await this.markupToHtml_.render(
|
||||||
|
note.markup_language,
|
||||||
|
bodyToRender,
|
||||||
|
{
|
||||||
|
bodyPaddingBottom: '3.8em', // Extra bottom padding to make it possible to scroll past the action button (so that it doesn't overlap the text)
|
||||||
|
...this.props.webViewStyle,
|
||||||
|
},
|
||||||
|
mdOptions
|
||||||
|
);
|
||||||
let html = result.html;
|
let html = result.html;
|
||||||
|
|
||||||
const resourceDownloadMode = Setting.value('sync.resourceDownloadMode');
|
const resourceDownloadMode = Setting.value('sync.resourceDownloadMode');
|
||||||
|
|
|
@ -73,7 +73,7 @@ class FsDriverBase {
|
||||||
// TODO: move out of here and make it part of joplin-renderer
|
// TODO: move out of here and make it part of joplin-renderer
|
||||||
// or assign to option using .bind(fsDriver())
|
// or assign to option using .bind(fsDriver())
|
||||||
async cacheCssToFile(cssStrings) {
|
async cacheCssToFile(cssStrings) {
|
||||||
const cssString = cssStrings.join('\n');
|
const cssString = Array.isArray(cssStrings) ? cssStrings.join('\n') : cssStrings;
|
||||||
const cssFilePath = `${Setting.value('tempDir')}/${md5(escape(cssString))}.css`;
|
const cssFilePath = `${Setting.value('tempDir')}/${md5(escape(cssString))}.css`;
|
||||||
if (!(await this.exists(cssFilePath))) {
|
if (!(await this.exists(cssFilePath))) {
|
||||||
await this.writeFile(cssFilePath, cssString, 'utf8');
|
await this.writeFile(cssFilePath, cssString, 'utf8');
|
||||||
|
|
|
@ -40,6 +40,10 @@ class HtmlToHtml {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async allAssets(/* theme*/) {
|
||||||
|
return []; // TODO
|
||||||
|
}
|
||||||
|
|
||||||
async render(markup, theme, options) {
|
async render(markup, theme, options) {
|
||||||
options = Object.assign({}, {
|
options = Object.assign({}, {
|
||||||
splitted: false,
|
splitted: false,
|
||||||
|
@ -80,7 +84,7 @@ class HtmlToHtml {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let cssStrings = noteStyle(theme, options);
|
let cssStrings = noteStyle(theme);
|
||||||
|
|
||||||
if (options.splitted) {
|
if (options.splitted) {
|
||||||
const splitted = this.splitHtml(html);
|
const splitted = this.splitHtml(html);
|
||||||
|
|
|
@ -36,6 +36,10 @@ class MarkupToHtml {
|
||||||
async render(markupLanguage, markup, theme, options) {
|
async render(markupLanguage, markup, theme, options) {
|
||||||
return this.renderer(markupLanguage).render(markup, theme, options);
|
return this.renderer(markupLanguage).render(markup, theme, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async allAssets(markupLanguage, theme) {
|
||||||
|
return this.renderer(markupLanguage).allAssets(theme);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN = 1;
|
MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN = 1;
|
||||||
|
|
|
@ -3,19 +3,22 @@ const md5 = require('md5');
|
||||||
const noteStyle = require('./noteStyle');
|
const noteStyle = require('./noteStyle');
|
||||||
const { fileExtension } = require('./pathUtils');
|
const { fileExtension } = require('./pathUtils');
|
||||||
const memoryCache = require('memory-cache');
|
const memoryCache = require('memory-cache');
|
||||||
|
|
||||||
|
// /!\/!\ Note: the order of rules is important!! /!\/!\
|
||||||
const rules = {
|
const rules = {
|
||||||
|
fence: require('./MdToHtml/rules/fence').default,
|
||||||
|
sanitize_html: require('./MdToHtml/rules/sanitize_html').default,
|
||||||
image: require('./MdToHtml/rules/image'),
|
image: require('./MdToHtml/rules/image'),
|
||||||
checkbox: require('./MdToHtml/rules/checkbox'),
|
checkbox: require('./MdToHtml/rules/checkbox').default,
|
||||||
katex: require('./MdToHtml/rules/katex'),
|
katex: require('./MdToHtml/rules/katex'),
|
||||||
link_open: require('./MdToHtml/rules/link_open'),
|
link_open: require('./MdToHtml/rules/link_open'),
|
||||||
html_image: require('./MdToHtml/rules/html_image'),
|
html_image: require('./MdToHtml/rules/html_image'),
|
||||||
highlight_keywords: require('./MdToHtml/rules/highlight_keywords'),
|
highlight_keywords: require('./MdToHtml/rules/highlight_keywords'),
|
||||||
code_inline: require('./MdToHtml/rules/code_inline'),
|
code_inline: require('./MdToHtml/rules/code_inline'),
|
||||||
fence: require('./MdToHtml/rules/fence').default,
|
|
||||||
fountain: require('./MdToHtml/rules/fountain'),
|
fountain: require('./MdToHtml/rules/fountain'),
|
||||||
mermaid: require('./MdToHtml/rules/mermaid').default,
|
mermaid: require('./MdToHtml/rules/mermaid').default,
|
||||||
sanitize_html: require('./MdToHtml/rules/sanitize_html').default,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const setupLinkify = require('./MdToHtml/setupLinkify');
|
const setupLinkify = require('./MdToHtml/setupLinkify');
|
||||||
const hljs = require('highlight.js');
|
const hljs = require('highlight.js');
|
||||||
const uslug = require('uslug');
|
const uslug = require('uslug');
|
||||||
|
@ -77,6 +80,13 @@ class MdToHtml {
|
||||||
return this.tempDir_;
|
return this.tempDir_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pluginNames() {
|
||||||
|
const output = [];
|
||||||
|
for (const n in rules) output.push(n);
|
||||||
|
for (const n in plugins) output.push(n);
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
pluginOptions(name) {
|
pluginOptions(name) {
|
||||||
let o = this.pluginOptions_[name] ? this.pluginOptions_[name] : {};
|
let o = this.pluginOptions_[name] ? this.pluginOptions_[name] : {};
|
||||||
o = Object.assign({
|
o = Object.assign({
|
||||||
|
@ -115,8 +125,10 @@ class MdToHtml {
|
||||||
throw new Error(`Unsupported inline mime type: ${mime}`);
|
throw new Error(`Unsupported inline mime type: ${mime}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
const name = `${pluginName}/${asset.name}`;
|
||||||
files.push(Object.assign({}, asset, {
|
files.push(Object.assign({}, asset, {
|
||||||
name: `${pluginName}/${asset.name}`,
|
name: name,
|
||||||
|
path: `pluginAssets/${name}`,
|
||||||
mime: mime,
|
mime: mime,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
@ -124,21 +136,52 @@ class MdToHtml {
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
files: files,
|
pluginAssets: files,
|
||||||
cssStrings: cssStrings,
|
cssStrings: cssStrings,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async render(body, style = null, options = null) {
|
async allAssets(theme) {
|
||||||
|
const assets = {};
|
||||||
|
for (const key in rules) {
|
||||||
|
if (!this.pluginEnabled(key)) continue;
|
||||||
|
const rule = rules[key];
|
||||||
|
|
||||||
|
if (rule.style) {
|
||||||
|
assets[key] = rule.style(theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const processedAssets = this.processPluginAssets(assets);
|
||||||
|
processedAssets.cssStrings.splice(0, 0, noteStyle(theme));
|
||||||
|
const output = await this.outputAssetsToExternalAssets_(processedAssets);
|
||||||
|
return output.pluginAssets;
|
||||||
|
}
|
||||||
|
|
||||||
|
async outputAssetsToExternalAssets_(output) {
|
||||||
|
for (const cssString of output.cssStrings) {
|
||||||
|
output.pluginAssets.push(await this.fsDriver().cacheCssToFile(cssString));
|
||||||
|
}
|
||||||
|
delete output.cssStrings;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "style" here is really the theme, as returned by themeStyle()
|
||||||
|
async render(body, theme = null, options = null) {
|
||||||
options = Object.assign({}, {
|
options = Object.assign({}, {
|
||||||
|
// In bodyOnly mode, the rendered Markdown is returned without the wrapper DIV
|
||||||
bodyOnly: false,
|
bodyOnly: false,
|
||||||
|
// In splitted mode, the CSS and HTML will be returned in separate properties.
|
||||||
|
// In non-splitted mode, CSS and HTML will be merged in the same document.
|
||||||
splitted: false,
|
splitted: false,
|
||||||
|
// When this is true, all assets such as CSS or JS are returned as external
|
||||||
|
// files. Otherwise some of them might be in the cssStrings property.
|
||||||
externalAssetsOnly: false,
|
externalAssetsOnly: false,
|
||||||
postMessageSyntax: 'postMessage',
|
postMessageSyntax: 'postMessage',
|
||||||
paddingBottom: '0',
|
|
||||||
highlightedKeywords: [],
|
highlightedKeywords: [],
|
||||||
codeTheme: 'atom-one-light.css',
|
codeTheme: 'atom-one-light.css',
|
||||||
style: Object.assign({}, defaultNoteStyle),
|
theme: Object.assign({}, defaultNoteStyle, theme),
|
||||||
|
plugins: {},
|
||||||
}, options);
|
}, options);
|
||||||
|
|
||||||
// The "codeHighlightCacheKey" option indicates what set of cached object should be
|
// The "codeHighlightCacheKey" option indicates what set of cached object should be
|
||||||
|
@ -150,7 +193,7 @@ class MdToHtml {
|
||||||
this.lastCodeHighlightCacheKey_ = options.codeHighlightCacheKey;
|
this.lastCodeHighlightCacheKey_ = options.codeHighlightCacheKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheKey = md5(escape(body + JSON.stringify(options) + JSON.stringify(style)));
|
const cacheKey = md5(escape(body + JSON.stringify(options) + JSON.stringify(options.theme)));
|
||||||
const cachedOutput = this.cachedOutputs_[cacheKey];
|
const cachedOutput = this.cachedOutputs_[cacheKey];
|
||||||
if (cachedOutput) return cachedOutput;
|
if (cachedOutput) return cachedOutput;
|
||||||
|
|
||||||
|
@ -237,19 +280,13 @@ class MdToHtml {
|
||||||
// Using the `context` object, a plugin can define what additional assets they need (css, fonts, etc.) using context.pluginAssets.
|
// Using the `context` object, a plugin can define what additional assets they need (css, fonts, etc.) using context.pluginAssets.
|
||||||
// The calling application will need to handle loading these assets.
|
// The calling application will need to handle loading these assets.
|
||||||
|
|
||||||
// /!\/!\ Note: the order of rules is important!! /!\/!\
|
for (const key in rules) {
|
||||||
|
if (!this.pluginEnabled(key)) continue;
|
||||||
|
const rule = rules[key];
|
||||||
|
const ruleInstall = rule.install ? rule.install : rule;
|
||||||
|
markdownIt.use(ruleInstall(context, { ...ruleOptions }));
|
||||||
|
}
|
||||||
|
|
||||||
markdownIt.use(rules.fence(context, ruleOptions));
|
|
||||||
markdownIt.use(rules.sanitize_html(context, ruleOptions));
|
|
||||||
markdownIt.use(rules.image(context, ruleOptions));
|
|
||||||
markdownIt.use(rules.checkbox(context, ruleOptions));
|
|
||||||
markdownIt.use(rules.link_open(context, ruleOptions));
|
|
||||||
markdownIt.use(rules.html_image(context, ruleOptions));
|
|
||||||
if (this.pluginEnabled('katex')) markdownIt.use(rules.katex(context, ruleOptions));
|
|
||||||
if (this.pluginEnabled('fountain')) markdownIt.use(rules.fountain(context, ruleOptions));
|
|
||||||
if (this.pluginEnabled('mermaid')) markdownIt.use(rules.mermaid(context, ruleOptions));
|
|
||||||
markdownIt.use(rules.highlight_keywords(context, ruleOptions));
|
|
||||||
markdownIt.use(rules.code_inline(context, ruleOptions));
|
|
||||||
markdownIt.use(markdownItAnchor, { slugify: uslugify });
|
markdownIt.use(markdownItAnchor, { slugify: uslugify });
|
||||||
|
|
||||||
for (const key in plugins) {
|
for (const key in plugins) {
|
||||||
|
@ -260,40 +297,27 @@ class MdToHtml {
|
||||||
|
|
||||||
const renderedBody = markdownIt.render(body);
|
const renderedBody = markdownIt.render(body);
|
||||||
|
|
||||||
let cssStrings = noteStyle(style, options);
|
let cssStrings = noteStyle(options.theme);
|
||||||
|
|
||||||
const pluginAssets = this.processPluginAssets(context.pluginAssets);
|
let output = this.processPluginAssets(context.pluginAssets);
|
||||||
cssStrings = cssStrings.concat(pluginAssets.cssStrings);
|
cssStrings = cssStrings.concat(output.cssStrings);
|
||||||
|
|
||||||
const output = {
|
|
||||||
pluginAssets: pluginAssets.files.map(f => {
|
|
||||||
return Object.assign({}, f, {
|
|
||||||
path: `pluginAssets/${f.name}`,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (options.bodyOnly) {
|
|
||||||
output.html = renderedBody;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.userCss) cssStrings.push(options.userCss);
|
if (options.userCss) cssStrings.push(options.userCss);
|
||||||
|
|
||||||
|
if (options.bodyOnly) {
|
||||||
|
output.html = renderedBody;
|
||||||
|
output.cssStrings = cssStrings;
|
||||||
|
} else {
|
||||||
const styleHtml = `<style>${cssStrings.join('\n')}</style>`;
|
const styleHtml = `<style>${cssStrings.join('\n')}</style>`;
|
||||||
|
output.html = `${styleHtml}<div id="rendered-md">${renderedBody}</div>`;
|
||||||
const html = `${styleHtml}<div id="rendered-md">${renderedBody}</div>`;
|
|
||||||
|
|
||||||
output.html = html;
|
|
||||||
|
|
||||||
if (options.splitted) {
|
if (options.splitted) {
|
||||||
output.cssStrings = cssStrings;
|
output.cssStrings = cssStrings;
|
||||||
output.html = `<div id="rendered-md">${renderedBody}</div>`;
|
output.html = `<div id="rendered-md">${renderedBody}</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.externalAssetsOnly) {
|
if (options.externalAssetsOnly) output = await this.outputAssetsToExternalAssets_(output);
|
||||||
output.pluginAssets.push(await this.fsDriver().cacheCssToFile(cssStrings));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fow now, we keep only the last entry in the cache
|
// Fow now, we keep only the last entry in the cache
|
||||||
this.cachedOutputs_ = {};
|
this.cachedOutputs_ = {};
|
||||||
|
|
|
@ -1,153 +0,0 @@
|
||||||
let checkboxIndex_ = -1;
|
|
||||||
|
|
||||||
const checkboxStyle = `
|
|
||||||
/* Remove the indentation from the checkboxes at the root of the document
|
|
||||||
(otherwise they are too far right), but keep it for their children to allow
|
|
||||||
nested lists. Make sure this value matches the UL margin. */
|
|
||||||
|
|
||||||
.md-checkbox .checkbox-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.md-checkbox {
|
|
||||||
list-style-type: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
li.md-checkbox input[type=checkbox] {
|
|
||||||
margin-left: -1.71em;
|
|
||||||
margin-right: 0.7em;
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
function createPrefixTokens(Token, id, checked, label, postMessageSyntax, sourceToken) {
|
|
||||||
let token = null;
|
|
||||||
const tokens = [];
|
|
||||||
|
|
||||||
// A bit hard to handle errors here and it's unlikely that the token won't have a valid
|
|
||||||
// map parameter, but if it does set it to a very high value, which will be more easy to notice
|
|
||||||
// in calling code.
|
|
||||||
const lineIndex = sourceToken.map && sourceToken.map.length ? sourceToken.map[0] : 99999999;
|
|
||||||
const checkedString = checked ? 'checked' : 'unchecked';
|
|
||||||
|
|
||||||
const labelId = `cb-label-${id}`;
|
|
||||||
|
|
||||||
const js = `
|
|
||||||
try {
|
|
||||||
if (this.checked) {
|
|
||||||
this.setAttribute('checked', 'checked');
|
|
||||||
} else {
|
|
||||||
this.removeAttribute('checked');
|
|
||||||
}
|
|
||||||
|
|
||||||
${postMessageSyntax}('checkboxclick:${checkedString}:${lineIndex}');
|
|
||||||
const label = document.getElementById("${labelId}");
|
|
||||||
label.classList.remove(this.checked ? 'checkbox-label-unchecked' : 'checkbox-label-checked');
|
|
||||||
label.classList.add(this.checked ? 'checkbox-label-checked' : 'checkbox-label-unchecked');
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Checkbox ${checkedString}:${lineIndex} error', error);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
`;
|
|
||||||
|
|
||||||
token = new Token('checkbox_wrapper_open', 'div', 1);
|
|
||||||
token.attrs = [['class', 'checkbox-wrapper']];
|
|
||||||
tokens.push(token);
|
|
||||||
|
|
||||||
token = new Token('checkbox_input', 'input', 0);
|
|
||||||
token.attrs = [['type', 'checkbox'], ['id', id], ['onclick', js]];
|
|
||||||
if (checked) token.attrs.push(['checked', 'checked']);
|
|
||||||
tokens.push(token);
|
|
||||||
|
|
||||||
token = new Token('label_open', 'label', 1);
|
|
||||||
token.attrs = [['id', labelId], ['for', id], ['class', `checkbox-label-${checkedString}`]];
|
|
||||||
tokens.push(token);
|
|
||||||
|
|
||||||
if (label) {
|
|
||||||
token = new Token('text', '', 0);
|
|
||||||
token.content = label;
|
|
||||||
tokens.push(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createSuffixTokens(Token) {
|
|
||||||
return [
|
|
||||||
new Token('label_close', 'label', -1),
|
|
||||||
new Token('checkbox_wrapper_close', 'div', -1),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function installRule(markdownIt, mdOptions, ruleOptions, context) {
|
|
||||||
markdownIt.core.ruler.push('checkbox', state => {
|
|
||||||
const tokens = state.tokens;
|
|
||||||
const Token = state.Token;
|
|
||||||
|
|
||||||
const checkboxPattern = /^\[([x|X| ])\] (.*)$/;
|
|
||||||
let currentListItem = null;
|
|
||||||
let processedFirstInline = false;
|
|
||||||
for (let i = 0; i < tokens.length; i++) {
|
|
||||||
const token = tokens[i];
|
|
||||||
|
|
||||||
if (token.type === 'list_item_open') {
|
|
||||||
currentListItem = token;
|
|
||||||
processedFirstInline = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (token.type === 'list_item_close') {
|
|
||||||
currentListItem = null;
|
|
||||||
processedFirstInline = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that we only support list items that start with "-" (not with "*")
|
|
||||||
if (currentListItem && currentListItem.markup === '-' && !processedFirstInline && token.type === 'inline') {
|
|
||||||
processedFirstInline = true;
|
|
||||||
const firstChild = token.children && token.children.length ? token.children[0] : null;
|
|
||||||
if (!firstChild) continue;
|
|
||||||
|
|
||||||
const matches = checkboxPattern.exec(firstChild.content);
|
|
||||||
if (!matches || matches.length < 2) continue;
|
|
||||||
|
|
||||||
checkboxIndex_++;
|
|
||||||
const checked = matches[1] !== ' ';
|
|
||||||
const id = `md-checkbox-${checkboxIndex_}`;
|
|
||||||
const label = matches.length >= 3 ? matches[2] : '';
|
|
||||||
|
|
||||||
// Prepend the text content with the checkbox markup and the opening <label> tag
|
|
||||||
// then append the </label> tag at the end of the text content.
|
|
||||||
|
|
||||||
const prefix = createPrefixTokens(Token, id, checked, label, ruleOptions.postMessageSyntax, token);
|
|
||||||
const suffix = createSuffixTokens(Token);
|
|
||||||
|
|
||||||
token.children = markdownIt.utils.arrayReplaceAt(token.children, 0, prefix);
|
|
||||||
token.children = token.children.concat(suffix);
|
|
||||||
|
|
||||||
// Add a class to the <li> container so that it can be targetted with CSS.
|
|
||||||
|
|
||||||
let itemClass = currentListItem.attrGet('class');
|
|
||||||
if (!itemClass) itemClass = '';
|
|
||||||
itemClass += ' md-checkbox joplin-checkbox';
|
|
||||||
currentListItem.attrSet('class', itemClass.trim());
|
|
||||||
|
|
||||||
if (!('checkbox' in context.pluginAssets)) {
|
|
||||||
context.pluginAssets['checkbox'] = [
|
|
||||||
{
|
|
||||||
inline: true,
|
|
||||||
text: checkboxStyle,
|
|
||||||
mime: 'text/css',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function(context, ruleOptions) {
|
|
||||||
return function(md, mdOptions) {
|
|
||||||
installRule(md, mdOptions, ruleOptions, context);
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
let checkboxIndex_ = -1;
|
||||||
|
|
||||||
|
const pluginAssets:Function[] = [];
|
||||||
|
|
||||||
|
pluginAssets[1] = function() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
inline: true,
|
||||||
|
mime: 'text/css',
|
||||||
|
text: `
|
||||||
|
/* Remove the indentation from the checkboxes at the root of the document
|
||||||
|
(otherwise they are too far right), but keep it for their children to allow
|
||||||
|
nested lists. Make sure this value matches the UL margin. */
|
||||||
|
|
||||||
|
.md-checkbox .checkbox-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.md-checkbox {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.md-checkbox input[type=checkbox] {
|
||||||
|
margin-left: -1.71em;
|
||||||
|
margin-right: 0.7em;
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
pluginAssets[2] = function(theme:any) {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
inline: true,
|
||||||
|
mime: 'text/css',
|
||||||
|
text: `
|
||||||
|
/* https://stackoverflow.com/questions/7478336/only-detect-click-event-on-pseudo-element#comment39751366_7478344 */
|
||||||
|
|
||||||
|
ul.joplin-checklist li {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.joplin-checklist {
|
||||||
|
list-style:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.joplin-checklist li::before {
|
||||||
|
content:"\\f14a";
|
||||||
|
font-family:ForkAwesome;
|
||||||
|
background-size: 16px 16px;
|
||||||
|
pointer-events: all;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
margin-left: -1.3em;
|
||||||
|
position: absolute;
|
||||||
|
color: ${theme.htmlColor};
|
||||||
|
}
|
||||||
|
|
||||||
|
.joplin-checklist li:not(.checked)::before {
|
||||||
|
content:"\\f0c8";
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
function createPrefixTokens(Token:any, id:string, checked:boolean, label:string, postMessageSyntax:string, sourceToken:any):any[] {
|
||||||
|
let token = null;
|
||||||
|
const tokens = [];
|
||||||
|
|
||||||
|
// A bit hard to handle errors here and it's unlikely that the token won't have a valid
|
||||||
|
// map parameter, but if it does set it to a very high value, which will be more easy to notice
|
||||||
|
// in calling code.
|
||||||
|
const lineIndex = sourceToken.map && sourceToken.map.length ? sourceToken.map[0] : 99999999;
|
||||||
|
const checkedString = checked ? 'checked' : 'unchecked';
|
||||||
|
|
||||||
|
const labelId = `cb-label-${id}`;
|
||||||
|
|
||||||
|
const js = `
|
||||||
|
try {
|
||||||
|
if (this.checked) {
|
||||||
|
this.setAttribute('checked', 'checked');
|
||||||
|
} else {
|
||||||
|
this.removeAttribute('checked');
|
||||||
|
}
|
||||||
|
|
||||||
|
${postMessageSyntax}('checkboxclick:${checkedString}:${lineIndex}');
|
||||||
|
const label = document.getElementById("${labelId}");
|
||||||
|
label.classList.remove(this.checked ? 'checkbox-label-unchecked' : 'checkbox-label-checked');
|
||||||
|
label.classList.add(this.checked ? 'checkbox-label-checked' : 'checkbox-label-unchecked');
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Checkbox ${checkedString}:${lineIndex} error', error);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
`;
|
||||||
|
|
||||||
|
token = new Token('checkbox_wrapper_open', 'div', 1);
|
||||||
|
token.attrs = [['class', 'checkbox-wrapper']];
|
||||||
|
tokens.push(token);
|
||||||
|
|
||||||
|
token = new Token('checkbox_input', 'input', 0);
|
||||||
|
token.attrs = [['type', 'checkbox'], ['id', id], ['onclick', js]];
|
||||||
|
if (checked) token.attrs.push(['checked', 'checked']);
|
||||||
|
tokens.push(token);
|
||||||
|
|
||||||
|
token = new Token('label_open', 'label', 1);
|
||||||
|
token.attrs = [['id', labelId], ['for', id], ['class', `checkbox-label-${checkedString}`]];
|
||||||
|
tokens.push(token);
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
token = new Token('text', '', 0);
|
||||||
|
token.content = label;
|
||||||
|
tokens.push(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSuffixTokens(Token:any):any[] {
|
||||||
|
return [
|
||||||
|
new Token('label_close', 'label', -1),
|
||||||
|
new Token('checkbox_wrapper_close', 'div', -1),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// @ts-ignore: Keep the function signature as-is despite unusued arguments
|
||||||
|
function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any) {
|
||||||
|
const pluginOptions = { renderingType: 1, ...ruleOptions.plugins['checkbox'] };
|
||||||
|
|
||||||
|
markdownIt.core.ruler.push('checkbox', (state:any) => {
|
||||||
|
const tokens = state.tokens;
|
||||||
|
const Token = state.Token;
|
||||||
|
|
||||||
|
const checkboxPattern = /^\[([x|X| ])\] (.*)$/;
|
||||||
|
let currentListItem = null;
|
||||||
|
let processedFirstInline = false;
|
||||||
|
const lists = [];
|
||||||
|
for (let i = 0; i < tokens.length; i++) {
|
||||||
|
const token = tokens[i];
|
||||||
|
|
||||||
|
if (token.type === 'bullet_list_open') {
|
||||||
|
lists.push(token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.type === 'bullet_list_close') {
|
||||||
|
lists.pop();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.type === 'list_item_open') {
|
||||||
|
currentListItem = token;
|
||||||
|
processedFirstInline = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token.type === 'list_item_close') {
|
||||||
|
currentListItem = null;
|
||||||
|
processedFirstInline = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that we only support list items that start with "-" (not with "*")
|
||||||
|
if (currentListItem && currentListItem.markup === '-' && !processedFirstInline && token.type === 'inline') {
|
||||||
|
processedFirstInline = true;
|
||||||
|
const firstChild = token.children && token.children.length ? token.children[0] : null;
|
||||||
|
if (!firstChild) continue;
|
||||||
|
|
||||||
|
const matches = checkboxPattern.exec(firstChild.content);
|
||||||
|
if (!matches || matches.length < 2) continue;
|
||||||
|
|
||||||
|
const checked = matches[1] !== ' ';
|
||||||
|
const label = matches.length >= 3 ? matches[2] : '';
|
||||||
|
|
||||||
|
const currentList = lists[lists.length - 1];
|
||||||
|
|
||||||
|
if (pluginOptions.renderingType === 1) {
|
||||||
|
checkboxIndex_++;
|
||||||
|
const id = `md-checkbox-${checkboxIndex_}`;
|
||||||
|
|
||||||
|
// Prepend the text content with the checkbox markup and the opening <label> tag
|
||||||
|
// then append the </label> tag at the end of the text content.
|
||||||
|
|
||||||
|
const prefix = createPrefixTokens(Token, id, checked, label, ruleOptions.postMessageSyntax, token);
|
||||||
|
const suffix = createSuffixTokens(Token);
|
||||||
|
|
||||||
|
token.children = markdownIt.utils.arrayReplaceAt(token.children, 0, prefix);
|
||||||
|
token.children = token.children.concat(suffix);
|
||||||
|
|
||||||
|
// Add a class to the <li> container so that it can be targetted with CSS.
|
||||||
|
|
||||||
|
let itemClass = currentListItem.attrGet('class');
|
||||||
|
if (!itemClass) itemClass = '';
|
||||||
|
itemClass += ' md-checkbox joplin-checkbox';
|
||||||
|
currentListItem.attrSet('class', itemClass.trim());
|
||||||
|
} else {
|
||||||
|
const textToken = new Token('text', '', 0);
|
||||||
|
textToken.content = label;
|
||||||
|
const tokens = [];
|
||||||
|
tokens.push(textToken);
|
||||||
|
|
||||||
|
token.children = markdownIt.utils.arrayReplaceAt(token.children, 0, tokens);
|
||||||
|
|
||||||
|
const listClass = currentList.attrGet('class') || '';
|
||||||
|
if (listClass.indexOf('joplin-') < 0) currentList.attrSet('class', (`${listClass} joplin-checklist`).trim());
|
||||||
|
|
||||||
|
if (checked) {
|
||||||
|
currentListItem.attrSet('class', (`${currentListItem.attrGet('class') || ''} checked`).trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!('checkbox' in context.pluginAssets)) {
|
||||||
|
context.pluginAssets['checkbox'] = pluginAssets[pluginOptions.renderingType](ruleOptions.theme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
install: function(context:any, ruleOptions:any) {
|
||||||
|
return function(md:any, mdOptions:any) {
|
||||||
|
installRule(md, mdOptions, ruleOptions, context);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
style: pluginAssets[2],
|
||||||
|
};
|
|
@ -1,6 +1,11 @@
|
||||||
const fountain = require('../../vendor/fountain.min.js');
|
const fountain = require('../../vendor/fountain.min.js');
|
||||||
|
|
||||||
const fountainCss = `
|
const fountainCss = function() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
inline: true,
|
||||||
|
mime: 'text/css',
|
||||||
|
text: `
|
||||||
.fountain {
|
.fountain {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
line-height: 107%;
|
line-height: 107%;
|
||||||
|
@ -92,8 +97,10 @@ const fountainCss = `
|
||||||
.fountain .dialogue h3,
|
.fountain .dialogue h3,
|
||||||
.fountain .dialogue h4 {
|
.fountain .dialogue h4 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}`,
|
||||||
`;
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
function renderFountainScript(markdownIt, content) {
|
function renderFountainScript(markdownIt, content) {
|
||||||
const result = fountain.parse(content);
|
const result = fountain.parse(content);
|
||||||
|
@ -114,13 +121,7 @@ function renderFountainScript(markdownIt, content) {
|
||||||
function addContextAssets(context) {
|
function addContextAssets(context) {
|
||||||
if ('fountain' in context.pluginAssets) return;
|
if ('fountain' in context.pluginAssets) return;
|
||||||
|
|
||||||
context.pluginAssets['fountain'] = [
|
context.pluginAssets['fountain'] = fountainCss();
|
||||||
{
|
|
||||||
inline: true,
|
|
||||||
text: fountainCss,
|
|
||||||
mime: 'text/css',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function installRule(markdownIt, mdOptions, ruleOptions, context) {
|
function installRule(markdownIt, mdOptions, ruleOptions, context) {
|
||||||
|
@ -136,8 +137,11 @@ function installRule(markdownIt, mdOptions, ruleOptions, context) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = function(context, ruleOptions) {
|
module.exports = {
|
||||||
|
install: function(context, ruleOptions) {
|
||||||
return function(md, mdOptions) {
|
return function(md, mdOptions) {
|
||||||
installRule(md, mdOptions, ruleOptions, context);
|
installRule(md, mdOptions, ruleOptions, context);
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
style: fountainCss,
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,6 +14,14 @@ const stringifySafe = require('json-stringify-safe');
|
||||||
|
|
||||||
katex = mhchemModule(katex);
|
katex = mhchemModule(katex);
|
||||||
|
|
||||||
|
function katexStyle() {
|
||||||
|
return [
|
||||||
|
{ name: 'katex.css' },
|
||||||
|
// Note: Katex also requires a number of fonts but they don't need to be specified here
|
||||||
|
// since they will be loaded as needed from the CSS.
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
// Test if potential opening or closing delimieter
|
// Test if potential opening or closing delimieter
|
||||||
// Assumes that there is a "$" at state.src[pos]
|
// Assumes that there is a "$" at state.src[pos]
|
||||||
function isValidDelim(state, pos) {
|
function isValidDelim(state, pos) {
|
||||||
|
@ -184,36 +192,15 @@ function math_block(state, start, end, silent) {
|
||||||
|
|
||||||
const cache_ = {};
|
const cache_ = {};
|
||||||
|
|
||||||
module.exports = function(context) {
|
module.exports = {
|
||||||
|
install: function(context) {
|
||||||
// Keep macros that persist across Katex blocks to allow defining a macro
|
// Keep macros that persist across Katex blocks to allow defining a macro
|
||||||
// in one block and re-using it later in other blocks.
|
// in one block and re-using it later in other blocks.
|
||||||
// https://github.com/laurent22/joplin/issues/1105
|
// https://github.com/laurent22/joplin/issues/1105
|
||||||
context.__katex = { macros: {} };
|
context.__katex = { macros: {} };
|
||||||
|
|
||||||
const addContextAssets = () => {
|
const addContextAssets = () => {
|
||||||
context.pluginAssets['katex'] = [
|
context.pluginAssets['katex'] = katexStyle();
|
||||||
{ name: 'katex.css' },
|
|
||||||
{ name: 'fonts/KaTeX_Main-Regular.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Main-Bold.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Main-BoldItalic.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Main-Italic.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Math-Italic.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Math-BoldItalic.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Size1-Regular.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Size2-Regular.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Size3-Regular.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Size4-Regular.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_AMS-Regular.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Caligraphic-Bold.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Caligraphic-Regular.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Fraktur-Bold.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Fraktur-Regular.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_SansSerif-Bold.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_SansSerif-Italic.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_SansSerif-Regular.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Script-Regular.woff2' },
|
|
||||||
{ name: 'fonts/KaTeX_Typewriter-Regular.woff2' },
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function renderToStringWithCache(latex, options) {
|
function renderToStringWithCache(latex, options) {
|
||||||
|
@ -243,7 +230,7 @@ module.exports = function(context) {
|
||||||
const katexInline = function(latex) {
|
const katexInline = function(latex) {
|
||||||
options.displayMode = false;
|
options.displayMode = false;
|
||||||
try {
|
try {
|
||||||
return `<span class="joplin-editable"><pre class="joplin-source" data-joplin-source-open="$" data-joplin-source-close="$">${latex}</pre>${renderToStringWithCache(latex, options)}</span>`;
|
return `<span class="joplin-editable"><span class="joplin-source" data-joplin-source-open="$" data-joplin-source-close="$">${latex}</span>${renderToStringWithCache(latex, options)}</span>`;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Katex error for:', latex, error);
|
console.error('Katex error for:', latex, error);
|
||||||
return latex;
|
return latex;
|
||||||
|
@ -277,4 +264,6 @@ module.exports = function(context) {
|
||||||
md.renderer.rules.math_inline = inlineRenderer;
|
md.renderer.rules.math_inline = inlineRenderer;
|
||||||
md.renderer.rules.math_block = blockRenderer;
|
md.renderer.rules.math_block = blockRenderer;
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
style: katexStyle,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
function addContextAssets(context:any) {
|
function style() {
|
||||||
if ('mermaid' in context.pluginAssets) return;
|
return [
|
||||||
|
|
||||||
context.pluginAssets['mermaid'] = [
|
|
||||||
{ name: 'mermaid.min.js' },
|
{ name: 'mermaid.min.js' },
|
||||||
{ name: 'mermaid_render.js' },
|
{ name: 'mermaid_render.js' },
|
||||||
{
|
{
|
||||||
|
@ -15,6 +13,12 @@ function addContextAssets(context:any) {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addContextAssets(context:any) {
|
||||||
|
if ('mermaid' in context.pluginAssets) return;
|
||||||
|
|
||||||
|
context.pluginAssets['mermaid'] = style();
|
||||||
|
}
|
||||||
|
|
||||||
// @ts-ignore: Keep the function signature as-is despite unusued arguments
|
// @ts-ignore: Keep the function signature as-is despite unusued arguments
|
||||||
function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any) {
|
function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any) {
|
||||||
const defaultRender:Function = markdownIt.renderer.rules.fence || function(tokens:any[], idx:number, options:any, env:any, self:any) {
|
const defaultRender:Function = markdownIt.renderer.rules.fence || function(tokens:any[], idx:number, options:any, env:any, self:any) {
|
||||||
|
@ -35,8 +39,11 @@ function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function(context:any, ruleOptions:any) {
|
export default {
|
||||||
|
install: function(context:any, ruleOptions:any) {
|
||||||
return function(md:any, mdOptions:any) {
|
return function(md:any, mdOptions:any) {
|
||||||
installRule(md, mdOptions, ruleOptions, context);
|
installRule(md, mdOptions, ruleOptions, context);
|
||||||
};
|
};
|
||||||
}
|
},
|
||||||
|
style: style,
|
||||||
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@ module.exports = {
|
||||||
raisedBackgroundColor: '#e5e5e5',
|
raisedBackgroundColor: '#e5e5e5',
|
||||||
htmlCodeColor: 'rgb(0,0,0)',
|
htmlCodeColor: 'rgb(0,0,0)',
|
||||||
htmlCodeFontSize: '.9em',
|
htmlCodeFontSize: '.9em',
|
||||||
|
bodyPaddingBottom: '0',
|
||||||
|
|
||||||
editorTheme: 'chrome',
|
editorTheme: 'chrome',
|
||||||
codeThemeCss: 'atom-one-light.css',
|
codeThemeCss: 'atom-one-light.css',
|
||||||
|
|
|
@ -1,36 +1,34 @@
|
||||||
module.exports = function(style, options) {
|
module.exports = function(theme) {
|
||||||
style = style ? style : {};
|
theme = theme ? theme : {};
|
||||||
|
|
||||||
// https://necolas.github.io/normalize.css/
|
|
||||||
const normalizeCss = `
|
|
||||||
html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
|
||||||
article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}
|
|
||||||
pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}
|
|
||||||
b,strong{font-weight:bolder}small{font-size:80%}img{border-style:none}
|
|
||||||
`;
|
|
||||||
|
|
||||||
const fontFamily = '\'Avenir\', \'Arial\', sans-serif';
|
const fontFamily = '\'Avenir\', \'Arial\', sans-serif';
|
||||||
|
|
||||||
const css =
|
const css =
|
||||||
`
|
`
|
||||||
|
/* https://necolas.github.io/normalize.css/ */
|
||||||
|
html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||||
|
article,aside,footer,header,nav,section{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||||
|
pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}
|
||||||
|
b,strong{font-weight:bolder}small{font-size:80%}img{border-style:none}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-size: ${style.htmlFontSize};
|
font-size: ${theme.htmlFontSize};
|
||||||
color: ${style.htmlColor};
|
color: ${theme.htmlColor};
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
line-height: ${style.htmlLineHeight};
|
line-height: ${theme.htmlLineHeight};
|
||||||
background-color: ${style.htmlBackgroundColor};
|
background-color: ${theme.htmlBackgroundColor};
|
||||||
font-family: ${fontFamily};
|
font-family: ${fontFamily};
|
||||||
padding-bottom: ${options.paddingBottom};
|
padding-bottom: ${theme.bodyPaddingBottom};
|
||||||
}
|
}
|
||||||
strong {
|
strong {
|
||||||
color: ${style.colorBright};
|
color: ${theme.colorBright};
|
||||||
}
|
}
|
||||||
kbd {
|
kbd {
|
||||||
border: 1px solid ${style.htmlCodeBorderColor};
|
border: 1px solid ${theme.htmlCodeBorderColor};
|
||||||
box-shadow: inset 0 -1px 0 ${style.htmlCodeBorderColor};
|
box-shadow: inset 0 -1px 0 ${theme.htmlCodeBorderColor};
|
||||||
padding: 2px 4px;
|
padding: 2px 4px;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
background-color: ${style.htmlCodeBackgroundColor};
|
background-color: ${theme.htmlCodeBackgroundColor};
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 7px;
|
width: 7px;
|
||||||
|
@ -79,7 +77,7 @@ module.exports = function(style, options) {
|
||||||
h1 {
|
h1 {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border-bottom: 1px solid ${style.htmlDividerColor};
|
border-bottom: 1px solid ${theme.htmlDividerColor};
|
||||||
padding-bottom: .3em;
|
padding-bottom: .3em;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
|
@ -95,7 +93,7 @@ module.exports = function(style, options) {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
color: ${style.htmlLinkColor};
|
color: ${theme.htmlLinkColor};
|
||||||
}
|
}
|
||||||
ul, ol {
|
ul, ol {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
|
@ -116,7 +114,7 @@ module.exports = function(style, options) {
|
||||||
width: 1.2em;
|
width: 1.2em;
|
||||||
height: 1.4em;
|
height: 1.4em;
|
||||||
margin-right: 0.4em;
|
margin-right: 0.4em;
|
||||||
background-color: ${style.htmlLinkColor};
|
background-color: ${theme.htmlLinkColor};
|
||||||
}
|
}
|
||||||
/* These icons are obtained from the wonderful ForkAwesome project by copying the src svgs
|
/* These icons are obtained from the wonderful ForkAwesome project by copying the src svgs
|
||||||
* into the css classes below.
|
* into the css classes below.
|
||||||
|
@ -177,7 +175,7 @@ module.exports = function(style, options) {
|
||||||
-webkit-mask-repeat: no-repeat;
|
-webkit-mask-repeat: no-repeat;
|
||||||
}
|
}
|
||||||
blockquote {
|
blockquote {
|
||||||
border-left: 4px solid ${style.htmlCodeBorderColor};
|
border-left: 4px solid ${theme.htmlCodeBorderColor};
|
||||||
padding-left: 1.2em;
|
padding-left: 1.2em;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
opacity: .7;
|
opacity: .7;
|
||||||
|
@ -185,45 +183,45 @@ module.exports = function(style, options) {
|
||||||
table {
|
table {
|
||||||
text-align: left-align;
|
text-align: left-align;
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
border: 1px solid ${style.htmlCodeBorderColor};
|
border: 1px solid ${theme.htmlCodeBorderColor};
|
||||||
background-color: ${style.htmlBackgroundColor};
|
background-color: ${theme.htmlBackgroundColor};
|
||||||
}
|
}
|
||||||
td, th {
|
td, th {
|
||||||
padding: .5em 1em .5em 1em;
|
padding: .5em 1em .5em 1em;
|
||||||
font-size: ${style.htmlFontSize};
|
font-size: ${theme.htmlFontSize};
|
||||||
color: ${style.htmlColor};
|
color: ${theme.htmlColor};
|
||||||
font-family: ${fontFamily};
|
font-family: ${fontFamily};
|
||||||
}
|
}
|
||||||
td {
|
td {
|
||||||
border: 1px solid ${style.htmlCodeBorderColor};
|
border: 1px solid ${theme.htmlCodeBorderColor};
|
||||||
}
|
}
|
||||||
th {
|
th {
|
||||||
border: 1px solid ${style.htmlCodeBorderColor};
|
border: 1px solid ${theme.htmlCodeBorderColor};
|
||||||
border-bottom: 2px solid ${style.htmlCodeBorderColor};
|
border-bottom: 2px solid ${theme.htmlCodeBorderColor};
|
||||||
background-color: ${style.htmlTableBackgroundColor};
|
background-color: ${theme.htmlTableBackgroundColor};
|
||||||
}
|
}
|
||||||
tr:nth-child(even) {
|
tr:nth-child(even) {
|
||||||
background-color: ${style.htmlTableBackgroundColor};
|
background-color: ${theme.htmlTableBackgroundColor};
|
||||||
}
|
}
|
||||||
tr:hover {
|
tr:hover {
|
||||||
background-color: ${style.raisedBackgroundColor};
|
background-color: ${theme.raisedBackgroundColor};
|
||||||
}
|
}
|
||||||
hr {
|
hr {
|
||||||
border: none;
|
border: none;
|
||||||
border-bottom: 2px solid ${style.htmlDividerColor};
|
border-bottom: 2px solid ${theme.htmlDividerColor};
|
||||||
}
|
}
|
||||||
img {
|
img {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
.inline-code {
|
.inline-code {
|
||||||
border: 1px solid ${style.htmlCodeBorderColor};
|
border: 1px solid ${theme.htmlCodeBorderColor};
|
||||||
background-color: ${style.htmlCodeBackgroundColor};
|
background-color: ${theme.htmlCodeBackgroundColor};
|
||||||
padding-right: .2em;
|
padding-right: .2em;
|
||||||
padding-left: .2em;
|
padding-left: .2em;
|
||||||
border-radius: .25em;
|
border-radius: .25em;
|
||||||
color: ${style.htmlCodeColor};
|
color: ${theme.htmlCodeColor};
|
||||||
font-size: ${style.htmlCodeFontSize};
|
font-size: ${theme.htmlCodeFontSize};
|
||||||
}
|
}
|
||||||
|
|
||||||
.highlighted-keyword {
|
.highlighted-keyword {
|
||||||
|
@ -316,5 +314,5 @@ module.exports = function(style, options) {
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return [normalizeCss, css];
|
return [css];
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
const execa = require('execa');
|
||||||
|
|
||||||
|
const rootDir = `${__dirname}/../../../`;
|
||||||
|
process.chdir(rootDir);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
src: [
|
||||||
|
'ReactNativeClient/**/*.tsx',
|
||||||
|
'ReactNativeClient/**/*.ts',
|
||||||
|
'ElectronClient/**/*.tsx',
|
||||||
|
'ElectronClient/**/*.ts',
|
||||||
|
'CliClient/**/*.tsx',
|
||||||
|
'CliClient/**/*.ts',
|
||||||
|
],
|
||||||
|
fn: async function() {
|
||||||
|
const promise = execa('node', ['node_modules/typescript/bin/tsc', '--project', 'tsconfig.json'], { cwd: rootDir });
|
||||||
|
promise.stdout.pipe(process.stdout);
|
||||||
|
return promise;
|
||||||
|
},
|
||||||
|
};
|
|
@ -99,7 +99,7 @@ utils.copyDir = async function(src, dest, options) {
|
||||||
|
|
||||||
// TODO: add support for delete flag
|
// TODO: add support for delete flag
|
||||||
|
|
||||||
await utils.execCommand(`xcopy /C /I /H /R /Y /S ${excludedFlag} "${src}" ${dest}`);
|
await utils.execCommand(`xcopy /C /I /H /R /Y /S ${excludedFlag} "${src}" "${dest}"`);
|
||||||
|
|
||||||
if (tempFile) await fs.remove(tempFile);
|
if (tempFile) await fs.remove(tempFile);
|
||||||
} else {
|
} else {
|
||||||
|
|
26
gulpfile.js
26
gulpfile.js
|
@ -1,28 +1,11 @@
|
||||||
const gulp = require('gulp');
|
const gulp = require('gulp');
|
||||||
const glob = require('glob');
|
const glob = require('glob');
|
||||||
const ts = require('gulp-typescript');
|
|
||||||
const execa = require('execa');
|
const execa = require('execa');
|
||||||
const utils = require('./Tools/gulp/utils');
|
const utils = require('./Tools/gulp/utils');
|
||||||
|
|
||||||
const tasks = {
|
const tasks = {
|
||||||
copyLib: require('./Tools/gulp/tasks/copyLib'),
|
copyLib: require('./Tools/gulp/tasks/copyLib'),
|
||||||
};
|
tsc: require('./Tools/gulp/tasks/tsc'),
|
||||||
|
|
||||||
const tsProject = ts.createProject('tsconfig.json');
|
|
||||||
|
|
||||||
const tscTaskSrc = [
|
|
||||||
'ReactNativeClient/**/*.tsx',
|
|
||||||
'ReactNativeClient/**/*.ts',
|
|
||||||
'ElectronClient/**/*.tsx',
|
|
||||||
'ElectronClient/**/*.ts',
|
|
||||||
'CliClient/**/*.tsx',
|
|
||||||
'CliClient/**/*.ts',
|
|
||||||
];
|
|
||||||
|
|
||||||
const tscTask = function() {
|
|
||||||
return tsProject.src()
|
|
||||||
.pipe(tsProject())
|
|
||||||
.js.pipe(gulp.dest('./'));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateIgnoredTypeScriptBuildTask = async function() {
|
const updateIgnoredTypeScriptBuildTask = async function() {
|
||||||
|
@ -34,6 +17,7 @@ const updateIgnoredTypeScriptBuildTask = async function() {
|
||||||
'**/CliClient/build/lib/**',
|
'**/CliClient/build/lib/**',
|
||||||
'**/CliClient/tests-build/lib/**',
|
'**/CliClient/tests-build/lib/**',
|
||||||
'**/ElectronClient/dist/**',
|
'**/ElectronClient/dist/**',
|
||||||
|
'**/Modules/TinyMCE/JoplinLists/**',
|
||||||
],
|
],
|
||||||
}).map(f => f.substr(__dirname.length + 1));
|
}).map(f => f.substr(__dirname.length + 1));
|
||||||
|
|
||||||
|
@ -50,17 +34,17 @@ const updateIgnoredTypeScriptBuildTask = async function() {
|
||||||
await utils.replaceFileText(`${__dirname}/.eslintignore`, regex, replacement);
|
await utils.replaceFileText(`${__dirname}/.eslintignore`, regex, replacement);
|
||||||
};
|
};
|
||||||
|
|
||||||
gulp.task('tsc', tscTask);
|
gulp.task('tsc', tasks.tsc.fn);
|
||||||
gulp.task('copyLib', tasks.copyLib.fn);
|
gulp.task('copyLib', tasks.copyLib.fn);
|
||||||
gulp.task('updateIgnoredTypeScriptBuild', updateIgnoredTypeScriptBuildTask);
|
gulp.task('updateIgnoredTypeScriptBuild', updateIgnoredTypeScriptBuildTask);
|
||||||
|
|
||||||
gulp.task('watch', function() {
|
gulp.task('watch', function() {
|
||||||
gulp.watch(tasks.copyLib.src, tasks.copyLib.fn);
|
gulp.watch(tasks.copyLib.src, tasks.copyLib.fn);
|
||||||
gulp.watch(tscTaskSrc, updateIgnoredTypeScriptBuildTask);
|
gulp.watch(tasks.tsc.src, updateIgnoredTypeScriptBuildTask);
|
||||||
|
|
||||||
// For watching, we use the actual tsc tool because it's more robust and
|
// For watching, we use the actual tsc tool because it's more robust and
|
||||||
// doesn't crash when there's an error
|
// doesn't crash when there's an error
|
||||||
const promise = execa('npx', ['tsc', '--watch', '--project', 'tsconfig.json'], { cwd: `${__dirname}` });
|
const promise = execa('node', ['node_modules/typescript/bin/tsc', '--project', 'tsconfig.json'], { cwd: `${__dirname}` });
|
||||||
promise.stdout.pipe(process.stdout);
|
promise.stdout.pipe(process.stdout);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,10 @@
|
||||||
"ElectronClient/gui/editors/PlainEditor.js",
|
"ElectronClient/gui/editors/PlainEditor.js",
|
||||||
"ElectronClient/gui/MultiNoteActions.js",
|
"ElectronClient/gui/MultiNoteActions.js",
|
||||||
"ElectronClient/gui/NoteContentPropertiesDialog.js",
|
"ElectronClient/gui/NoteContentPropertiesDialog.js",
|
||||||
"ElectronClient/gui/utils/NoteText.js"
|
"ElectronClient/gui/utils/NoteText.js",
|
||||||
|
"ElectronClient/gui/editors/TinyMCE/plugins/lists.js",
|
||||||
|
"ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js",
|
||||||
|
"ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js"
|
||||||
],
|
],
|
||||||
"folder_exclude_patterns":
|
"folder_exclude_patterns":
|
||||||
[
|
[
|
||||||
|
@ -89,7 +92,10 @@
|
||||||
"ReactNativeClient/ios/Joplin.xcodeproj/project.xcworkspace",
|
"ReactNativeClient/ios/Joplin.xcodeproj/project.xcworkspace",
|
||||||
"ReactNativeClient/ios/Joplin.xcworkspace/xcuserdata",
|
"ReactNativeClient/ios/Joplin.xcworkspace/xcuserdata",
|
||||||
"ReactNativeClient/ios/Joplin.xcodeproj/xcuserdata",
|
"ReactNativeClient/ios/Joplin.xcodeproj/xcuserdata",
|
||||||
"ElectronClient/pluginAssets"
|
"ElectronClient/pluginAssets",
|
||||||
|
"Modules/TinyMCE/JoplinLists/dist",
|
||||||
|
"Modules/TinyMCE/JoplinLists/lib",
|
||||||
|
"Modules/TinyMCE/JoplinLists/scratch"
|
||||||
],
|
],
|
||||||
"path": "."
|
"path": "."
|
||||||
}
|
}
|
||||||
|
|
|
@ -3071,43 +3071,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"gulp-typescript": {
|
|
||||||
"version": "6.0.0-alpha.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/gulp-typescript/-/gulp-typescript-6.0.0-alpha.1.tgz",
|
|
||||||
"integrity": "sha512-KoT0TTfjfT7w3JItHkgFH1T/zK4oXWC+a8xxKfniRfVcA0Fa1bKrIhztYelYmb+95RB80OLMBreknYkdwzdi2Q==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"ansi-colors": "^4.1.1",
|
|
||||||
"plugin-error": "^1.0.1",
|
|
||||||
"source-map": "^0.7.3",
|
|
||||||
"through2": "^3.0.1",
|
|
||||||
"vinyl": "^2.2.0",
|
|
||||||
"vinyl-fs": "^3.0.3"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-colors": {
|
|
||||||
"version": "4.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
|
|
||||||
"integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"source-map": {
|
|
||||||
"version": "0.7.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
|
|
||||||
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"through2": {
|
|
||||||
"version": "3.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz",
|
|
||||||
"integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"readable-stream": "2 || 3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"gulplog": {
|
"gulplog": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz",
|
||||||
|
@ -4806,18 +4769,6 @@
|
||||||
"semver-compare": "^1.0.0"
|
"semver-compare": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugin-error": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==",
|
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
|
||||||
"ansi-colors": "^1.0.1",
|
|
||||||
"arr-diff": "^4.0.0",
|
|
||||||
"arr-union": "^3.1.0",
|
|
||||||
"extend-shallow": "^3.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"posix-character-classes": {
|
"posix-character-classes": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
||||||
|
|
|
@ -37,7 +37,6 @@
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
"glob": "^7.1.6",
|
"glob": "^7.1.6",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^4.0.2",
|
||||||
"gulp-typescript": "^6.0.0-alpha.1",
|
|
||||||
"husky": "^3.0.2",
|
"husky": "^3.0.2",
|
||||||
"lint-staged": "^9.2.1",
|
"lint-staged": "^9.2.1",
|
||||||
"typescript": "^3.7.3"
|
"typescript": "^3.7.3"
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"target": "es2015",
|
"target": "es2015",
|
||||||
"alwaysStrict": true,
|
"alwaysStrict": true,
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"listEmittedFiles": true,
|
"listEmittedFiles": false,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
|
|
Loading…
Reference in New Issue