├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── app-dev ├── css │ ├── context-info.css │ ├── dialogs.css │ ├── fonts.css │ ├── global.css │ ├── header.css │ ├── lib-overwrite.css │ ├── toolbar.css │ ├── workspace.css │ └── wysiwyg-content.css ├── documents │ └── quick-guide-english.html ├── fonts │ ├── Material-Icons.woff2 │ ├── Roboto-Bold.woff2 │ ├── Roboto-BoldItalic.woff2 │ ├── Roboto-Italic.woff2 │ ├── Roboto-LightItalic.woff2 │ ├── Roboto-Regular.woff2 │ └── Roboto-light.woff2 ├── img │ ├── uncolored-logo.svg │ └── windows-run-admin.png ├── js │ ├── classes │ │ └── Document.class.js │ ├── context-info.js │ ├── dialogs.js │ ├── functions │ │ ├── Content.functions.js │ │ ├── Dialogs.functions.js │ │ ├── Documents.functions.js │ │ ├── IO.functions.js │ │ ├── Remote.functions.js │ │ ├── Toolbar.functions.js │ │ ├── Utils.functions.js │ │ └── Window.functions.js │ ├── init.js │ ├── modules.js │ ├── remote-check.js │ ├── save.js │ ├── search.js │ ├── settings.js │ ├── tabs.js │ ├── toolbar.js │ └── window.js ├── json │ └── emojis-list.json ├── lang │ └── english.xml ├── lib │ ├── Countable.js │ ├── LightRange.ES6.js │ ├── Sortable.no-loader.min.js │ ├── emojify-base.min.css │ ├── emojify-emoticons.min.css │ ├── emojify.min.css │ ├── emojify.min.js │ ├── findAndReplaceDOMText.no-loader.js │ ├── foreach.min.js │ ├── getmdl-select.min.css │ ├── getmdl-select.min.css.map │ ├── getmdl-select.min.js │ ├── getmdl-select.min.js.map │ ├── jsVideoUrlParser.min.js │ ├── marked.min.js │ ├── material.min.css │ ├── material.min.css.map │ ├── material.min.js │ ├── material.min.js.map │ ├── mousetrap-global-bind.min.js │ ├── mousetrap.min.js │ ├── normalize.custom.css │ ├── purify.min.js.map │ ├── purify.no-loader.min.js │ ├── reqwest.no-loader.min.js │ ├── to-markdown.custom.js │ ├── versions-compare.js │ ├── wysiwyg.min.js │ └── zenscroll.no-loader.min.js ├── main-process.js ├── package.json ├── themes │ ├── doc-themes │ │ ├── lib │ │ │ ├── document.css │ │ │ ├── document.js │ │ │ ├── emojify.min.js │ │ │ ├── emojify.pack.min.css │ │ │ ├── highlight.pack.custom.min.js │ │ │ └── normalize.custom.min.css │ │ └── themes-dir │ │ │ ├── _ORIGINAL_attitude │ │ │ ├── assets │ │ │ │ └── fonts │ │ │ │ │ ├── Roboto-Bold.woff2 │ │ │ │ │ ├── Roboto-BoldItalic.woff2 │ │ │ │ │ ├── Roboto-Italic.woff2 │ │ │ │ │ ├── Roboto-Light.woff2 │ │ │ │ │ ├── Roboto-LightItalic.woff2 │ │ │ │ │ ├── Roboto-Regular.woff2 │ │ │ │ │ └── google-fonts.css │ │ │ ├── highlight.js-theme.min.css │ │ │ ├── preview.png │ │ │ ├── script.js │ │ │ ├── style.css │ │ │ └── template.html │ │ │ ├── _ORIGINAL_github-style │ │ │ ├── highlight.js-theme.min.css │ │ │ ├── preview.png │ │ │ ├── script.js │ │ │ ├── style.css │ │ │ └── template.html │ │ │ ├── _ORIGINAL_horizon │ │ │ ├── assets │ │ │ │ └── fonts │ │ │ │ │ ├── Roboto-Bold.woff2 │ │ │ │ │ ├── Roboto-BoldItalic.woff2 │ │ │ │ │ ├── Roboto-Italic.woff2 │ │ │ │ │ ├── Roboto-Regular.woff2 │ │ │ │ │ ├── RobotoSlab-Light.woff2 │ │ │ │ │ └── google-fonts.css │ │ │ ├── highlight.js-theme.min.css │ │ │ ├── preview.png │ │ │ ├── script.js │ │ │ ├── style.css │ │ │ └── template.html │ │ │ └── _ORIGINAL_white-room │ │ │ ├── assets │ │ │ └── fonts │ │ │ │ ├── Roboto-Bold.woff2 │ │ │ │ ├── Roboto-BoldItalic.woff2 │ │ │ │ ├── Roboto-Italic.woff2 │ │ │ │ ├── Roboto-Light.woff2 │ │ │ │ ├── Roboto-LightItalic.woff2 │ │ │ │ ├── Roboto-Regular.woff2 │ │ │ │ └── google-fonts.css │ │ │ ├── highlight.js-theme.min.css │ │ │ ├── preview.png │ │ │ ├── script.js │ │ │ ├── style.css │ │ │ └── template.html │ └── ui-themes │ │ └── _ORIGINAL_white-room │ │ ├── preview.png │ │ └── style.css └── views │ └── main.html ├── app └── .keep ├── build ├── background.png ├── icon.icns ├── icon.ico └── icons │ ├── .keep │ ├── 128x128.png │ ├── 16x16.png │ ├── 24x24.png │ ├── 256x256.png │ ├── 32x32.png │ ├── 48x48.png │ ├── 512x512.png │ ├── 64x64.png │ └── 96x96.png ├── dist └── .keep ├── gulpfile.js ├── node_modules └── .keep ├── package.json └── project ├── doc-theme-creation-pack ├── assets │ ├── fonts │ │ └── Roboto │ │ │ ├── Roboto-Bold.woff2 │ │ │ ├── Roboto-BoldItalic.woff2 │ │ │ ├── Roboto-Italic.woff2 │ │ │ ├── Roboto-Light.woff2 │ │ │ ├── Roboto-LightItalic.woff2 │ │ │ ├── Roboto-Regular.woff2 │ │ │ └── google-fonts.css │ ├── img │ │ └── sun-blue.png │ └── lib │ │ ├── document.css │ │ ├── document.js │ │ ├── emojify.min.js │ │ ├── emojify.pack.min.css │ │ ├── highlight.pack.custom.min.js │ │ └── normalize.custom.min.css ├── highlight.js-theme.min.css ├── preview.png ├── script.js ├── style.css └── template.html ├── docs ├── README.md ├── assets │ ├── uncolored-large-screenshot-win.png │ └── uncolored-logo.png └── priority-info.md ├── document-examples ├── rich-doc-example.html └── sun-blue.png ├── images ├── README.md ├── app-dmg-background-osx.ai ├── app-icon.ai ├── app-splash-screen.ai ├── app-splash-screen_anim.psd ├── assets │ ├── Lato-Light.ttf │ ├── README.md │ ├── Roboto-Light.ttf │ └── TestBkg.png ├── icon_windows_multi-layered.xcf ├── render │ ├── 1024x1024.png │ ├── old │ │ └── install-spinner.gif │ └── uncolored-logo-mini.png └── website │ ├── focus-on-content.psd │ ├── markdown-compatible.psd │ ├── rich-content-tools.psd │ ├── softpedia-certificate.psd │ └── uncolored-large-screenshot.psd └── launcher-models ├── linux.sh ├── osx.sh └── win.bat /.gitignore: -------------------------------------------------------------------------------- 1 | # Exclude OS cache files. 2 | **/.DS_Store 3 | **/._.DS_Store 4 | **/Thumbs.db 5 | 6 | # Exclude IDE files 7 | .idea 8 | 9 | # Exclude NPM packages and build files 10 | node_modules/** 11 | app/** 12 | dist/** 13 | 14 | # But keep folders 15 | !node_modules/.keep 16 | !app/.keep 17 | !dist/.keep 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Release Notes 2 | 3 | 4 | 5 | ##### 0.10.2 : Major issue fixes 6 | - Fixed **undraggable window title bar region** (Major issue) ([topic #17](https://github.com/n457/Uncolored/issues/17)) 7 | - Fixed **minor information bar unlimited height** (Major issue) ([topic #18](https://github.com/n457/Uncolored/issues/18)) 8 | - Updated window title bar with a **responsive behavior in case of small window** 9 | 10 | ##### 0.10.1 : New Features + minor issue fixes 11 | - Added a **window control button** to toggle (Un)colored window **always on top** 12 | - Insert a **new line in a paragraph** with `Shift + Enter` / `⇧Enter` keyboard shortcut 13 | - Fixed **background gradient** in document theme **Horizon** in case of short content. 14 | - minor UI improvements 15 | 16 | ##### 0.9.1 : New Features + New Document Themes + Major & minor issue fixes 17 | - Added (Un)colored **multiplatform UI-integrated window controls**, with related settings ([topic #3](https://github.com/n457/Uncolored/issues/3)) 18 | - Added text selection basic commands : **select all, copy, paste** ([topic #14](https://github.com/n457/Uncolored/issues/14)) 19 | - Added **Table of Content** visualization & **navigation** system 20 | - Added **anchors & anchor links full support** for both HTML & Markdown document formats, with smooth auto-scroll 21 | - Moved Document Information dialog to the bottom right of (Un)colored window, and reduced its dimensions 22 | - Added a **Recount button** in the Document Information dialog 23 | - Added **2 new document themes : Attitude & Horizon** 24 | - Increased in-app documents & White Room documents **font size a bit, for more lisibility** 25 | - Replaced `Uncolored` in-app URL-friendly name by `(Un)colored` **real application name** 26 | - *On OS X*, closing (Un)colored via window controls do the same as `⌘Q` ([topic #8](https://github.com/n457/Uncolored/issues/8)) 27 | - *On Linux*, shortened launcher application description 28 | - Improved (Un)colored **speed performance** 29 | - Fixed a content clearing issue 30 | - minor UI text changes & improvements 31 | - minor UI styles changes & improvements 32 | - *Work in Progress* : Emojis insertion via the toolbar 33 | 34 | ##### 0.8.1 : New Features + Major & minor bug fixes 35 | - Additional ***Embed Content*** tool : insert SoundCloud tracks, Vine videos, and **any kind of other embed content** 36 | - *Open application folder* button in *About* dialog ([topic #10](https://github.com/n457/Uncolored/issues/10)) 37 | - *Open settings file folder* button in *Settings* dialog ([topic #10](https://github.com/n457/Uncolored/issues/10)) 38 | - Changed default document tab names *Unnamed-X* to a more explicit one *Document-X* ([topic #1](https://github.com/n457/Uncolored/issues/1)) 39 | - Darken the background-color of the selected text/content for more visibility ([topic #6](https://github.com/n457/Uncolored/issues/6)) 40 | - Special thanks to Thomas Guilleminot in the *About* dialog ([topic #9](https://github.com/n457/Uncolored/issues/9)) 41 | - minor UI changes 42 | - **Major issues fixes :** 43 | - Sometimes the paste command doesn't work ([topic #4](https://github.com/n457/Uncolored/issues/4)) 44 | - Dropping external assets in the app change app view ([topic #5](https://github.com/n457/Uncolored/issues/5)) 45 | - **minor issues fixes :** 46 | - Wrong toolbar positioning when a link is added via the *Apply Link* tool 47 | - Duplicate file name extension when using *"Save As"* on an already saved document 48 | 49 | ##### 0.7.2 : Bug fixes & Added OS X versions 50 | - Fixed empty priority info dialog 51 | - Fixed empty paragraphs in editor 52 | 53 | ##### 0.7.1 : Quickfix 54 | - Quickfix update check system 55 | 56 | ##### 0.7.0 : Features 57 | - **Multiple document tabs** management 58 | - Standard word processor **inline tools** (bold, italic, underline, strikethrough, add link to selection, superscript, subscript, clear format) 59 | - 6 levels **title tools** 60 | - **Block tools** (paragraph, quote, unordered & ordered list, image insertion via URL, **YouTube / Vimeo / Dailymotion / Twitch video** insertion via URL) 61 | - Real-time **emojis** *(from http://www.emoji-cheat-sheet.com/ for now)* 62 | - Export to **HTML documents** with 2 **themes** : GitHub Style & White Room *(more to come)* 63 | - Export to **Markdown documents** 64 | - **Interface themes** system *(1 interface theme for now, but more to come)* 65 | - 41 **mouse & keyboard shortcuts** 66 | - Basic **search / replace** system *(need improvements)* 67 | - Document information display (number of paragraphs, words, etc.) 68 | - Minimalist interface that let you focus on what matters : **the content** 69 | - Update auto-check system (enable / disable in settings) 70 | - Built-in english **quick guide** *(more languages to come)* 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [](https://twitter.com/n457_media) Following [@n457_media](https://twitter.com/n457_media) on Twitter is **the best way** to keep you updated on my projects, including (Un)colored ! 2 | 3 |

4 | 5 |

6 | (Un)colored logo 7 |

8 | 9 |

10 | Next generation desktop rich content editor that saves documents with themes.
11 | HTML & Markdown compatible. For Windows, OS X & Linux. 12 |

13 | 14 | **version 0.10.2 — Beta** ~ (*Next release: version 1.0, ongoing development*) 15 | 16 | ![](./project/docs/assets/uncolored-large-screenshot-win.png) 17 | 18 | 19 | - (Un)colored is an editor that allows the user to focus on the **text** & **content types** (titles, lists, etc.), then selects the document **saving format**. 20 | - If the selected format is **HTML**, the user can choose an export **document theme** which defines the appearance of the saved document, that can be viewed directly in a web browser. 21 | - If the selected format is **Markdown**, the document is saved as raw Markdown file, without theme. 22 | - A saved HTML / Markdown document can be re-imported in the editor and re-exported, with theme or not, **endlessly**. 23 | 24 | 25 | ### Supported Platforms 26 | (Un)colored is provided for **64-bit systems only**, on **OS X 10.9** and later, **Windows 7** and later, **Linux** (**Ubuntu 12.04** and later, **Debian 8** and later). 27 | 28 | 29 | ### :package: Features *(for now)* 30 | - **Multiple document tabs** management 31 | - Standard word processor **inline tools** (bold, italic, underline, strikethrough, add link to selection, superscript, subscript, clear format) 32 | - 6 levels **title tools** 33 | - **Block tools** (paragraph, quote, unordered & ordered list, image insertion via URL) 34 | - **Web-oriented tools** (**YouTube / Vimeo / Dailymotion / Twitch video** insertion via URL, or **ANY embedded content** (SoundCloud tracks, Facebook posts, ...)) 35 | - Export to **HTML documents** with 4 **themes** *(always more to come)* 36 | - Export to **Markdown documents** 37 | - **Table of Content** visualization & **navigation** system 38 | - **Always on Top** window control button 39 | - Real-time **emojis** *(from http://www.emoji-cheat-sheet.com/ for now)* 40 | - **Interface themes** system *(1 interface theme for now, but more to come)* 41 | - More than **40 mouse & keyboard shortcuts** 42 | - Basic **search / replace** system *(needs improvements)* 43 | - Document information display (number of paragraphs, words, etc.) 44 | - Minimalist interface that let you focus on what matters : **the content** 45 | - Update auto-check system (enable / disable in settings) 46 | - Built-in english **quick guide** *(more languages to come)* 47 | 48 | 49 | ### :nut_and_bolt: Technical Details 50 | (Un)colored is made to be **fast** & fully compatible with **WebKit** web browser engine and **V8** JavaScript engine (those implemented in Google Chrome). So the application is entirely written without any compatibility fallback code and allows using **latest** web technologies. 51 | 52 | - **ES6** : Object Oriented & more beautiful than classic JavaScript. 53 | 54 | For the complete list of used projects, see below the [Related Projects section](#related-projects). 55 | 56 | 57 | ### :warning: Known Issues 58 | - *[Major issue]* GitHub Flavored Markdown documents import/export not fully supported (and missing tools for it). 59 | - *[Major issue]* When entering a local image relative path in the *Image via URL* tool field, the image in the exported document is not visible in-browser. 60 | 61 | 62 | ### :busts_in_silhouette: Contribute 63 | If you have any **question** about the project, noticed a performance **issue** or a **bug**, you can let me know by posting on [the GitHub issues section](https://github.com/n457/Uncolored/issues?utf8=%E2%9C%93&q=). I will be glad to answer :octocat: 64 | 65 | Before creating a new issue topic, be careful to check if your question/issue is not in the [Known Issues section](#known-issues) above or already in [the GitHub issues section](https://github.com/n457/Uncolored/issues?utf8=%E2%9C%93&q=). 66 | 67 | 68 | ### :wrench: Technical Documentation *(not ready yet)* 69 | To know how to **create a document theme** or a **interface theme**, how to **launch the development version** of (Un)colored, **how to build it** or **how it's made**, see [the Full Documentation](./project/docs/README.md). 70 | 71 | 72 | ### :gift: Donation 73 | [— Support (Un)colored —](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=n457%2econtact%40gmail%2ecom&lc=FR&item_name=n457%20%2f%20Bertrand%20Vignaud%2dLerouge¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) 74 | 75 | **(Un)colored is free and open source**. Making applications like this **takes time**. If you like this app, please consider to donate to **support my work** :wink: 76 | If you don't know how much to give, 6 € is a reasonnable amount (will be automatically converted into your currency). Have a nice day ! 77 | 78 | 79 | ### :page_facing_up: License 80 | (Un)colored is released under the [Apache License 2.0](LICENSE). More information on http://choosealicense.com/licenses/apache-2.0/ 81 | 82 | 83 | ### :heart: Related Projects 84 | **Thanks to creators and contributors of these open source projects that (Un)colored proudly uses :** 85 | 86 | [Countable.js](https://sacha.me/Countable/) • 87 | [DOMPurify](https://cure53.de/purify) • 88 | [Electron](http://electron.atom.io/) • 89 | [electron-builder](https://github.com/electron-userland/electron-builder) • 90 | [emojify.js](http://hassankhan.me/emojify.js/) • 91 | [findAndReplaceDOMText](https://github.com/padolsey/findAndReplaceDOMText) • 92 | [foreach.js](https://github.com/toddmotto/foreach) • 93 | [getmdl-select](http://creativeit.github.io/getmdl-select/) • 94 | [github-markdown-css](https://sindresorhus.com/github-markdown-css/) • 95 | [jsVideoUrlParser](https://github.com/Zod-/jsVideoUrlParser) • 96 | [LightRange.js](http://n457.github.io/LightRange.js/) • 97 | [marked](https://github.com/chjj/marked) • 98 | [Material Design Lite](https://getmdl.io/) • 99 | [Mousetrap](https://craig.is/killing/mice) • 100 | [Mousetrap Global Bind](https://github.com/ccampbell/mousetrap/tree/master/plugins/global-bind) • 101 | [Node.js](https://nodejs.org) • 102 | [Normalize.css](http://necolas.github.io/normalize.css/) • 103 | [reqwest](https://github.com/ded/reqwest) • 104 | [Sortable](http://rubaxa.github.io/Sortable/) • 105 | [to-markdown](http://domchristie.github.io/to-markdown/) • 106 | [versions-compare](https://gist.github.com/alexey-bass/1115557) • 107 | [wysiwyg.js](http://wysiwygjs.github.io/) • 108 | [Zenscroll](https://zengabor.github.io/zenscroll/) 109 | 110 | 111 | Proudly powered by [Electron](http://electron.atom.io/) 112 | 113 | [Electron logo](http://electron.atom.io/) 114 | -------------------------------------------------------------------------------- /app-dev/css/context-info.css: -------------------------------------------------------------------------------- 1 | #minor-info-bar_unc2741.protected-id { 2 | position: absolute; 3 | left: 0; 4 | bottom: 0; 5 | max-width: calc(100% - 12px); 6 | max-height: 30%; 7 | font-size: 12px; 8 | line-height: 15px; 9 | word-wrap: break-word; 10 | padding: 4px 6px; 11 | opacity: 0; 12 | overflow-y: hidden; 13 | pointer-events: none; 14 | z-index: 2; 15 | transition: opacity 0.2s; } 16 | #minor-info-bar_unc2741.protected-id.active { 17 | opacity: 1; } 18 | 19 | 20 | .menu-list { 21 | position: absolute; 22 | opacity: 0; 23 | pointer-events: none; 24 | transition: opacity 0.2s; } 25 | .menu-list.active { 26 | opacity: 1; 27 | pointer-events: auto; } 28 | .menu-list .options { 29 | display: none; } 30 | .menu-list .label { 31 | text-align: center; 32 | padding: 4px 0; } 33 | .menu-list .menu-list-item { 34 | padding: 2px 8px; } 35 | .menu-list a.menu-list-item { 36 | display: block; 37 | text-decoration: none; 38 | cursor: default; } 39 | 40 | /* top & left via JS */ 41 | #context-menu_unc2741.protected-id { 42 | z-index: 5; } 43 | #context-menu_unc2741.protected-id[data-target="tab"] .tab-view, 44 | #context-menu_unc2741.protected-id[data-target="text"] .text-view, 45 | #context-menu_unc2741.protected-id[data-target="a"] .a-view, 46 | #context-menu_unc2741.protected-id[data-target="anchor-link"] .anchor-link-view, 47 | #context-menu_unc2741.protected-id[data-target="anchor"] .anchor-view, 48 | #context-menu_unc2741.protected-id[data-target="img"] .img-view, 49 | #context-menu_unc2741.protected-id[data-target="emoji"] .emoji-view { 50 | display: block; } 51 | 52 | 53 | /* top & left via JS */ 54 | #preview-large_unc2741.protected-id { 55 | position: absolute; 56 | opacity: 0; 57 | pointer-events: none; 58 | z-index: 10; 59 | transition: opacity 0.2s; } 60 | #preview-large_unc2741.protected-id.active { 61 | opacity: 1; } 62 | #preview-large_unc2741.protected-id .label { 63 | padding: 4px 6px; } 64 | -------------------------------------------------------------------------------- /app-dev/css/dialogs.css: -------------------------------------------------------------------------------- 1 | .mdl-card { 2 | position: absolute; 3 | width: 95%; 4 | max-height: calc(100% - 144px); 5 | opacity: 0; 6 | pointer-events: none; 7 | z-index: 6; } 8 | 9 | .mdl-card.dialog-large { 10 | max-width: 650px; } 11 | .mdl-card.dialog-small { 12 | max-width: 350px; } 13 | 14 | .mdl-card.dialog-top { 15 | top: 90px; 16 | transition: top 0.3s, opacity 0.3s; } 17 | .mdl-card.dialog-right { 18 | right: 17px; } 19 | .mdl-card.dialog-bottom { 20 | bottom: 10px; 21 | transition: bottom 0.3s, opacity 0.3s; } 22 | .mdl-card.dialog-h-center { 23 | left: 0; 24 | right: 0; 25 | margin: auto; } 26 | 27 | body[data-dialog="save-as"] #save-as-dialog_unc2741.protected-id, 28 | body[data-dialog="table-content"] #table-content-dialog_unc2741.protected-id, 29 | body[data-dialog="settings"] #settings-dialog_unc2741.protected-id, 30 | body[data-dialog="shortcuts"] #shortcuts-dialog_unc2741.protected-id, 31 | body[data-dialog="about"] #about-dialog_unc2741.protected-id, 32 | body[data-dialog="print"] #print-dialog_unc2741.protected-id, 33 | body[data-dialog="unsaved-docs"] #unsaved-docs-dialog_unc2741.protected-id, 34 | body[data-dialog="new-update"] #new-update-dialog_unc2741.protected-id, 35 | body[data-dialog="priority-info"] #priority-info-dialog_unc2741.protected-id, 36 | body[data-dialog="io-error"] #io-error-dialog_unc2741.protected-id { 37 | top: 112px; 38 | opacity: 1; 39 | pointer-events: auto; } 40 | body[data-dialog="search"] #search-dialog_unc2741.protected-id, 41 | body[data-dialog="doc-info"] #doc-info-dialog_unc2741.protected-id { 42 | bottom: 32px; 43 | opacity: 1; 44 | pointer-events: auto; } 45 | 46 | .mdl-card img { 47 | display: block; 48 | margin: auto; 49 | -webkit-user-drag: none; } 50 | 51 | 52 | .mdl-card__title .material-icons { 53 | margin-right: 12px; } 54 | 55 | 56 | .mdl-card__supporting-text a {} 57 | .mdl-card__supporting-text a:hover { 58 | text-decoration: none; } 59 | 60 | 61 | .overflow-y-section { 62 | overflow-y: auto; } 63 | 64 | 65 | .inline-label { 66 | margin-right: 18px; } 67 | 68 | 69 | .select-section { 70 | overflow: visible; } 71 | .select-section .mdl-menu { 72 | width: 100%; } 73 | 74 | 75 | .preview-list-label { 76 | margin-top: 16px; } 77 | .preview-list .mdl-list__item { 78 | transition: background-color 0.2s; } 79 | .preview-list .mdl-list__item:active, 80 | .preview-list .mdl-list__item.active {} 81 | 82 | 83 | /* Save as dialog */ 84 | #save-path-field_unc2741.protected-id { 85 | cursor: pointer; } 86 | 87 | 88 | /* Search / Replace dialog */ 89 | #search-dialog_unc2741.protected-id .search-field-container { 90 | margin-right: 7px; } 91 | #search-dialog_unc2741.protected-id .replace-field-container { 92 | margin-left: 7px; } 93 | 94 | 95 | /* Table of content dialog */ 96 | #table-content-dialog_unc2741.protected-id .no-headings-view, 97 | #table-content-dialog_unc2741.protected-id .headings-list-view { 98 | display: none; } 99 | #table-content-dialog_unc2741.protected-id[data-view="no-headings"] .no-headings-view, 100 | #table-content-dialog_unc2741.protected-id[data-view="headings-list"] .headings-list-view { 101 | display: block; } 102 | 103 | #table-content-dialog_unc2741.protected-id .headings-list { 104 | margin: 8px 0 20px 0; } 105 | #table-content-dialog_unc2741.protected-id .mdl-list__item { 106 | min-height: 0; 107 | font-size: 14px; 108 | padding-top: 7px; 109 | padding-bottom: 7px; } 110 | #table-content-dialog_unc2741.protected-id .mdl-list__item-secondary-action { 111 | font-size: 10px; } 112 | 113 | 114 | /* Shortcuts dialog */ 115 | #shortcuts-dialog_unc2741.protected-id .shortcuts-list { 116 | margin-bottom: 20px; } 117 | #shortcuts-dialog_unc2741.protected-id h6 { 118 | padding: 4px 16px; 119 | margin: 0; } 120 | #shortcuts-dialog_unc2741.protected-id .mdl-list__item { 121 | min-height: 27px; 122 | font-size: 13px; } 123 | #shortcuts-dialog_unc2741.protected-id .mdl-list__item:nth-child(even) {} 124 | 125 | 126 | /* About dialog */ 127 | #about-dialog_unc2741.protected-id .logo { 128 | height: 56px; 129 | margin-top: 24px; 130 | margin-bottom: 50px; } 131 | 132 | #about-dialog_unc2741.protected-id .update-check { 133 | text-align: center; } 134 | #about-dialog_unc2741.protected-id .loading-text, 135 | #about-dialog_unc2741.protected-id .loading-icon, 136 | #about-dialog_unc2741.protected-id .up-to-date, 137 | #about-dialog_unc2741.protected-id .new-update { 138 | display: none; } 139 | #about-dialog_unc2741.protected-id[data-state="loading"] .loading-text, 140 | #about-dialog_unc2741.protected-id[data-state="loading"] .loading-icon, 141 | #about-dialog_unc2741.protected-id[data-state="up-to-date"] .up-to-date, 142 | #about-dialog_unc2741.protected-id[data-state="new-update"] .new-update { 143 | display: block; } 144 | #about-dialog_unc2741.protected-id .new-update {} 145 | #about-dialog_unc2741.protected-id .ajax-error { 146 | display: none; 147 | /* Because .ajax-error contains URL */ 148 | word-wrap: break-word; } 149 | #about-dialog_unc2741.protected-id[data-state="error"] .ajax-error { 150 | display: block; } 151 | #about-dialog_unc2741.protected-id .ajax-error a {} 152 | 153 | #about-dialog_unc2741.protected-id .author-info { 154 | margin-top: 10px; } 155 | #about-dialog_unc2741.protected-id .author-avatar { 156 | display: block; 157 | width: 50px; 158 | height: 50px; 159 | background-image: url('https://avatars0.githubusercontent.com/u/11824581?v=3&s=460'); 160 | background-position: center; 161 | background-size: cover; 162 | border-radius: 50%; 163 | margin: auto; } 164 | #about-dialog_unc2741.protected-id .author-info p { 165 | text-align: center; 166 | margin-top: 13px; } 167 | #about-dialog_unc2741.protected-id .author-info .material-icons {} 168 | 169 | #about-dialog_unc2741.protected-id .donate { 170 | cursor: pointer; 171 | transition: background-color 1s; } 172 | #about-dialog_unc2741.protected-id .donate:hover {} 173 | #about-dialog_unc2741.protected-id .donate:active {} 174 | -------------------------------------------------------------------------------- /app-dev/css/fonts.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Roboto'; 3 | font-weight: 300; 4 | font-style: normal; 5 | src: url(../fonts/Roboto-Light.woff2) format('woff2'); } 6 | @font-face { 7 | font-family: 'Roboto'; 8 | font-weight: 300; 9 | font-style: italic; 10 | src: url(../fonts/Roboto-LightItalic.woff2) format('woff2'); } 11 | 12 | @font-face { 13 | font-family: 'Roboto'; 14 | /*font-weight: 400;*/ 15 | font-weight: normal; 16 | font-style: normal; 17 | src: url(../fonts/Roboto-Regular.woff2) format('woff2'); } 18 | @font-face { 19 | font-family: 'Roboto'; 20 | /*font-weight: 700;*/ 21 | font-weight: bold; 22 | font-style: normal; 23 | src: url(../fonts/Roboto-Bold.woff2) format('woff2'); } 24 | @font-face { 25 | font-family: 'Roboto'; 26 | /*font-weight: 400;*/ 27 | font-weight: normal; 28 | font-style: italic; 29 | src: url(../fonts/Roboto-Italic.woff2) format('woff2'); } 30 | 31 | @font-face { 32 | font-family: 'Roboto'; 33 | /*font-weight: 700;*/ 34 | font-weight: bold; 35 | font-style: italic; 36 | src: url(../fonts/Roboto-BoldItalic.woff2) format('woff2'); } 37 | 38 | 39 | 40 | 41 | @font-face { 42 | font-family: 'Material Icons'; 43 | /*font-weight: 400;*/ 44 | font-weight: normal; 45 | font-style: normal; 46 | src: url(../fonts/Material-Icons.woff2) format('woff2'); } 47 | 48 | .material-icons { 49 | font-family: 'Material Icons' !important; 50 | font-weight: normal; 51 | font-style: normal; 52 | font-size: 24px; 53 | line-height: 1; 54 | letter-spacing: normal; 55 | text-transform: none; 56 | display: inline-block; 57 | white-space: nowrap; 58 | word-wrap: normal; 59 | direction: ltr; 60 | -webkit-font-feature-settings: 'liga'; 61 | -webkit-font-smoothing: antialiased; } 62 | -------------------------------------------------------------------------------- /app-dev/css/global.css: -------------------------------------------------------------------------------- 1 | body { 2 | cursor: default; 3 | overflow: hidden; 4 | -webkit-font-smoothing: antialiased; 5 | -webkit-user-select: none; } 6 | body.no-frame { 7 | box-sizing: border-box; 8 | border-width: 1px; 9 | border-style: solid; } 10 | body.windows-os .osx-os:not(.windows-os), 11 | body.windows-os .linux-os:not(.windows-os), 12 | body.osx-os .windows-os:not(.osx-os), 13 | body.osx-os .linux-os:not(.osx-os), 14 | body.linux-os .osx-os:not(.linux-os), 15 | body.linux-os .windows-os:not(.linux-os) { 16 | display: none; } 17 | 18 | a {} 19 | 20 | input, [contenteditable] { 21 | outline: 0; } 22 | 23 | [contenteditable="false"] { 24 | cursor: default; } 25 | 26 | kbd { 27 | font-family: 'Roboto'; 28 | 29 | /* CSS properties from https://developers.google.com/web/tools/chrome-devtools/iterate/inspect-styles/shortcuts */ 30 | display: inline-block; 31 | font-size: 11px; 32 | line-height: 1.4; 33 | white-space: nowrap; 34 | padding: .1em .6em; 35 | margin: 0 .1em; 36 | border-width: 1px; 37 | border-style: solid; 38 | border-radius: 3px; } 39 | 40 | 41 | .hover-effects:hover {} 42 | .hover-effects:active {} 43 | 44 | .disabled { 45 | opacity: 0.3; 46 | pointer-events: none; } 47 | 48 | .remove-list-style { 49 | list-style: none; 50 | padding: 0; 51 | margin: 0; } 52 | 53 | 54 | .italic { 55 | font-style: italic; } 56 | /* highlight */ 57 | .hl {} 58 | 59 | .fake-link { 60 | color: inherit; 61 | text-decoration: underline; 62 | cursor: default; } 63 | .fake-link:hover { 64 | text-decoration: none; } 65 | 66 | 67 | p .material-icons { 68 | vertical-align: middle; } 69 | 70 | .material-icons.loading-icon { 71 | margin: 6px 0; 72 | animation: infinite-rotation 0.3s linear infinite; } 73 | @keyframes infinite-rotation { 74 | from { transform: rotate(0deg); } 75 | to { transform: rotate(360deg); } } 76 | 77 | 78 | ::selection {} 79 | 80 | 81 | ::-webkit-scrollbar { 82 | /* width for vertical scrollbar */ 83 | width: 6px; 84 | /* height for horizontal scrollbar */ 85 | height: 6px; } 86 | ::-webkit-scrollbar-thumb { 87 | border-radius: 99px; } 88 | -------------------------------------------------------------------------------- /app-dev/css/lib-overwrite.css: -------------------------------------------------------------------------------- 1 | body { 2 | /* By default, only header has Roboto, body has Helvetica. */ 3 | font-family: 'Roboto'; } 4 | 5 | 6 | .mdl-layout { 7 | overflow-y: hidden; } 8 | body.no-frame .mdl-layout { 9 | width: calc(100% - 2px); } 10 | .mdl-layout__header { 11 | position: absolute; 12 | /* By default, the header has an min-height of 64px (cancelled when the window becomes small). */ 13 | min-height: auto; 14 | background-color: transparent; 15 | opacity: 0; 16 | box-shadow: none; 17 | transition: opacity 0.2s; } 18 | .mdl-layout__header:hover, 19 | .mdl-layout__header.active { 20 | opacity: 1; } 21 | 22 | .mdl-card { 23 | /* By default, a card has a min-height of 200px. */ 24 | min-height: 0; } 25 | 26 | /* Prevent .mdl-card__title from reducing when resizing the window (so the card) vertically */ 27 | .mdl-card__title { 28 | min-height: 60px; } 29 | 30 | .mdl-card__supporting-text { 31 | width: calc(100% - 32px); } 32 | 33 | .mdl-list { 34 | padding: 0; 35 | margin: 0; } 36 | .mdl-list__item { 37 | padding: 4px 16px; } 38 | 39 | .mdl-menu { 40 | /* By default, a menu list has an ugly padding top & bottom. */ 41 | padding: 0; } 42 | .mdl-menu__item { 43 | display: flex; 44 | /* By default, a menu item has a height of 48px. That is too large. */ 45 | height: 36px; 46 | /* By default, a menu item text is black. */ 47 | line-height: 36px; 48 | cursor: default; } 49 | 50 | .mdl-textfield { 51 | /* By default, a text field has a width of 300px. */ 52 | width: 100%; } 53 | .mdl-textfield--floating-label.is-focused .mdl-textfield__label, 54 | .mdl-textfield--floating-label.is-dirty .mdl-textfield__label, 55 | .mdl-textfield--floating-label.has-placeholder .mdl-textfield__label {} 56 | .mdl-textfield__label:after {} 57 | 58 | .mdl-switch__track, 59 | .mdl-switch__thumb, 60 | .mdl-switch__ripple-container { 61 | cursor: default; } 62 | .mdl-switch.is-checked .mdl-switch__track {} 63 | .mdl-switch.is-checked .mdl-switch__thumb {} 64 | .mdl-switch__ripple-container .mdl-ripple {} 65 | 66 | /* Prevent .mdl-card__actions from reducing when resizing the window (so the card) vertically */ 67 | .mdl-card__actions { 68 | min-height: 53px; } 69 | 70 | .mdl-button { 71 | cursor: default; } 72 | .mdl-button.mdl-button--colored {} 73 | .mdl-button--raised.mdl-button--colored {} 74 | 75 | .mdl-tooltip { 76 | max-width: 80%; 77 | font-size: 12px; 78 | word-wrap: break-word; } 79 | .mdl-tooltip:empty { 80 | display: none; } 81 | -------------------------------------------------------------------------------- /app-dev/css/toolbar.css: -------------------------------------------------------------------------------- 1 | /* top & left via JS */ 2 | #toolbar_unc2741.protected-id { 3 | position: absolute; 4 | opacity: 0; 5 | pointer-events: none; 6 | z-index: 4; 7 | transition: opacity 0.2s; } 8 | #toolbar_unc2741.protected-id.active { 9 | opacity: 1; 10 | pointer-events: auto; } 11 | 12 | #toolbar_unc2741.protected-id .toolbar-button { 13 | float: left; 14 | width: 32px; 15 | height: 32px; 16 | text-align: center; 17 | transition: background-color 0.2s; } 18 | #toolbar_unc2741.protected-id .toolbar-button.active {} 19 | #toolbar_unc2741.protected-id .toolbar-button .material-icons { 20 | font-size: 18px; 21 | line-height: 32px; } 22 | 23 | #toolbar_unc2741.protected-id .tools-list-view { 24 | display: none; } 25 | #toolbar_unc2741.protected-id[data-view="tools-list"] .tools-list-view { 26 | display: block; } 27 | #toolbar_unc2741.protected-id li { 28 | outline: 0; } 29 | #toolbar_unc2741.protected-id .tools-list-view li:nth-child(7), 30 | #toolbar_unc2741.protected-id .tools-list-view li:nth-child(14) { 31 | clear: left; } 32 | #tool-paragraph_unc2741.protected-id .material-icons { 33 | font-size: 15px !important; } 34 | #tool-h2_unc2741.protected-id .material-icons { 35 | font-size: 16px !important; } 36 | #tool-h3_unc2741.protected-id .material-icons { 37 | font-size: 14px !important; } 38 | #tool-h4_unc2741.protected-id .material-icons { 39 | font-size: 12px !important; } 40 | #tool-h5_unc2741.protected-id .material-icons { 41 | font-size: 10px !important; } 42 | #tool-h6_unc2741.protected-id .material-icons { 43 | font-size: 8px !important; } 44 | 45 | #toolbar_unc2741.protected-id .tool-view { 46 | display: none; } 47 | #toolbar_unc2741.protected-id[data-view="remote-image"] .remote-image-view, 48 | #toolbar_unc2741.protected-id[data-view="remote-video"] .remote-video-view, 49 | #toolbar_unc2741.protected-id[data-view="remote-embed"] .remote-embed-view, 50 | #toolbar_unc2741.protected-id[data-view="apply-link"] .apply-link-view, 51 | #toolbar_unc2741.protected-id[data-view="emojis"] .emojis-view { 52 | display: block; } 53 | 54 | #toolbar_unc2741.protected-id input[type="text"].invalid {} 55 | #toolbar_unc2741.protected-id .inline-form input[type="text"] { 56 | float: left; 57 | width: 152px; 58 | height: 32px; 59 | padding: 0 4px; 60 | border: 0; 61 | transition: background-color 0.2s; } 62 | 63 | #toolbar_unc2741.protected-id .emojis-view .emojis-list { 64 | width: 100%; 65 | height: 128px; 66 | overflow-y: auto; } 67 | #toolbar_unc2741.protected-id .emojis-view .category-title { 68 | text-align: center; 69 | padding: 2px 0; 70 | clear: both; } 71 | #toolbar_unc2741.protected-id .emojis-view .emoji-button { 72 | font-size: 16px; 73 | line-height: 33px; } 74 | -------------------------------------------------------------------------------- /app-dev/css/workspace.css: -------------------------------------------------------------------------------- 1 | .content-container { 2 | display: none; 3 | position: absolute; 4 | top: 0; 5 | left: 0; 6 | right: 0; 7 | bottom: 0; 8 | overflow-y: auto; } 9 | .content-container.active { 10 | display: block; } 11 | 12 | /* display: inline-block; necessary for natively inserting
in

with shift + enter shortcut */ 13 | /* http://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome/20533364#20533364 */ 14 | .content-container .wysiwyg-content { 15 | display: inline-block; 16 | width: calc(100% - 140px); 17 | min-height: calc(100% - 336px); 18 | padding: 104px 70px 232px 70px; 19 | cursor: auto; 20 | -webkit-user-select: auto; } 21 | searchresult {} 22 | -------------------------------------------------------------------------------- /app-dev/css/wysiwyg-content.css: -------------------------------------------------------------------------------- 1 | .wysiwyg-content > :first-child { 2 | padding-top: 0 !important; 3 | margin-top: 0 !important; } 4 | .wysiwyg-content > :last-child { 5 | padding-bottom: 0 !important; 6 | margin-bottom: 0 !important; } 7 | 8 | .wysiwyg-content h1, 9 | .wysiwyg-content h2, 10 | .wysiwyg-content h3, 11 | .wysiwyg-content h4, 12 | .wysiwyg-content h5, 13 | .wysiwyg-content h6, 14 | .wysiwyg-content p, 15 | .wysiwyg-content blockquote, 16 | .wysiwyg-content ul, 17 | .wysiwyg-content ol, 18 | .wysiwyg-content iframe, 19 | .wysiwyg-content table, 20 | .wysiwyg-content pre { 21 | margin: 16px 0; } 22 | 23 | 24 | .wysiwyg-content h1, 25 | .wysiwyg-content h2, 26 | .wysiwyg-content h3, 27 | .wysiwyg-content h4, 28 | .wysiwyg-content h5, 29 | .wysiwyg-content h6 { 30 | /* Selected CSS properties from MDL .mdl-card__title & .mdl-card__title-text */ 31 | font-weight: 300; 32 | line-height: normal; } 33 | .wysiwyg-content h1 { 34 | font-size: 26px; } 35 | .wysiwyg-content h2 { 36 | font-size: 24px; } 37 | .wysiwyg-content h3 { 38 | font-size: 22px; } 39 | .wysiwyg-content h4 { 40 | font-size: 20px; } 41 | .wysiwyg-content h5 { 42 | font-size: 18px; } 43 | .wysiwyg-content h6 { 44 | font-size: 16px; } 45 | 46 | .wysiwyg-content p, 47 | .wysiwyg-content li { 48 | font-size: 15px; 49 | line-height: 2; } 50 | 51 | /* For the most part from material.min.css */ 52 | .wysiwyg-content blockquote { 53 | position: relative; 54 | font-size: 18px; 55 | font-weight: 300; 56 | font-style: italic; 57 | line-height: 1.35; 58 | letter-spacing: .08em; 59 | margin-left: 30px; } 60 | blockquote:before { 61 | position: absolute; 62 | left: -0.5em; 63 | content: '“'; } 64 | blockquote:after { 65 | content: '”'; 66 | margin-left: -0.05em; } 67 | 68 | .wysiwyg-content ul, 69 | .wysiwyg-content ol { 70 | padding-left: 20px; } 71 | 72 | .wysiwyg-content img { 73 | max-width: 100%; 74 | cursor: move; } 75 | 76 | .wysiwyg-content iframe { 77 | display: block; 78 | max-width: 100%; 79 | border: 0; } 80 | 81 | .wysiwyg-content a {} 82 | .wysiwyg-content a[contenteditable="false"] { 83 | cursor: pointer; } 84 | 85 | 86 | 87 | /* From https://github.com/yabwe/medium-editor-tables/blob/master/dist/css/medium-editor-tables.css */ 88 | /* Not fully implemented yet */ 89 | .wysiwyg-content table { 90 | border-collapse: collapse; 91 | resize: both; 92 | table-layout: fixed; 93 | border-width: 1px; 94 | border-style: solid; } 95 | .wysiwyg-content table td { 96 | padding: 6px 10px; 97 | border-width: 1px; 98 | border-style: solid; } 99 | 100 | 101 | /* Most of styles here from https://github.com/sindresorhus/github-markdown-css/blob/gh-pages/github-markdown.css */ 102 | /* Not fully implemented yet */ 103 | .wysiwyg-content pre { 104 | line-height: 1.45; 105 | padding: 16px; 106 | border-radius: 3px; 107 | overflow: auto; } 108 | 109 | 110 | 111 | .wysiwyg-content .emoji .emoji-text { 112 | font-size: 0; } 113 | -------------------------------------------------------------------------------- /app-dev/fonts/Material-Icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/fonts/Material-Icons.woff2 -------------------------------------------------------------------------------- /app-dev/fonts/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/fonts/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /app-dev/fonts/Roboto-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/fonts/Roboto-BoldItalic.woff2 -------------------------------------------------------------------------------- /app-dev/fonts/Roboto-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/fonts/Roboto-Italic.woff2 -------------------------------------------------------------------------------- /app-dev/fonts/Roboto-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/fonts/Roboto-LightItalic.woff2 -------------------------------------------------------------------------------- /app-dev/fonts/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/fonts/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /app-dev/fonts/Roboto-light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/fonts/Roboto-light.woff2 -------------------------------------------------------------------------------- /app-dev/img/windows-run-admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/img/windows-run-admin.png -------------------------------------------------------------------------------- /app-dev/js/context-info.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 3 | const $MinorInfoBar = document.getElementById('minor-info-bar_unc2741'); 4 | 5 | const $ContextMenuLinkOpen = N.$ContextMenu.querySelector('.a-view .open a'); 6 | const $ContextMenuAnchorGoTo = N.$ContextMenu.getElementsByClassName('go-to-anchor')[0]; 7 | const $ContextMenuImgOpen = N.$ContextMenu.querySelector('.img-view .open a'); 8 | 9 | const $PreviewLarge = document.getElementById('preview-large_unc2741'); 10 | const $PreviewLargeImg = $PreviewLarge.getElementsByTagName('img')[0]; 11 | 12 | 13 | 14 | const funcFindRealTarget = (Parameter) => { 15 | let $Target = Parameter.$ElementBase; 16 | 17 | while ( 18 | $Target.tagName !== 'A' 19 | && $Target.tagName !== 'IMG' 20 | && ! N.RegExpHeading.test($Target.tagName) 21 | && ! $Target.classList.contains('emoji') 22 | && ! $Target.classList.contains('preview-list-item') 23 | && ! $Target.classList.contains('tab') 24 | && $Target !== N.$Workspace 25 | && $Target !== document.body 26 | ) { 27 | $Target = $Target.parentNode; 28 | } 29 | 30 | if ( 31 | $Target.tagName === 'A' 32 | || $Target.tagName === 'IMG' 33 | || N.RegExpHeading.test($Target.tagName) 34 | || $Target.classList.contains('emoji') 35 | || $Target.classList.contains('preview-list-item') 36 | || $Target.classList.contains('tab') 37 | || $Target === N.$Workspace 38 | ) { 39 | let TargetInfo = {}; 40 | 41 | if ($Target.tagName === 'A') { 42 | // http://stackoverflow.com/questions/3861076/how-can-i-extract-text-after-hash-in-the-href-part-from-a-tag/3861095#3861095 43 | if ($Target.hash) { 44 | TargetInfo = { 45 | $Target: $Target, 46 | strTargetType: 'anchor-link', 47 | strTargetValue: $Target.hash 48 | }; 49 | } else { 50 | TargetInfo = { 51 | $Target: $Target, 52 | strTargetType: 'a', 53 | strTargetValue: $Target.href 54 | }; 55 | } 56 | } 57 | else if ($Target.tagName === 'IMG') { 58 | TargetInfo = { 59 | $Target: $Target, 60 | strTargetType: 'img', 61 | strTargetValue: $Target.src 62 | }; 63 | } 64 | else if (N.RegExpHeading.test($Target.tagName)) { 65 | TargetInfo = { 66 | $Target: $Target, 67 | strTargetType: 'anchor', 68 | strTargetValue: '#' + $Target.id 69 | }; 70 | } 71 | else if ($Target.classList.contains('emoji')) { 72 | TargetInfo = { 73 | $Target: $Target, 74 | strTargetType: 'emoji', 75 | strTargetValue: $Target.getElementsByClassName('emoji-text')[0].textContent 76 | }; 77 | } 78 | else if ($Target.classList.contains('preview-list-item')) { 79 | TargetInfo = { 80 | $Target: $Target, 81 | strTargetType: 'preview-list-item', 82 | strTargetValue: $Target.getElementsByTagName('img')[0].src 83 | }; 84 | } 85 | else if ($Target.classList.contains('tab')) { 86 | TargetInfo = { 87 | $Target: $Target, 88 | strTargetType: 'tab', 89 | strTargetValue: N.arrDocs[$Target.dataset.id].strPath 90 | }; 91 | } 92 | else if ($Target === N.$Workspace) { 93 | TargetInfo = { 94 | $Target: $Target, 95 | strTargetType: 'text', 96 | strTargetValue: null 97 | }; 98 | } 99 | 100 | return TargetInfo; 101 | } else { 102 | return null; 103 | } 104 | }; 105 | 106 | 107 | 108 | const funcPositionElementContext = (Parameters) => { 109 | if (Parameters.Event.pageX < window.innerWidth / 2) { 110 | Parameters.$Element.style.left = Parameters.Event.pageX + 'px'; 111 | } else { 112 | Parameters.$Element.style.left = (Parameters.Event.pageX - Parameters.$Element.offsetWidth) + 'px'; 113 | } 114 | 115 | if (Parameters.Event.pageY < window.innerHeight / 2) { 116 | Parameters.$Element.style.top = Parameters.Event.pageY + 'px'; 117 | } else { 118 | Parameters.$Element.style.top = (Parameters.Event.pageY - Parameters.$Element.offsetHeight) + 'px'; 119 | } 120 | }; 121 | 122 | 123 | 124 | document.addEventListener('click', (Event) => { 125 | if ( ! Event.target.classList.contains('menu-list-item')) { 126 | N.$ContextMenu.classList.remove('active'); 127 | } 128 | }); 129 | 130 | 131 | 132 | document.addEventListener('mousemove', (Event) => { 133 | const TargetInfo = funcFindRealTarget({ $ElementBase: Event.target }); 134 | 135 | if (TargetInfo && TargetInfo.strTargetValue) { 136 | 137 | if (TargetInfo.$Target.classList.contains('no-info-bar') || TargetInfo.$Target.classList.contains('no-context-info')) { 138 | $MinorInfoBar.classList.remove('active'); 139 | } else { 140 | $MinorInfoBar.textContent = TargetInfo.strTargetValue; 141 | $MinorInfoBar.classList.add('active'); 142 | } 143 | 144 | if (TargetInfo.strTargetType === 'preview-list-item') { 145 | $PreviewLargeImg.src = TargetInfo.strTargetValue; 146 | funcPositionElementContext({ 147 | $Element: $PreviewLarge, 148 | Event: Event 149 | }); 150 | $PreviewLarge.classList.add('active'); 151 | } else { 152 | $PreviewLarge.classList.remove('active'); 153 | } 154 | 155 | } else { 156 | $MinorInfoBar.classList.remove('active'); 157 | $PreviewLarge.classList.remove('active'); 158 | } 159 | }); 160 | 161 | 162 | document.addEventListener('contextmenu', (Event) => { 163 | const TargetInfo = funcFindRealTarget({ $ElementBase: Event.target }); 164 | 165 | if (TargetInfo) { 166 | 167 | if (TargetInfo.$Target.classList.contains('no-context-menu') || TargetInfo.$Target.classList.contains('no-context-info')) { 168 | N.$ContextMenu.classList.remove('active'); 169 | } 170 | else { 171 | N.$ContextMenu.dataset.target = TargetInfo.strTargetType; 172 | 173 | // No need to check TargetInfo.strTargetValue, the user can't directly add an empty value and the document content is cleared at input. 174 | 175 | if (TargetInfo.strTargetType === 'a') { 176 | $ContextMenuLinkOpen.href = TargetInfo.strTargetValue; 177 | } 178 | else if (TargetInfo.strTargetType === 'anchor-link') { 179 | $ContextMenuAnchorGoTo.dataset.anchor = TargetInfo.strTargetValue; 180 | } 181 | else if (TargetInfo.strTargetType === 'img') { 182 | $ContextMenuImgOpen.href = TargetInfo.strTargetValue; 183 | } 184 | else if (TargetInfo.strTargetType === 'tab') { 185 | N.$Header.classList.add('active'); 186 | } 187 | 188 | funcPositionElementContext({ 189 | $Element: N.$ContextMenu, 190 | Event: Event 191 | }); 192 | 193 | N.$ContextMenu.classList.add('active'); 194 | 195 | N.LastContextMenuElementInfo = TargetInfo; 196 | 197 | // Toolbar and context menu must not be visible at the same time. 198 | N.Functions.Toolbar.funcResetView(); 199 | N.$Toolbar.classList.remove('active'); 200 | } 201 | } else { 202 | N.$ContextMenu.classList.remove('active'); 203 | } 204 | }); 205 | 206 | 207 | 208 | N.$ContextMenu.addEventListener('mousedown', (Event) => { 209 | Event.preventDefault(); 210 | }); 211 | 212 | 213 | forEach(N.$ContextMenu.querySelectorAll('li.copy'), ($Item) => { 214 | $Item.addEventListener('click', () => { 215 | N.$ContextMenu.classList.remove('active'); 216 | N.ElectronFramework.Clipboard.writeText(N.LastContextMenuElementInfo.strTargetValue); 217 | }); 218 | }); 219 | 220 | forEach(N.$ContextMenu.querySelectorAll('li.open'), ($Item) => { 221 | $Item.addEventListener('click', () => { 222 | N.$ContextMenu.classList.remove('active'); 223 | }); 224 | }); 225 | 226 | N.$ContextMenu.getElementsByClassName('close-tabs')[0].addEventListener('click', () => { 227 | N.$ContextMenu.classList.remove('active'); 228 | N.Functions.Documents.funcCloseAll(); 229 | }); 230 | 231 | N.$ContextMenu.getElementsByClassName('select-all')[0].addEventListener('click', () => { 232 | N.$ContextMenu.classList.remove('active'); 233 | document.execCommand('selectAll'); 234 | }); 235 | N.$ContextMenu.getElementsByClassName('copy-selection')[0].addEventListener('click', () => { 236 | N.$ContextMenu.classList.remove('active'); 237 | document.execCommand('copy'); 238 | }); 239 | N.$ContextMenu.getElementsByClassName('paste')[0].addEventListener('click', () => { 240 | N.$ContextMenu.classList.remove('active'); 241 | document.execCommand('paste'); 242 | }); 243 | 244 | 245 | $ContextMenuAnchorGoTo.addEventListener('click', () => { 246 | N.$ContextMenu.classList.remove('active'); 247 | 248 | const $Anchor = document.querySelector($ContextMenuAnchorGoTo.dataset.anchor); 249 | if ($Anchor) { 250 | zenscroll.createScroller(N.DocActive.$ContentContainer).center($Anchor); 251 | } 252 | }); 253 | 254 | })(); 255 | -------------------------------------------------------------------------------- /app-dev/js/dialogs.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 3 | forEach(document.getElementsByClassName('close-dialog-button'), ($Button) => { 4 | $Button.addEventListener('click', () => { 5 | N.Functions.Dialogs.funcForceClose(); 6 | }); 7 | }); 8 | 9 | 10 | 11 | N.$MenuOpenDocFolder.addEventListener('click', () => { 12 | N.DocActive.methOpenFolder(); 13 | }); 14 | Mousetrap.bindGlobal('mod+shift+o', () => { 15 | N.$MenuOpenDocFolder.click(); 16 | }); 17 | 18 | 19 | document.getElementById('menu-save-as_unc2741').addEventListener('click', () => { 20 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'save-as' }); 21 | }); 22 | Mousetrap.bindGlobal('mod+shift+s', () => { 23 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'save-as' }); 24 | }); 25 | 26 | 27 | document.getElementById('menu-search_unc2741').addEventListener('click', () => { 28 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'search' }); 29 | N.$SearchInput.select(); 30 | }); 31 | Mousetrap.bindGlobal('mod+f', () => { 32 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'search' }); 33 | N.$SearchInput.select(); 34 | }); 35 | 36 | 37 | document.getElementById('menu-table-content_unc2741').addEventListener('click', () => { 38 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'table-content' }); 39 | N.Functions.Dialogs.funcUpdateTableContent(); 40 | }); 41 | N.$TableContentDialog.getElementsByClassName('refresh-button')[0].addEventListener('click', () => { 42 | N.Functions.Dialogs.funcUpdateTableContent(); 43 | }); 44 | 45 | 46 | document.getElementById('menu-doc-info_unc2741').addEventListener('click', () => { 47 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'doc-info' }); 48 | N.Functions.Dialogs.funcUpdateDocInfo(); 49 | }); 50 | N.$DocInfoDialog.getElementsByClassName('recount-button')[0].addEventListener('click', () => { 51 | N.Functions.Dialogs.funcUpdateDocInfo(); 52 | }); 53 | 54 | document.getElementById('menu-print_unc2741').addEventListener('click', () => { 55 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'print' }); 56 | }); 57 | Mousetrap.bindGlobal('mod+p', () => { 58 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'print' }); 59 | }); 60 | 61 | 62 | document.getElementById('menu-settings_unc2741').addEventListener('click', () => { 63 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'settings' }); 64 | }); 65 | 66 | 67 | document.getElementById('menu-shortcuts_unc2741').addEventListener('click', () => { 68 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'shortcuts' }); 69 | }); 70 | Mousetrap.bindGlobal('ctrl+space', () => { 71 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'shortcuts' }); 72 | }); 73 | 74 | 75 | N.$MenuQuickGuide.addEventListener('click', () => { 76 | N.Functions.Documents.funcOpen({ arrPaths: [`${N.strAppPath}/documents/quick-guide-${N.Settings.strUILangSlug}.html`] }); 77 | }); 78 | 79 | 80 | document.getElementById('menu-about_unc2741').addEventListener('click', () => { 81 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'about' }); 82 | }); 83 | N.$AboutDialog.getElementsByClassName('app-folder-button')[0].addEventListener('click', () => { 84 | N.ElectronFramework.Shell.showItemInFolder(N.strAppExePath); 85 | }); 86 | 87 | 88 | 89 | Mousetrap.bindGlobal('escape', () => { 90 | if (N.$Toolbar.dataset.view === 'tools-list') { 91 | N.$Toolbar.classList.remove('active'); 92 | } 93 | 94 | N.$ContextMenu.classList.remove('active'); 95 | 96 | N.Functions.Dialogs.funcCloseCurrent(); 97 | }); 98 | 99 | 100 | Mousetrap.bindGlobal('enter', () => { 101 | N.Functions.Dialogs.funcValidateCurrent(); 102 | }); 103 | 104 | 105 | 106 | 107 | N.$UnsavedDocsDialog.getElementsByClassName('quit-app')[0].addEventListener('click', () => { 108 | document.body.classList.add('force-close-app'); 109 | N.Functions.Window.funcClose(); 110 | }); 111 | 112 | 113 | window.addEventListener('beforeunload', (Event) => { 114 | if ( ! document.body.classList.contains('force-close-app')) { 115 | const AfterCloseStatus = N.Functions.Documents.funcCloseAll(); 116 | 117 | // Prevent the window from closing if there are unsaved documents 118 | if (AfterCloseStatus.boolUnsavedDocs) { 119 | Event.returnValue = false; 120 | } 121 | } 122 | }); 123 | 124 | })(); 125 | -------------------------------------------------------------------------------- /app-dev/js/functions/Content.functions.js: -------------------------------------------------------------------------------- 1 | N.Functions.Content = {}; 2 | 3 | 4 | N.Functions.Content.funcPurifyHTML = (Parameters) => { 5 | let arrAllowedTags = []; 6 | let arrAllowedAttr = []; 7 | 8 | if (Parameters.strAllowedContentMode === 'paste') { 9 | arrAllowedTags = [ 10 | 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 11 | 'blockquote', 'ul', 'ol', 'li', 'img', 12 | 'b', 'i', 'u', 'strike', 'a', 'sup', 'sub', 'br' 13 | ]; 14 | arrAllowedAttr = [ 15 | 'src', 'href' 16 | ]; 17 | } 18 | else if (Parameters.strAllowedContentMode === 'document') { 19 | arrAllowedTags = [ 20 | 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 21 | 'p', 'blockquote', 'ul', 'ol', 'li', 'img', 'iframe', 22 | 'b', 'i', 'u', 'strike', 'a', 'sup', 'sub', 'br' 23 | ]; 24 | arrAllowedAttr = [ 25 | 'src', 'href', 26 | 'id', 27 | // For iframe only : 28 | 'width', 'height', 'allowfullscreen' 29 | ]; 30 | } 31 | else if (Parameters.strAllowedContentMode === 'embed') { 32 | arrAllowedTags = ['iframe']; 33 | arrAllowedAttr = [ 34 | 'src', 'width', 'height', 'allowfullscreen' 35 | ]; 36 | } 37 | 38 | const PurifyParameters = { ALLOWED_TAGS: arrAllowedTags, ALLOWED_ATTR: arrAllowedAttr }; 39 | 40 | return DOMPurify.sanitize(Parameters.strHTML, PurifyParameters); 41 | }; 42 | 43 | 44 | 45 | N.Functions.Content.funcSetHeadingsIDs = (Parameter) => { 46 | forEach(Parameter.Document.$ContentEditable.querySelectorAll('h1, h2, h3, h4, h5, h6'), ($H) => { 47 | // From https://github.com/chjj/marked/blob/master/lib/marked.js#L800 48 | $H.id = $H.textContent.toLowerCase().replace(/[^\w]+/g, '-'); 49 | }); 50 | }; 51 | 52 | 53 | 54 | N.Functions.Content.funcDisplayEmojis = (Parameter) => { 55 | // https://github.com/Ranks/emojify.js#browser 56 | emojify.run(Parameter.Document.$ContentEditable, (strEmojiSymbol, strEmojiName) => { 57 | const $Span = document.createElement('span'); 58 | $Span.classList.add('emoji'); 59 | $Span.classList.add('emoji-' + strEmojiName); 60 | $Span.setAttribute('contenteditable', 'false'); 61 | 62 | const $SpanInner = document.createElement('span'); 63 | $SpanInner.classList.add('emoji-text'); 64 | $SpanInner.innerHTML = ':' + strEmojiName + ':'; 65 | 66 | $Span.appendChild($SpanInner); 67 | 68 | return $Span; 69 | }); 70 | 71 | forEach(Parameter.Document.$ContentEditable.getElementsByClassName('emoji'), ($Emoji) => { 72 | $Emoji.setAttribute('contenteditable', 'false'); 73 | }); 74 | }; 75 | 76 | 77 | 78 | N.Functions.Content.funcClearHTMLForHTMLExport = (Parameter) => { 79 | // Selecting all links exept those with [href] starting with "#" (anchor links) 80 | forEach(Parameter.$ContentEditable.querySelectorAll('a:not([href^="#"])'), ($A) => { 81 | $A.target = '_blank'; 82 | }); 83 | 84 | forEach(Parameter.$ContentEditable.getElementsByClassName('emoji'), ($Emoji) => { 85 | $Emoji.removeAttribute('contenteditable'); 86 | $Emoji.title = $Emoji.getElementsByClassName('emoji-text')[0].textContent; 87 | }); 88 | // Not deleting   because user may write some in

 elements in a future release of the app.
 89 | };
 90 | 
 91 | N.Functions.Content.funcClearHTMLForMarkdownExport = (Parameter) => {
 92 |   while (Parameter.$ContentEditable.getElementsByClassName('emoji').length) {
 93 |     forEach(Parameter.$ContentEditable.getElementsByClassName('emoji'), ($Emoji) => {
 94 |       if ($Emoji) {
 95 |         $Emoji.outerHTML = $Emoji.getElementsByClassName('emoji-text')[0].textContent;
 96 |       }
 97 |     });
 98 |   }
 99 | };
100 | 
101 | 
102 | 
103 | N.Functions.Content.funcClearHTML = (Parameter) => {
104 |   // Must use while : outerHTML = innerHTML cause issues with for/forEach loops
105 | 
106 |   // It happens that the editor outputs some 
. But we want

instead. 107 | while (Parameter.Document.$ContentEditable.getElementsByTagName('div').length) { 108 | forEach(Parameter.Document.$ContentEditable.getElementsByTagName('div'), ($Div) => { 109 | if ($Div) { 110 | $Div.outerHTML = '

' + $Div.innerHTML + '

'; 111 | } 112 | }); 113 | } 114 | 115 | // It happens that the editor outputs some . But we want only .emoji 116 | while (Parameter.Document.$ContentEditable.querySelectorAll('span:not(.emoji):not(.emoji-text), a:not([href])').length) { 117 | forEach(Parameter.Document.$ContentEditable.querySelectorAll('span:not(.emoji):not(.emoji-text), a:not([href])'), ($Element) => { 118 | if ($Element) { 119 | $Element.outerHTML = $Element.innerHTML; 120 | } 121 | }); 122 | } 123 | 124 | // It happens that the editor outputs some element with [style], we don't want this attribute. 125 | forEach(Parameter.Document.$ContentEditable.querySelectorAll('[style]'), ($Element) => { 126 | $Element.removeAttribute('style'); 127 | }); 128 | 129 | // See N.Functions.Content.funcPurifyHTML() for a better comprehension 130 | 131 | forEach(Parameter.Document.$ContentEditable.querySelectorAll('[href]:not(a)'), ($Element) => { 132 | $Element.removeAttribute('href'); 133 | }); 134 | 135 | forEach(Parameter.Document.$ContentEditable.querySelectorAll('[src]:not(img):not(iframe)'), ($Element) => { 136 | $Element.removeAttribute('src'); 137 | }); 138 | forEach(Parameter.Document.$ContentEditable.querySelectorAll('img:not([src]), iframe:not([src])'), ($Element) => { 139 | $Element.parentNode.removeChild($Element); 140 | }); 141 | 142 | forEach(Parameter.Document.$ContentEditable.querySelectorAll('[width]:not(iframe), [height]:not(iframe), [allowfullscreen]:not(iframe)'), ($Element) => { 143 | $Element.removeAttribute('width'); 144 | $Element.removeAttribute('height'); 145 | $Element.removeAttribute('allowfullscreen'); 146 | }); 147 | }; 148 | 149 | 150 | 151 | N.Functions.Content.funcCleanSearch = (Parameter) => { 152 | // Must use while : outerHTML = innerHTML cause issues with for/forEach loops 153 | while (Parameter.Document.$ContentEditable.getElementsByTagName('searchresult').length) { 154 | forEach(Parameter.Document.$ContentEditable.getElementsByTagName('searchresult'), ($Searchresult) => { 155 | if ($Searchresult) { 156 | $Searchresult.outerHTML = $Searchresult.innerHTML; 157 | } 158 | }); 159 | } 160 | 161 | N.$SearchResultsTotal.textContent = '0'; 162 | }; 163 | -------------------------------------------------------------------------------- /app-dev/js/functions/Dialogs.functions.js: -------------------------------------------------------------------------------- 1 | N.Functions.Dialogs = {}; 2 | 3 | 4 | N.Functions.Dialogs.funcShow = (Parameter) => { 5 | if (N.strDialogActiveSlug === 'search') { 6 | N.Functions.Content.funcCleanSearch({ Document: N.DocActive }); 7 | } 8 | 9 | document.body.dataset.dialog = Parameter.strDialogSlug; 10 | N.strDialogActiveSlug = Parameter.strDialogSlug; 11 | 12 | if (N.strDialogActiveSlug) { 13 | N.$DialogActive = document.getElementById(`${N.strDialogActiveSlug}-dialog_unc2741`); 14 | } else { 15 | N.$DialogActive = null; 16 | } 17 | 18 | N.$Toolbar.classList.remove('active'); 19 | }; 20 | 21 | 22 | N.Functions.Dialogs.funcValidateCurrent = () => { 23 | if (N.strDialogActiveSlug) { 24 | const $RaisedButton = N.$DialogActive.getElementsByClassName('mdl-button--raised')[0]; 25 | if ($RaisedButton) { 26 | $RaisedButton.click(); 27 | } 28 | } 29 | }; 30 | 31 | 32 | N.Functions.Dialogs.funcCloseCurrent = () => { 33 | if (N.strDialogActiveSlug) { 34 | // In every dialog, there is at least one .close-dialog-button 35 | N.$DialogActive.getElementsByClassName('close-dialog-button')[0].click(); 36 | } 37 | }; 38 | 39 | 40 | N.Functions.Dialogs.funcForceClose = () => { 41 | N.Functions.Dialogs.funcShow({ strDialogSlug: '' }); 42 | }; 43 | 44 | 45 | N.Functions.Dialogs.funcCloseContext = () => { 46 | if ( 47 | N.strDialogActiveSlug 48 | && N.strDialogActiveSlug !== 'table-content' 49 | && N.strDialogActiveSlug !== 'doc-info' 50 | && N.strDialogActiveSlug !== 'unsaved-docs' 51 | && N.strDialogActiveSlug !== 'new-update' 52 | && N.strDialogActiveSlug !== 'priority-info' 53 | && N.strDialogActiveSlug !== 'io-error' 54 | ) { 55 | N.Functions.Dialogs.funcCloseCurrent(); 56 | } 57 | }; 58 | 59 | 60 | 61 | N.Functions.Dialogs.funcUpdateTableContent = () => { 62 | N.$HeadingsList.innerHTML = ''; 63 | const arrDocHeadings = N.DocActive.$ContentEditable.querySelectorAll('h1, h2, h3, h4, h5, h6'); 64 | 65 | if (arrDocHeadings.length) { 66 | N.Functions.Content.funcSetHeadingsIDs({ Document: N.DocActive }); 67 | 68 | forEach(arrDocHeadings, ($H) => { 69 | const intHeadingLevel = parseInt($H.tagName.slice(-1)); 70 | let strHeadingLevel = ''; 71 | switch (intHeadingLevel) { 72 | case 1: 73 | strHeadingLevel = 'XL'; 74 | break; 75 | case 2: 76 | strHeadingLevel = 'L'; 77 | break; 78 | case 3: 79 | strHeadingLevel = 'M'; 80 | break; 81 | case 4: 82 | strHeadingLevel = 'S'; 83 | break; 84 | case 5: 85 | strHeadingLevel = 'XS'; 86 | break; 87 | case 6: 88 | strHeadingLevel = 'XXS'; 89 | break; 90 | } 91 | 92 | const $HeadingItem = N.$TableContentHeadingModel.cloneNode(true); 93 | $HeadingItem.getElementsByClassName('mdl-list__item-primary-content')[0].textContent = $H.textContent; 94 | $HeadingItem.getElementsByClassName('mdl-list__item-secondary-action')[0].textContent = strHeadingLevel; 95 | $HeadingItem.dataset.anchor = '#' + $H.id; 96 | 97 | 98 | $HeadingItem.addEventListener('click', () => { 99 | const $Anchor = document.querySelector($HeadingItem.dataset.anchor); 100 | if ($Anchor) { 101 | zenscroll.createScroller(N.DocActive.$ContentContainer).center($Anchor); 102 | } 103 | }); 104 | 105 | N.$HeadingsList.appendChild($HeadingItem); 106 | }); 107 | 108 | N.$TableContentDialog.dataset.view = 'headings-list'; 109 | } 110 | else { 111 | N.$TableContentDialog.dataset.view = 'no-headings'; 112 | } 113 | }; 114 | 115 | 116 | 117 | N.Functions.Dialogs.funcUpdateDocInfo = () => { 118 | if (N.DocActive.strDocThemeName) { 119 | N.$DocInfoThemeName.textContent = N.DocActive.strDocThemeName; 120 | } else { 121 | N.$DocInfoThemeName.textContent = '—'; 122 | } 123 | 124 | Countable.once(N.DocActive.$ContentEditable, (Counter) => { 125 | N.$DocInfoParagraphs.textContent = Counter.paragraphs; 126 | N.$DocInfoSentences.textContent = Counter.sentences; 127 | N.$DocInfoWords.textContent = Counter.words; 128 | N.$DocInfoCharacters.textContent = Counter.characters; 129 | N.$DocInfoCharactersSpaces.textContent = Counter.all; 130 | }); 131 | }; 132 | -------------------------------------------------------------------------------- /app-dev/js/functions/Documents.functions.js: -------------------------------------------------------------------------------- 1 | N.Functions.Documents = {}; 2 | 3 | 4 | N.Functions.Documents.funcDocActiveChange = () => { 5 | if (N.DocActive) { 6 | N.DocActive.LastSelection = lightrange.saveSelection(); 7 | N.DocActive.$Tab.classList.remove('active'); 8 | N.DocActive.$ContentContainer.classList.remove('active'); 9 | 10 | N.Functions.Content.funcCleanSearch({ Document: N.DocActive }); 11 | } 12 | }; 13 | 14 | 15 | N.Functions.Documents.funcOpenDialog = () => { 16 | N.Functions.Dialogs.funcCloseContext(); 17 | 18 | N.ElectronFramework.Dialog.showOpenDialog({ 19 | properties: [ 20 | 'openFile', 21 | 'multiSelections', 22 | 'createDirectory' 23 | ], 24 | filters: [ 25 | {name: 'Supported documents', extensions: ['html', 'htm', 'md', 'markdown']}, 26 | {name: 'HTML documents', extensions: ['html', 'htm']}, 27 | {name: 'Markdown documents', extensions: ['md', 'markdown']} 28 | ] 29 | }, (arrPaths) => { 30 | // arrPaths is undefined if the opening is cancelled by the user 31 | if (arrPaths) { 32 | N.Functions.Documents.funcOpen({ arrPaths: arrPaths }); 33 | } 34 | }); 35 | }; 36 | 37 | 38 | 39 | N.Functions.Documents.funcOpen = (Parameter) => { 40 | 41 | forEach(Parameter.arrPaths, (strPath) => { 42 | strPath = N.ElectronFramework.Path.normalize(strPath); 43 | 44 | let boolIsDocAlreadyOpened = false; 45 | 46 | // Check the current path with all opened document 47 | forEach(N.arrDocs, (Doc) => { 48 | // If the document is saved on disk (has a path) and has the same path (already opened) 49 | if (Doc && Doc.strPath && Doc.strPath === strPath) { 50 | // We mark it for the next step 51 | boolIsDocAlreadyOpened = true; 52 | if (Parameter.arrPaths.length === 1) { 53 | Doc.methShow(); 54 | } 55 | // Break the loop, it's useless to continue 56 | return; 57 | } 58 | }); 59 | 60 | // After looping into all the saved and opened documents, the document is not already opened, so we open it 61 | if ( ! boolIsDocAlreadyOpened) { 62 | const FilePathInfo = N.Functions.Utils.funcGetFilePathInfo({ strPath: strPath }); 63 | 64 | let LoadedFile = N.Functions.IO.funcLoadDoc({ strPath: strPath, strFormat: FilePathInfo.strFormat }); 65 | 66 | if (LoadedFile) { 67 | let strDocThemeSlug; 68 | let strDocThemeName; 69 | if (FilePathInfo.strFormat === 'HTML') { 70 | strDocThemeSlug = LoadedFile.strLoadedDocThemeSlug; 71 | strDocThemeName = LoadedFile.strLoadedDocThemeName; 72 | } 73 | 74 | let boolLockedDoc = false; 75 | if (FilePathInfo.strDirPath === N.ElectronFramework.Path.normalize(`${N.strAppPath}/documents`)) { 76 | boolLockedDoc = true; 77 | } 78 | 79 | new Document({ 80 | strPath: strPath, 81 | strContent: LoadedFile.strLoadedContent, 82 | strDocThemeSlug: strDocThemeSlug, 83 | strDocThemeName: strDocThemeName, 84 | boolLockedDoc: boolLockedDoc 85 | }); 86 | 87 | // Close the previous empty tab if there is only two tabs 88 | if (N.intCurrentDocs === 2) { 89 | const $PreviousTab = N.DocActive.$Tab.previousElementSibling; 90 | if ( ! $PreviousTab.classList.contains('doc-unsaved') && ! $PreviousTab.classList.contains('opened-doc')) { 91 | $PreviousTab.getElementsByClassName('close-tab')[0].click(); 92 | } 93 | } 94 | } 95 | } 96 | }); 97 | 98 | }; 99 | 100 | 101 | // Option : 102 | // - boolForceAction 103 | N.Functions.Documents.funcCloseAll = (Option) => { 104 | forEach(N.arrDocs, (Doc) => { 105 | if (Doc) { 106 | if (Option && Option.boolForceAction) { 107 | Doc.methClose({ boolForceAction: true }); 108 | } else { 109 | Doc.methClose(); 110 | } 111 | } 112 | }); 113 | 114 | if (N.$TabsList.getElementsByClassName('doc-unsaved').length) { 115 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'unsaved-docs' }); 116 | // returning a status to show a warning dialog at application closing 117 | return { boolUnsavedDocs: true }; 118 | } 119 | 120 | return { boolUnsavedDocs: false }; 121 | }; 122 | 123 | 124 | 125 | N.Functions.Documents.funcMenuAvailabilityCheck = () => { 126 | if (N.DocActive.strPath) { 127 | N.$MenuOpenDocFolder.classList.remove('disabled'); 128 | 129 | if (N.DocActive.$Tab.classList.contains('doc-unsaved')) { 130 | N.$MenuSave.classList.remove('disabled'); 131 | } else { 132 | N.$MenuSave.classList.add('disabled'); 133 | } 134 | } else { 135 | N.$MenuOpenDocFolder.classList.add('disabled'); 136 | N.$MenuSave.classList.add('disabled'); 137 | } 138 | 139 | if (N.DocActive.boolLocked) { 140 | N.$MenuSave.classList.add('disabled'); 141 | } 142 | }; 143 | -------------------------------------------------------------------------------- /app-dev/js/functions/IO.functions.js: -------------------------------------------------------------------------------- 1 | N.Functions.IO = {}; 2 | 3 | 4 | N.Functions.IO.funcIOErrorCheck = (Parameters) => { 5 | try { 6 | // Execute and return whatever funcToTry returns 7 | return Parameters.funcToTry(); 8 | } 9 | catch (Error) { 10 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'io-error' }); 11 | if (Parameters.funcCatch) { 12 | Parameters.funcCatch(); 13 | } 14 | 15 | console.error(Error); 16 | return false; 17 | } 18 | }; 19 | 20 | 21 | 22 | N.Functions.IO.funcReadFile = (Parameter) => { 23 | // Return whatever funcToTry returns 24 | return N.Functions.IO.funcIOErrorCheck({ funcToTry: () => { 25 | return N.ElectronFramework.Fs.readFileSync(Parameter.strPath, 'utf8'); 26 | } }); 27 | }; 28 | 29 | 30 | 31 | N.Functions.IO.funcLoadSettings = () => { 32 | N.Settings; 33 | 34 | try { 35 | N.Settings = JSON.parse( N.ElectronFramework.Fs.readFileSync(N.strSettingsFilePath, 'utf8') ); 36 | N.Functions.IO.funcSettingsLegacy(); 37 | } 38 | catch (Error) { 39 | N.Settings = { 40 | boolWindowFrame: false, 41 | boolFirstStart: true, 42 | strUIThemeSlug: '_ORIGINAL_white-room', 43 | strUILangSlug: 'english', 44 | boolAutoUpdateCheck: true, 45 | boolLinuxControlsRight: false 46 | }; 47 | 48 | N.Functions.IO.funcSaveSettings(); 49 | } 50 | }; 51 | 52 | 53 | 54 | N.Functions.IO.funcSaveSettings = () => { 55 | N.Functions.IO.funcIOErrorCheck({ funcToTry: () => { 56 | N.ElectronFramework.Fs.writeFileSync(N.strSettingsFilePath, JSON.stringify(N.Settings), 'utf8'); 57 | } }); 58 | }; 59 | 60 | 61 | 62 | // Settings object compatibility with previous versions of the app 63 | N.Functions.IO.funcSettingsLegacy = () => { 64 | let boolModified = false; 65 | 66 | if( ! N.Settings.hasOwnProperty('boolWindowFrame')) { 67 | N.Settings.boolWindowFrame = false; 68 | boolModified = true; 69 | } 70 | if( ! N.Settings.hasOwnProperty('boolLinuxControlsRight')) { 71 | N.Settings.boolLinuxControlsRight = false; 72 | boolModified = true; 73 | } 74 | 75 | if (boolModified) { 76 | N.Functions.IO.funcSaveSettings(); 77 | } 78 | }; 79 | 80 | 81 | 82 | N.Functions.IO.funcLoadDocThemesLibs = () => { 83 | N.Functions.IO.funcIOErrorCheck({ funcToTry: () => { 84 | const strDocThemesLibrariesPath = `${N.strAppPath}/themes/doc-themes/lib`; 85 | N.DocThemesLibraries = {}; 86 | 87 | // Lib 88 | N.DocThemesLibraries.strNormalizeCSS = N.ElectronFramework.Fs.readFileSync(`${strDocThemesLibrariesPath}/normalize.custom.min.css`, 'utf8'); 89 | 90 | N.DocThemesLibraries.strHighlightJS = N.ElectronFramework.Fs.readFileSync(`${strDocThemesLibrariesPath}/highlight.pack.custom.min.js`, 'utf8'); 91 | 92 | N.DocThemesLibraries.strEmojisCSS = N.ElectronFramework.Fs.readFileSync(`${strDocThemesLibrariesPath}/emojify.pack.min.css`, 'utf8'); 93 | N.DocThemesLibraries.strEmojisJS = N.ElectronFramework.Fs.readFileSync(`${strDocThemesLibrariesPath}/emojify.min.js`, 'utf8'); 94 | 95 | // Common code 96 | N.DocThemesLibraries.strDocumentCSS = N.ElectronFramework.Fs.readFileSync(`${strDocThemesLibrariesPath}/document.css`, 'utf8'); 97 | N.DocThemesLibraries.strDocumentJS = N.ElectronFramework.Fs.readFileSync(`${strDocThemesLibrariesPath}/document.js`, 'utf8'); 98 | } }); 99 | }; 100 | 101 | 102 | 103 | N.Functions.IO.funcLoadEmojis = () => { 104 | // Return whatever funcToTry returns 105 | return N.Functions.IO.funcIOErrorCheck({ funcToTry: () => { 106 | // https://github.com/github/gemoji/blob/master/db/emoji.json 107 | return JSON.parse( N.ElectronFramework.Fs.readFileSync(`${N.strAppPath}/json/emojis-list.json`, 'utf8') ); 108 | } }); 109 | }; 110 | 111 | 112 | 113 | // Parameters : 114 | // - strSavePath 115 | // - strFormat 116 | // - strDocThemeSlug 117 | // - $ContentEditable 118 | N.Functions.IO.funcSaveDoc = (Parameters) => { 119 | // Return whatever funcToTry returns 120 | return N.Functions.IO.funcIOErrorCheck({ funcToTry: () => { 121 | const $ContentEditableClone = Parameters.$ContentEditable.cloneNode(true); 122 | 123 | const strDocThemePath = `${N.strAppPath}/themes/doc-themes/themes-dir/${Parameters.strDocThemeSlug}`; 124 | let strDocContent = ''; 125 | 126 | if (Parameters.strFormat === 'HTML') { 127 | N.Functions.Content.funcClearHTMLForHTMLExport({ $ContentEditable: $ContentEditableClone }); 128 | 129 | strDocContent = N.ElectronFramework.Fs.readFileSync(`${strDocThemePath}/template.html`, 'utf8'); 130 | 131 | const strHighlightThemeCSS = N.ElectronFramework.Fs.readFileSync(`${strDocThemePath}/highlight.js-theme.min.css`, 'utf8'); 132 | const strDocThemeCSS = N.ElectronFramework.Fs.readFileSync(`${strDocThemePath}/style.css`, 'utf8'); 133 | 134 | const strDocThemeJS = N.ElectronFramework.Fs.readFileSync(`${strDocThemePath}/script.js`, 'utf8'); 135 | 136 | // Lib 137 | strDocContent = strDocContent.replace('/* __normalizecss */', N.DocThemesLibraries.strNormalizeCSS); 138 | 139 | if ($ContentEditableClone.getElementsByTagName('pre').length) { 140 | strDocContent = strDocContent.replace('/* __highlightthemecss */', strHighlightThemeCSS); 141 | strDocContent = strDocContent.replace('/* __highlightjs */', N.DocThemesLibraries.strHighlightJS); 142 | } else { 143 | strDocContent = strDocContent.replace('/* __highlightthemecss */', ''); 144 | strDocContent = strDocContent.replace('/* __highlightjs */', ''); 145 | } 146 | 147 | if ($ContentEditableClone.getElementsByClassName('emoji').length) { 148 | strDocContent = strDocContent.replace('/* __emojifycss */', N.DocThemesLibraries.strEmojisCSS); 149 | strDocContent = strDocContent.replace('/* __emojifyjs */', N.DocThemesLibraries.strEmojisJS); 150 | } else { 151 | strDocContent = strDocContent.replace('/* __emojifycss */', ''); 152 | strDocContent = strDocContent.replace('/* __emojifyjs */', ''); 153 | } 154 | 155 | // Common code 156 | strDocContent = strDocContent.replace('/* __documentcss */', N.DocThemesLibraries.strDocumentCSS); 157 | strDocContent = strDocContent.replace('/* __documentjs */', N.DocThemesLibraries.strDocumentJS); 158 | 159 | // Doc theme code 160 | strDocContent = strDocContent.replace('/* __docthemecss */', strDocThemeCSS); 161 | strDocContent = strDocContent.replace('/* __docthemejs */', strDocThemeJS); 162 | 163 | // HTML content replace at the end, to avoid regexp false detection with previous strings 164 | strDocContent = strDocContent.replace('', $ContentEditableClone.innerHTML); 165 | } 166 | 167 | 168 | else if (Parameters.strFormat === 'Markdown') { 169 | N.Functions.Content.funcClearHTMLForMarkdownExport({ $ContentEditable: $ContentEditableClone }); 170 | strDocContent = N.Functions.Utils.funcHTMLToMarkdown({ strContent: $ContentEditableClone.innerHTML }); 171 | } 172 | 173 | N.ElectronFramework.Fs.writeFileSync(Parameters.strSavePath, strDocContent, 'utf8'); 174 | 175 | return true; 176 | } }); 177 | }; 178 | 179 | 180 | 181 | // Parameters : 182 | // - strPath 183 | // - strFormat 184 | N.Functions.IO.funcLoadDoc = (Parameters) => { 185 | // Return whatever funcToTry returns 186 | return N.Functions.IO.funcIOErrorCheck({ funcToTry: () => { 187 | let strDocContent = N.ElectronFramework.Fs.readFileSync(Parameters.strPath, 'utf8'); 188 | 189 | if (Parameters.strFormat === 'HTML') { 190 | return { 191 | strLoadedDocThemeSlug: strDocContent.match('- __themeslug: (.*) -')[1], 192 | strLoadedDocThemeName: strDocContent.match('- __themename: (.*) -')[1], 193 | // RegExp -> [\s\S] any characters, even line breaks 194 | strLoadedContent: strDocContent.match(/
([\s\S]*)<\/main>/)[1] 195 | }; 196 | } 197 | else if (Parameters.strFormat === 'Markdown') { 198 | return { 199 | strLoadedContent: N.Functions.Utils.funcMarkdownToHTML({ strContent: strDocContent }) 200 | }; 201 | } 202 | } }); 203 | }; 204 | -------------------------------------------------------------------------------- /app-dev/js/functions/Remote.functions.js: -------------------------------------------------------------------------------- 1 | N.Functions.Remote = {}; 2 | 3 | 4 | N.Functions.Remote.funcGetUpdateInfo = (Parameters) => { 5 | reqwest({ 6 | // url: `${N.strAppPath}/../CHANGELOG.md`, 7 | url: 'https://raw.githubusercontent.com/n457/Uncolored/master/CHANGELOG.md', 8 | success: (strResponse) => { 9 | let boolUpdateAvailable = false; 10 | 11 | // Remotly, strResponse.response doesn't work, use strResponse instead 12 | let strRemoteContent = N.Functions.Utils.funcMarkdownToHTML({ strContent: strResponse }); 13 | 14 | if (strRemoteContent) { 15 | const strRemoteLastestVersion = strRemoteContent.match('- currentversion: (.*) -')[1]; 16 | 17 | if (versionCompare(strRemoteLastestVersion, N.strAppVersion) > 0) { 18 | boolUpdateAvailable = true; 19 | } 20 | 21 | Parameters.funcOnSuccess.call(null, { 22 | boolUpdateAvailable: boolUpdateAvailable, 23 | strRemoteLastestVersion: strRemoteLastestVersion, 24 | strRemoteReleaseNotesHTML: strRemoteContent 25 | }); 26 | } 27 | }, 28 | error: (Error) => { 29 | console.error(Error); 30 | Parameters.funcOnError.call(null, Error); 31 | } 32 | }); 33 | }; 34 | 35 | 36 | N.Functions.Remote.funcGetPriorityInfo = (Parameters) => { 37 | reqwest({ 38 | // url: `${N.strAppPath}/../project/docs/priority-info.md`, 39 | url: 'https://raw.githubusercontent.com/n457/Uncolored/master/project/docs/priority-info.md', 40 | success: (strResponse) => { 41 | // Remotly, strResponse.response doesn't work, use strResponse instead 42 | let strRemoteContent = N.Functions.Utils.funcMarkdownToHTML({ strContent: strResponse }); 43 | 44 | if (strRemoteContent) { 45 | Parameters.funcOnSuccess.call(null, { 46 | strRemotePriorityInfoHTML: strRemoteContent 47 | }); 48 | } 49 | }, 50 | error: (Error) => { 51 | console.error(Error); 52 | Parameters.funcOnError.call(null, Error); 53 | } 54 | }); 55 | }; 56 | -------------------------------------------------------------------------------- /app-dev/js/functions/Toolbar.functions.js: -------------------------------------------------------------------------------- 1 | N.Functions.Toolbar = {}; 2 | 3 | 4 | N.Functions.Toolbar.funcAutoPosition = () => { 5 | const Range = lightrange.getSelectionInfo(); 6 | const intToolsWidth = N.$Toolbar.offsetWidth; 7 | 8 | let intToolsTop = Range.yStart - N.$Toolbar.offsetHeight - 4; 9 | // 38px : .tab-bar height 10 | if (intToolsTop < 38) { 11 | intToolsTop = Range.yStart + Range.height + 5; 12 | } 13 | N.$Toolbar.style.top = intToolsTop + 'px'; 14 | 15 | 16 | let intToolsLeft = Range.xStart + (Range.width / 2 - intToolsWidth / 2); 17 | if (intToolsLeft < 7) { 18 | intToolsLeft = 7; 19 | } 20 | else { 21 | const intWindowRightLimit = window.innerWidth - 12; 22 | if (intToolsLeft + intToolsWidth > intWindowRightLimit) { 23 | intToolsLeft = intWindowRightLimit - intToolsWidth; 24 | } 25 | } 26 | N.$Toolbar.style.left = intToolsLeft + 'px'; 27 | 28 | 29 | // To know if the selection is a range or a caret, for displaying the toolbar or not 30 | return Range; 31 | }; 32 | 33 | 34 | N.Functions.Toolbar.funcResetPosition = () => { 35 | N.$Toolbar.style.top = '134px'; 36 | N.$Toolbar.style.left = '7px'; 37 | }; 38 | 39 | 40 | N.Functions.Toolbar.funcView = () => { 41 | N.$Toolbar.dataset.view = 'tools-list'; 42 | 43 | if (N.Functions.Toolbar.funcAutoPosition().characters) { 44 | N.$Toolbar.classList.add('active'); 45 | } else { 46 | N.$Toolbar.classList.remove('active'); 47 | } 48 | }; 49 | 50 | 51 | N.Functions.Toolbar.funcResetView = () => { 52 | lightrange.restoreSelection(N.DocActive.LastSelection); 53 | N.$Toolbar.dataset.view = 'tools-list'; 54 | N.Functions.Toolbar.funcAutoPosition(); 55 | }; 56 | 57 | 58 | N.Functions.Toolbar.funcCheckTools = () => { 59 | forEach(N.$Toolbar.getElementsByTagName('li'), ($Button) => { 60 | if ($Button.dataset.cmd) { 61 | if ($Button.dataset.cmdcheck) { 62 | if (document.queryCommandState($Button.dataset.cmdcheck)) { 63 | $Button.classList.add('active'); 64 | } else { 65 | $Button.classList.remove('active'); 66 | } 67 | } else { 68 | if (document.queryCommandState($Button.dataset.cmd)) { 69 | $Button.classList.add('active'); 70 | } else { 71 | $Button.classList.remove('active'); 72 | } 73 | } 74 | } else if ($Button.dataset.tag) { 75 | if (document.queryCommandValue('formatBlock') === $Button.dataset.tag) { 76 | $Button.classList.add('active'); 77 | } else { 78 | $Button.classList.remove('active'); 79 | } 80 | } 81 | }); 82 | 83 | if (N.$ToolQuote.classList.contains('active') || N.$ToolH1.classList.contains('active') || N.$ToolH2.classList.contains('active') || N.$ToolH3.classList.contains('active') || N.$ToolH4.classList.contains('active') || N.$ToolH5.classList.contains('active') || N.$ToolH6.classList.contains('active') ) { 84 | N.$ToolUnorderedList.classList.add('disabled'); 85 | N.$ToolOrderedList.classList.add('disabled'); 86 | } else { 87 | N.$ToolUnorderedList.classList.remove('disabled'); 88 | N.$ToolOrderedList.classList.remove('disabled'); 89 | } 90 | 91 | if (N.$ToolUnorderedList.classList.contains('active') || N.$ToolOrderedList.classList.contains('active')) { 92 | N.$ToolQuote.classList.add('disabled'); 93 | N.$ToolH1.classList.add('disabled'); 94 | N.$ToolH2.classList.add('disabled'); 95 | N.$ToolH3.classList.add('disabled'); 96 | N.$ToolH4.classList.add('disabled'); 97 | N.$ToolH5.classList.add('disabled'); 98 | N.$ToolH6.classList.add('disabled'); 99 | } else { 100 | N.$ToolQuote.classList.remove('disabled'); 101 | N.$ToolH1.classList.remove('disabled'); 102 | N.$ToolH2.classList.remove('disabled'); 103 | N.$ToolH3.classList.remove('disabled'); 104 | N.$ToolH4.classList.remove('disabled'); 105 | N.$ToolH5.classList.remove('disabled'); 106 | N.$ToolH6.classList.remove('disabled'); 107 | } 108 | }; 109 | -------------------------------------------------------------------------------- /app-dev/js/functions/Utils.functions.js: -------------------------------------------------------------------------------- 1 | N.Functions.Utils = {}; 2 | 3 | 4 | N.Functions.Utils.funcSetDOMAppName = () => { 5 | forEach(document.getElementsByClassName('app-name'), ($Element) => { 6 | $Element.textContent = N.strAppName; 7 | }); 8 | }; 9 | 10 | 11 | // Parameter : 12 | // - strPath 13 | N.Functions.Utils.funcGetFilePathInfo = (Parameter) => { 14 | const strExtensionLowerCase = N.ElectronFramework.Path.extname(Parameter.strPath).toLowerCase(); 15 | 16 | let strFormat = ''; 17 | if (['.html', '.htm'].indexOf(strExtensionLowerCase) !== -1) { 18 | strFormat = 'HTML'; 19 | } else if (['.md', '.markdown'].indexOf(strExtensionLowerCase) !== -1) { 20 | strFormat = 'Markdown'; 21 | } 22 | 23 | return { 24 | strName: N.ElectronFramework.Path.basename(Parameter.strPath), 25 | strDirPath: N.ElectronFramework.Path.dirname(Parameter.strPath), 26 | strFormat: strFormat 27 | }; 28 | }; 29 | 30 | 31 | // Parameters : 32 | // - strEvent 33 | // - $Element 34 | N.Functions.Utils.funcTriggerEvent = (Parameters) => { 35 | Parameters.$Element.dispatchEvent(new Event(Parameters.strEvent)); 36 | }; 37 | 38 | 39 | // http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex 40 | // https://github.com/sindresorhus/escape-string-regexp 41 | // Parameter : 42 | // - strRegExp 43 | N.Functions.Utils.funcEscapeStringRegexp = (Parameter) => { 44 | return Parameter.strRegExp.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'); 45 | }; 46 | 47 | 48 | N.Functions.Utils.funcNoCacheSuffix = () => { 49 | // http://stackoverflow.com/questions/728616/disable-cache-for-some-images/6116854#6116854 50 | return '?nocache2650=' + new Date().getTime(); 51 | }; 52 | 53 | 54 | N.Functions.Utils.funcMarkdownToHTML = (Parameter) => { 55 | try { 56 | return marked(Parameter.strContent); 57 | } catch (Error) { 58 | console.error(Error); 59 | // Error during the convertion. 60 | return false; 61 | } 62 | }; 63 | 64 | N.Functions.Utils.funcHTMLToMarkdown = (Parameter) => { 65 | try { 66 | return toMarkdown(Parameter.strContent); 67 | } catch (Error) { 68 | console.error(Error); 69 | // Error during the convertion. 70 | return false; 71 | } 72 | }; 73 | -------------------------------------------------------------------------------- /app-dev/js/functions/Window.functions.js: -------------------------------------------------------------------------------- 1 | N.Functions.Window = {}; 2 | 3 | 4 | N.Functions.Window.funcClose = () => { 5 | window.close(); 6 | }; 7 | 8 | N.Functions.Window.funcMinimize = () => { 9 | N.ElectronFramework.CurrentWindow.minimize(); 10 | }; 11 | 12 | N.Functions.Window.funcToggleMaximize = () => { 13 | if (N.ElectronFramework.CurrentWindow.isMaximized()) { 14 | N.ElectronFramework.CurrentWindow.unmaximize(); 15 | } else { 16 | N.ElectronFramework.CurrentWindow.maximize(); 17 | } 18 | }; 19 | 20 | N.Functions.Window.funcToggleFullscreen = () => { 21 | if (N.ElectronFramework.CurrentWindow.isFullScreen()) { 22 | N.ElectronFramework.CurrentWindow.setFullScreen(false); 23 | document.body.classList.remove('fullscreen'); 24 | } else { 25 | N.ElectronFramework.CurrentWindow.setFullScreen(true); 26 | document.body.classList.add('fullscreen'); 27 | } 28 | }; 29 | 30 | N.Functions.Window.funcToggleAlwaysOnTop = () => { 31 | if (N.ElectronFramework.CurrentWindow.isAlwaysOnTop()) { 32 | N.ElectronFramework.CurrentWindow.setAlwaysOnTop(false); 33 | document.body.classList.remove('always-on-top'); 34 | } else { 35 | N.ElectronFramework.CurrentWindow.setAlwaysOnTop(true); 36 | document.body.classList.add('always-on-top'); 37 | } 38 | }; 39 | 40 | 41 | N.Functions.Window.funcPlatformDetect = () => { 42 | if (process.platform === 'win32') { 43 | N.strOS = 'windows'; 44 | document.body.classList.add('windows-os'); 45 | } else if (process.platform === 'darwin') { 46 | N.strOS = 'osx'; 47 | document.body.classList.add('osx-os'); 48 | } else { 49 | N.strOS = 'linux'; 50 | document.body.classList.add('linux-os'); 51 | } 52 | }; 53 | -------------------------------------------------------------------------------- /app-dev/js/init.js: -------------------------------------------------------------------------------- 1 | N.strAppName = '(Un)colored'; 2 | N.strAppVersion = N.ElectronFramework.App.getVersion(); 3 | 4 | N.strAppPath = N.ElectronFramework.App.getAppPath(); 5 | N.strAppExePath = N.ElectronFramework.App.getPath('exe'); 6 | 7 | N.strAppUserData = N.ElectronFramework.App.getPath('userData'); 8 | N.strSettingsFilePath = `${N.strAppUserData}/settings.min.json`; 9 | 10 | 11 | N.Functions.IO.funcLoadSettings(); 12 | N.Functions.IO.funcLoadDocThemesLibs(); 13 | 14 | 15 | N.$Header = document.getElementsByClassName('mdl-layout__header')[0]; 16 | N.$WindowControls = document.getElementsByClassName('window-controls')[0]; 17 | N.$TabsList = document.getElementsByClassName('tabs-list')[0]; 18 | N.$TabModel = document.querySelector('#tab-model-container_unc2741 li'); 19 | 20 | N.$MenuOpenDocFolder = document.getElementById('menu-open-doc-folder_unc2741'); 21 | N.$MenuSave = document.getElementById('menu-save_unc2741'); 22 | N.$MenuQuickGuide = document.getElementById('menu-quick-guide_unc2741'); 23 | 24 | N.$Workspace = document.getElementsByClassName('mdl-layout__content')[0]; 25 | N.$ContentContainerModel = N.$Workspace.getElementsByClassName('content-container')[0]; 26 | N.$GlobalUnsavedMark = document.getElementById('global-unsaved-mark_unc2741'); 27 | 28 | N.$Toolbar = document.getElementById('toolbar_unc2741'); 29 | // Need to be here because of disabled tool state 30 | N.$ToolQuote = document.getElementById('tool-quote_unc2741'); 31 | N.$ToolUnorderedList = document.getElementById('tool-unordered-list_unc2741'); 32 | N.$ToolOrderedList = document.getElementById('tool-ordered-list_unc2741'); 33 | N.$ToolH1 = document.getElementById('tool-h1_unc2741'); 34 | N.$ToolH2 = document.getElementById('tool-h2_unc2741'); 35 | N.$ToolH3 = document.getElementById('tool-h3_unc2741'); 36 | N.$ToolH4 = document.getElementById('tool-h4_unc2741'); 37 | N.$ToolH5 = document.getElementById('tool-h5_unc2741'); 38 | N.$ToolH6 = document.getElementById('tool-h6_unc2741'); 39 | 40 | N.$ContextMenu = document.getElementById('context-menu_unc2741'); 41 | 42 | N.$SearchDialog = document.getElementById('search-dialog_unc2741'); 43 | N.$SearchInput = document.getElementById('search-field_unc2741'); 44 | N.$ReplaceInput = document.getElementById('replace-field_unc2741'); 45 | N.$SearchResultsTotal = N.$SearchDialog.getElementsByClassName('search-results-total')[0]; 46 | 47 | N.$TableContentDialog = document.getElementById('table-content-dialog_unc2741'); 48 | N.$HeadingsList = N.$TableContentDialog.getElementsByClassName('headings-list')[0]; 49 | N.$TableContentHeadingModel = document.querySelector('#heading-item-model-container_unc2741 li'); 50 | 51 | N.$DocInfoDialog = document.getElementById('doc-info-dialog_unc2741'); 52 | N.$DocInfoThemeName = N.$DocInfoDialog.getElementsByClassName('doc-theme-name')[0]; 53 | N.$DocInfoParagraphs = N.$DocInfoDialog.getElementsByClassName('paragraphs-number')[0]; 54 | N.$DocInfoSentences = N.$DocInfoDialog.getElementsByClassName('sentences-number')[0]; 55 | N.$DocInfoWords = N.$DocInfoDialog.getElementsByClassName('words-number')[0]; 56 | N.$DocInfoCharacters = N.$DocInfoDialog.getElementsByClassName('characters-number')[0]; 57 | N.$DocInfoCharactersSpaces = N.$DocInfoDialog.getElementsByClassName('characters-spaces-number')[0]; 58 | 59 | N.$AboutDialog = document.getElementById('about-dialog_unc2741'); 60 | 61 | N.$UnsavedDocsDialog = document.getElementById('unsaved-docs-dialog_unc2741'); 62 | 63 | 64 | N.arrDocs = []; 65 | N.DocActive; 66 | 67 | N.intDocIDsCount = -1; 68 | N.intCurrentDocs = 0; 69 | 70 | N.strLastHTMLCopied = ''; 71 | N.strLastHTMLPasted = ''; 72 | N.strLastHTMLDragged = ''; 73 | N.timerInput; 74 | N.timerClipboard; 75 | // N.boolCanDrop always "false" by default 76 | N.boolCanDrop = false; 77 | 78 | N.LastContextMenuElementInfo; 79 | N.RegExpHeading = new RegExp(/^h\d$/, 'i'); 80 | 81 | N.$DialogActive; 82 | N.strDialogActiveSlug = ''; 83 | 84 | 85 | 86 | N.Functions.Utils.funcSetDOMAppName(); 87 | 88 | 89 | Mousetrap.bindGlobal('tab', () => { 90 | if (document.activeElement !== N.$SearchInput) { 91 | return false; 92 | } 93 | }); 94 | Mousetrap.bindGlobal('shift+tab', () => { 95 | if (document.activeElement !== N.$ReplaceInput) { 96 | return false; 97 | } 98 | }); 99 | 100 | emojify.setConfig({ mode: 'data-uri' }); 101 | -------------------------------------------------------------------------------- /app-dev/js/modules.js: -------------------------------------------------------------------------------- 1 | // Only one namespace 2 | const N = {}; 3 | N.ElectronFramework = {}; 4 | N.Functions = {}; 5 | 6 | N.boolDevMode = false; 7 | 8 | N.ElectronFramework.Electron = require('electron'); 9 | 10 | // Uses to access main process things. More info here : 11 | // http://electron.atom.io/docs/v0.37.2/api/remote/ 12 | N.ElectronFramework.Remote = N.ElectronFramework.Electron.remote; 13 | 14 | // Get access to the BrowserWindow object which this web page belongs to. 15 | N.ElectronFramework.CurrentWindow = N.ElectronFramework.Remote.getCurrentWindow(); 16 | 17 | // Hide application menu bar 18 | N.ElectronFramework.CurrentWindow.setMenuBarVisibility(false); 19 | 20 | if (N.boolDevMode) { 21 | N.ElectronFramework.CurrentWindow.webContents.openDevTools(); 22 | } 23 | 24 | // Module to control application life. 25 | N.ElectronFramework.App = N.ElectronFramework.Remote.app; 26 | // Used to handle native dialog windows 27 | N.ElectronFramework.Dialog = N.ElectronFramework.Remote.dialog; 28 | 29 | // Used to trigger native commands 30 | N.ElectronFramework.Shell = N.ElectronFramework.Electron.shell; 31 | // Used to handle clipboard data 32 | N.ElectronFramework.Clipboard = N.ElectronFramework.Electron.clipboard; 33 | 34 | // "fs" for "File System" : used to read and write files on disks. 35 | N.ElectronFramework.Fs = require('fs'); 36 | 37 | // manipulate path string 38 | N.ElectronFramework.Path = require('path'); 39 | -------------------------------------------------------------------------------- /app-dev/js/remote-check.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 3 | const $AppCurrentVersion = N.$AboutDialog.getElementsByClassName('app-version')[0]; 4 | 5 | const $NewUpdateDialog = document.getElementById('new-update-dialog_unc2741'); 6 | const $AppRemoteVersion = $NewUpdateDialog.getElementsByClassName('last-version')[0]; 7 | const $RemoteReleaseNotes = $NewUpdateDialog.getElementsByClassName('release-notes')[0]; 8 | 9 | const $RemotePriorityInfo = document.getElementById('priority-info-dialog_unc2741').getElementsByClassName('mdl-card__supporting-text')[0]; 10 | 11 | 12 | const funcUpdateCheck = () => { 13 | N.$AboutDialog.dataset.state = 'loading'; 14 | 15 | N.Functions.Remote.funcGetUpdateInfo({ 16 | funcOnSuccess: (UpdateCheckResult) => { 17 | if (UpdateCheckResult.boolUpdateAvailable) { 18 | $AppRemoteVersion.textContent = UpdateCheckResult.strRemoteLastestVersion; 19 | $RemoteReleaseNotes.innerHTML = UpdateCheckResult.strRemoteReleaseNotesHTML; 20 | N.$AboutDialog.dataset.state = 'new-update'; 21 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'new-update' }); 22 | } else { 23 | N.$AboutDialog.dataset.state = 'up-to-date'; 24 | } 25 | }, 26 | funcOnError: (Error) => { 27 | N.$AboutDialog.dataset.state = 'error'; 28 | } 29 | }); 30 | }; 31 | 32 | const funcPriorityInfoCheck = () => { 33 | N.Functions.Remote.funcGetPriorityInfo({ 34 | funcOnSuccess: (PriorityInfoResult) => { 35 | if (PriorityInfoResult.strRemotePriorityInfoHTML !== '') { 36 | $RemotePriorityInfo.innerHTML = PriorityInfoResult.strRemotePriorityInfoHTML; 37 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'priority-info' }); 38 | } 39 | }, 40 | funcOnError: (Error) => {} 41 | }); 42 | }; 43 | 44 | 45 | N.$AboutDialog.getElementsByClassName('show-update-dialog')[0].addEventListener('click', () => { 46 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'new-update' }); 47 | }); 48 | 49 | N.$AboutDialog.getElementsByClassName('recheck')[0].addEventListener('click', () => { 50 | funcUpdateCheck(); 51 | }); 52 | 53 | 54 | $AppCurrentVersion.textContent = N.strAppVersion; 55 | 56 | 57 | if (N.Settings.boolAutoUpdateCheck) { 58 | funcUpdateCheck(); 59 | } 60 | 61 | let timerPriorityInfoCheck; 62 | timerPriorityInfoCheck = setTimeout(() => { 63 | funcPriorityInfoCheck(); 64 | clearTimeout(timerPriorityInfoCheck); 65 | }, 4000); 66 | 67 | })(); 68 | -------------------------------------------------------------------------------- /app-dev/js/save.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 3 | const $SaveAsDialog = document.getElementById('save-as-dialog_unc2741'); 4 | 5 | const $PathField = document.getElementById('save-path-field_unc2741'); 6 | let strPathFieldValue = ''; 7 | const $PathFieldContainer = $PathField.parentNode; 8 | const $PathFieldTooltip = $SaveAsDialog.getElementsByClassName('mdl-tooltip')[0]; 9 | 10 | 11 | const $DocThemeListLabel = $SaveAsDialog.getElementsByClassName('preview-list-label')[0]; 12 | // For populating document themes item list 13 | const $DocThemeList = $SaveAsDialog.getElementsByClassName('preview-list')[0]; 14 | const $DocThemeItem = $DocThemeList.getElementsByClassName('preview-list-item')[0]; 15 | 16 | let FilePathInfo = {}; 17 | 18 | 19 | 20 | const funcSaveDocController = (Options) => { 21 | if ( (N.DocActive.$Tab.classList.contains('doc-unsaved') && ! N.DocActive.boolLocked) || (Options && Options.boolSaveAsMode) ) { 22 | if (N.DocActive.strPath || (Options && Options.boolSaveAsMode) ) { 23 | let SaveParameters = {}; 24 | 25 | if (Options && Options.boolSaveAsMode) { 26 | SaveParameters = { 27 | strSavePath: Options.strSavePath, 28 | strFormat: Options.strFormat, 29 | strDocThemeSlug: Options.strDocThemeSlug, 30 | $ContentEditable: N.DocActive.$ContentEditable 31 | }; 32 | } else { 33 | SaveParameters = { 34 | strSavePath: N.DocActive.strPath, 35 | strFormat: N.DocActive.strFormat, 36 | strDocThemeSlug: N.DocActive.strDocThemeSlug, 37 | $ContentEditable: N.DocActive.$ContentEditable 38 | }; 39 | } 40 | 41 | const boolSaveResult = N.Functions.IO.funcSaveDoc(SaveParameters); 42 | 43 | if (boolSaveResult) { 44 | N.DocActive.strLastSavedContent = N.DocActive.$ContentEditable.innerHTML; 45 | N.DocActive.$Tab.classList.remove('doc-unsaved'); 46 | N.$GlobalUnsavedMark.classList.remove('active'); 47 | } 48 | 49 | } else { 50 | N.Functions.Dialogs.funcShow({ strDialogSlug: 'save-as' }); 51 | } 52 | } 53 | }; 54 | 55 | 56 | 57 | N.$MenuSave.addEventListener('click', () => { 58 | funcSaveDocController(); 59 | }); 60 | Mousetrap.bindGlobal('mod+s', () => { 61 | // We don't force the N.$MenuSave.click() here to keep the save as dialog display system. 62 | funcSaveDocController(); 63 | }); 64 | 65 | 66 | 67 | 68 | 69 | // Handling document themes 70 | 71 | const arrFolderNames = N.ElectronFramework.Fs.readdirSync(`${N.strAppPath}/themes/doc-themes/themes-dir/`); 72 | // For each interface theme 73 | forEach(arrFolderNames, (strFolderName) => { 74 | // Create a document theme list item 75 | const $DocThemeItemClone = $DocThemeItem.cloneNode(true); 76 | $DocThemeItemClone.removeAttribute('hidden'); 77 | $DocThemeItemClone.dataset.slug = strFolderName; 78 | 79 | // Trying to read the HTML document theme file. 80 | const strDocThemeHTML = N.Functions.IO.funcReadFile({ strPath: `${N.strAppPath}/themes/doc-themes/themes-dir/${strFolderName}/template.html` }); 81 | 82 | if (strDocThemeHTML) { 83 | // Getting the document theme name inside the file content 84 | $DocThemeItemClone.getElementsByClassName('mdl-list__item-primary-content')[0].textContent = strDocThemeHTML.match('- __themename: (.*) -')[1]; 85 | $DocThemeItemClone.getElementsByTagName('img')[0].src = `../themes/doc-themes/themes-dir/${strFolderName}/preview.png`; 86 | 87 | // Set document theme on item click 88 | $DocThemeItemClone.addEventListener('click', () => { 89 | const $DocThemeSelected = $DocThemeList.getElementsByClassName('active')[0]; 90 | if ($DocThemeSelected) { 91 | $DocThemeSelected.classList.remove('active'); 92 | } 93 | $DocThemeItemClone.classList.add('active'); 94 | }); 95 | 96 | $DocThemeList.appendChild($DocThemeItemClone); 97 | } 98 | }); 99 | 100 | // Selecting the corresponding document theme in the list 101 | $DocThemeList.querySelector('li:nth-child(2)').click(); 102 | 103 | 104 | 105 | 106 | 107 | 108 | $PathField.addEventListener('click', () => { 109 | const strExtensionLowerCase = N.ElectronFramework.Path.extname(N.DocActive.$FileName.textContent).toLowerCase(); 110 | let strDefaultPathExtension = ''; 111 | 112 | if (['.html', '.htm', '.md', '.markdown'].indexOf(strExtensionLowerCase) === -1) { 113 | strDefaultPathExtension = '.html'; 114 | } 115 | 116 | N.ElectronFramework.Dialog.showSaveDialog({ 117 | defaultPath: N.DocActive.$FileName.textContent + strDefaultPathExtension, 118 | filters: [ 119 | {name: 'HTML document', extensions: ['html', 'htm']}, 120 | {name: 'Markdown document', extensions: ['md', 'markdown']} 121 | ] 122 | }, (strSavePath) => { 123 | // strSavePath is undefined if the action is cancelled by the user 124 | if (strSavePath) { 125 | $PathField.value = strSavePath; 126 | strPathFieldValue = strSavePath; 127 | $PathFieldTooltip.textContent = strSavePath; 128 | 129 | FilePathInfo = N.Functions.Utils.funcGetFilePathInfo({ strPath: strSavePath }); 130 | if (FilePathInfo.strFormat === 'HTML') { 131 | $DocThemeListLabel.removeAttribute('hidden'); 132 | $DocThemeList.removeAttribute('hidden'); 133 | } else if (FilePathInfo.strFormat === 'Markdown') { 134 | $DocThemeListLabel.setAttribute('hidden', null); 135 | $DocThemeList.setAttribute('hidden', null); 136 | } 137 | 138 | } else { 139 | $PathField.value = ''; 140 | strPathFieldValue = ''; 141 | $PathFieldTooltip.textContent = ''; 142 | } 143 | 144 | N.Functions.Utils.funcTriggerEvent({ 145 | strEvent: 'input', 146 | $Element: $PathField 147 | }); 148 | 149 | }); 150 | 151 | }); 152 | 153 | 154 | // Save button 155 | $SaveAsDialog.getElementsByClassName('save-button')[0].addEventListener('click', () => { 156 | if (strPathFieldValue) { 157 | $PathFieldContainer.classList.remove('is-invalid'); 158 | N.Functions.Dialogs.funcForceClose(); 159 | 160 | let strDocThemeSlug = null; 161 | let strDocThemeName = null; 162 | if (FilePathInfo.strFormat === 'HTML') { 163 | const $DocThemeSelected = $DocThemeList.getElementsByClassName('active')[0]; 164 | strDocThemeSlug = $DocThemeSelected.dataset.slug; 165 | strDocThemeName = $DocThemeSelected.textContent; 166 | } 167 | 168 | funcSaveDocController({ 169 | boolSaveAsMode: true, 170 | strSavePath: strPathFieldValue, 171 | strFormat: FilePathInfo.strFormat, 172 | strDocThemeSlug: strDocThemeSlug 173 | }); 174 | N.DocActive.methSetTabData({ 175 | strPath: strPathFieldValue, 176 | strDocThemeSlug: strDocThemeSlug, 177 | strDocThemeName: strDocThemeName 178 | }); 179 | 180 | N.DocActive.methShow(); 181 | 182 | } else { 183 | $PathFieldContainer.classList.add('is-invalid'); 184 | } 185 | }); 186 | 187 | })(); 188 | -------------------------------------------------------------------------------- /app-dev/js/search.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 3 | const $SearchInputContainer = N.$SearchInput.parentNode; 4 | const $ReplaceInputContainer = N.$ReplaceInput.parentNode; 5 | const $SwitchCaseSensitive = N.$SearchDialog.querySelector('[for="switch-search-case-sensitive_unc2741"]'); 6 | 7 | let LastSearchRegExp; 8 | let Search = {}; 9 | 10 | 11 | 12 | const funcMakeSearchRegExp = () => { 13 | let strRegExpFlags = 'g'; 14 | if ( ! $SwitchCaseSensitive.classList.contains('is-checked')) { 15 | strRegExpFlags += 'i'; 16 | } 17 | LastSearchRegExp = new RegExp(`(${N.Functions.Utils.funcEscapeStringRegexp({ strRegExp: N.$SearchInput.value })})`, strRegExpFlags); 18 | }; 19 | 20 | 21 | const funcFindAll = () => { 22 | Search = findAndReplaceDOMText(N.DocActive.$ContentEditable, { 23 | find: LastSearchRegExp, 24 | 25 | replace: function(Portion, Match) { 26 | const $Searchresult = document.createElement('searchresult'); 27 | $Searchresult.dataset.match = Match.index; 28 | $Searchresult.textContent = Portion.text; 29 | return $Searchresult; 30 | } 31 | }); 32 | }; 33 | 34 | 35 | const funcReplaceAll = () => { 36 | let strReplace = N.$ReplaceInput.value; 37 | if (! strReplace) { 38 | // findAndReplaceDOMText() doesn't accept empty value, but replace ' ' by an empty value 39 | strReplace = ' '; 40 | } 41 | 42 | findAndReplaceDOMText(N.DocActive.$ContentEditable, { 43 | find: LastSearchRegExp, 44 | replace: strReplace 45 | }); 46 | }; 47 | 48 | 49 | const funcFindReplaceController = (Parameter) => { 50 | if (N.$SearchInput.value) { 51 | $SearchInputContainer.classList.remove('is-invalid'); 52 | 53 | if (Parameter.strMode === 'find-all') { 54 | N.Functions.Content.funcCleanSearch({ Document: N.DocActive }); 55 | funcMakeSearchRegExp(); 56 | funcFindAll(); 57 | N.$SearchResultsTotal.textContent = Search.reverts.length; 58 | } 59 | 60 | else if (Parameter.strMode === 'replace-all') { 61 | N.Functions.Content.funcCleanSearch({ Document: N.DocActive }); 62 | funcMakeSearchRegExp(); 63 | funcReplaceAll(); 64 | 65 | N.Functions.Utils.funcTriggerEvent({ 66 | strEvent: 'input', 67 | $Element: N.DocActive.$ContentEditable 68 | }); 69 | 70 | N.$SearchResultsTotal.textContent = '0'; 71 | } 72 | } 73 | else { 74 | $SearchInputContainer.classList.add('is-invalid'); 75 | } 76 | }; 77 | 78 | 79 | N.$SearchDialog.getElementsByClassName('find-all-button')[0].addEventListener('click', () => { 80 | funcFindReplaceController({ strMode: 'find-all' }); 81 | }); 82 | N.$SearchDialog.getElementsByClassName('replace-all-button')[0].addEventListener('click', () => { 83 | funcFindReplaceController({ strMode: 'replace-all' }); 84 | }); 85 | 86 | })(); 87 | -------------------------------------------------------------------------------- /app-dev/js/settings.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 3 | const $SettingsDialog = document.getElementById('settings-dialog_unc2741'); 4 | 5 | const $UIThemeSheet = document.getElementById('ui-theme-sheet_unc2741'); 6 | 7 | // For populating UI language select list 8 | const $SelectUILangInput = document.getElementById('select-ui-lang_unc2741'); 9 | const $SelectUILang = $SettingsDialog.querySelector('.mdl-menu[for="select-ui-lang_unc2741"]'); 10 | const $SelectUILangItem = $SelectUILang.getElementsByClassName('mdl-menu__item')[0]; 11 | 12 | const $SwitchAutoUpdate = $SettingsDialog.querySelector('[for="switch-auto-update-check_unc2741"]'); 13 | const $SwitchAutoUpdateInput = $SwitchAutoUpdate.getElementsByTagName('input')[0]; 14 | 15 | const $SwitchWindowFrame = $SettingsDialog.querySelector('[for="switch-window-frame_unc2741"]'); 16 | const $SwitchWindowFrameInput = $SwitchWindowFrame.getElementsByTagName('input')[0]; 17 | 18 | const $SwitchLinuxControlsRight = $SettingsDialog.querySelector('[for="switch-linux-controls-right_unc2741"]'); 19 | const $SwitchLinuxControlsRightInput = $SwitchLinuxControlsRight.getElementsByTagName('input')[0]; 20 | 21 | // For populating UI themes select list 22 | const $UIThemesList = $SettingsDialog.getElementsByClassName('preview-list')[0]; 23 | const $UIThemesListItem = $UIThemesList.getElementsByClassName('preview-list-item')[0]; 24 | 25 | 26 | 27 | 28 | const funcResetSettings = () => { 29 | if (N.Settings.strUILangSlug !== 'english') { 30 | $SelectUILang.querySelector('[data-slug="english"]').click(); 31 | } 32 | if ( ! N.Settings.boolAutoUpdateCheck) { 33 | $SwitchAutoUpdateInput.click(); 34 | } 35 | if (N.Settings.boolWindowFrame) { 36 | $SwitchWindowFrameInput.click(); 37 | } 38 | if (N.Settings.boolLinuxControlsRight) { 39 | $SwitchLinuxControlsRightInput.click(); 40 | } 41 | if (N.Settings.strUIThemeSlug !== '_ORIGINAL_white-room') { 42 | $UIThemesList.querySelector('[data-slug="_ORIGINAL_white-room"]').click(); 43 | } 44 | }; 45 | 46 | 47 | // TODO [(Un)colored] : Lang switch function (no need to reload) 48 | const funcSetUIThemeLang = () => { 49 | 50 | }; 51 | 52 | 53 | 54 | 55 | // Handling UI languages 56 | 57 | const arrFileNames = N.ElectronFramework.Fs.readdirSync(`${N.strAppPath}/lang/`); 58 | // For each UI language 59 | forEach(arrFileNames, (strFileName) => { 60 | // Create a UI language list item 61 | const $SelectUILangItemClone = $SelectUILangItem.cloneNode(true); 62 | $SelectUILangItemClone.removeAttribute('hidden'); 63 | $SelectUILangItemClone.dataset.slug = strFileName.replace('.xml', ''); 64 | 65 | // Trying to read the UI languages XML file. 66 | const strUILangXML = N.Functions.IO.funcReadFile({ strPath: `${N.strAppPath}/lang/${strFileName}` }); 67 | 68 | if (strUILangXML) { 69 | const Parser = new DOMParser(); 70 | const $UILangXML = Parser.parseFromString(strUILangXML, 'text/xml'); 71 | 72 | // Getting the UI language name inside the file content 73 | $SelectUILangItemClone.textContent = $UILangXML.getElementsByTagName('langname')[0].textContent; 74 | 75 | // Set UI lang on item click 76 | $SelectUILangItemClone.addEventListener('click', () => { 77 | // If the clicked lang is different than the actual one 78 | if ($SelectUILangItemClone.dataset.slug !== N.Settings.strUILangSlug) { 79 | N.Settings.strUILangSlug = $SelectUILangItemClone.dataset.slug; 80 | N.Functions.IO.funcSaveSettings(); 81 | } 82 | }); 83 | 84 | if ($SelectUILangItemClone.dataset.slug === N.Settings.strUILangSlug) { 85 | $SelectUILangInput.value = $SelectUILangItemClone.textContent; 86 | } 87 | 88 | $SelectUILang.appendChild($SelectUILangItemClone); 89 | } 90 | }); 91 | 92 | 93 | 94 | 95 | // Handling UI themes 96 | 97 | const arrFolderNames = N.ElectronFramework.Fs.readdirSync(`${N.strAppPath}/themes/ui-themes/`); 98 | // For each interface theme 99 | forEach(arrFolderNames, (strFolderName) => { 100 | // Create a UI theme list item 101 | const $UIThemesListItemClone = $UIThemesListItem.cloneNode(true); 102 | $UIThemesListItemClone.removeAttribute('hidden'); 103 | $UIThemesListItemClone.dataset.slug = strFolderName; 104 | 105 | // Trying to read the UI theme CSS file. 106 | const strUIThemeCSS = N.Functions.IO.funcReadFile({ strPath: `${N.strAppPath}/themes/ui-themes/${strFolderName}/style.css` }); 107 | 108 | if (strUIThemeCSS) { 109 | // Getting the UI theme name inside the file content 110 | $UIThemesListItemClone.getElementsByClassName('mdl-list__item-primary-content')[0].textContent = strUIThemeCSS.match('- __themename: (.*) -')[1]; 111 | $UIThemesListItemClone.getElementsByTagName('img')[0].src = `../themes/ui-themes/${strFolderName}/preview.png`; 112 | 113 | // Set UI theme on item click 114 | $UIThemesListItemClone.addEventListener('click', () => { 115 | // If the clicked theme is different than the actual one 116 | if ($UIThemesListItemClone.dataset.slug !== N.Settings.strUIThemeSlug || ! $UIThemesList.getElementsByClassName('active')[0]) { 117 | N.Settings.strUIThemeSlug = $UIThemesListItemClone.dataset.slug; 118 | $UIThemeSheet.href = `${N.strAppPath}/themes/ui-themes/${strFolderName}/style.css`; 119 | const $UIThemeSelected = $UIThemesList.getElementsByClassName('active')[0]; 120 | if ($UIThemeSelected) { 121 | $UIThemeSelected.classList.remove('active'); 122 | } 123 | $UIThemesListItemClone.classList.add('active'); 124 | 125 | N.Functions.IO.funcSaveSettings(); 126 | } 127 | }); 128 | 129 | $UIThemesList.appendChild($UIThemesListItemClone); 130 | } 131 | }); 132 | 133 | // Selecting the corresponding UI theme in the list 134 | $UIThemesList.querySelector(`[data-slug="${N.Settings.strUIThemeSlug}"]`).click(); 135 | 136 | 137 | 138 | // Handling app auto update check 139 | $SwitchAutoUpdateInput.addEventListener('click', () => { 140 | N.Settings.boolAutoUpdateCheck = ! $SwitchAutoUpdate.classList.contains('is-checked'); 141 | N.Functions.IO.funcSaveSettings(); 142 | }); 143 | 144 | // Handling app window frame 145 | $SwitchWindowFrameInput.addEventListener('click', () => { 146 | N.Settings.boolWindowFrame = ! $SwitchWindowFrame.classList.contains('is-checked'); 147 | N.Functions.IO.funcSaveSettings(); 148 | }); 149 | 150 | // Handling Linux controls side 151 | $SwitchLinuxControlsRightInput.addEventListener('click', () => { 152 | N.Settings.boolLinuxControlsRight = ! $SwitchLinuxControlsRight.classList.contains('is-checked'); 153 | 154 | if (N.Settings.boolLinuxControlsRight) { 155 | document.body.dataset.linuxcontrolsside = 'right'; 156 | } else { 157 | document.body.dataset.linuxcontrolsside = 'left'; 158 | } 159 | 160 | N.Functions.IO.funcSaveSettings(); 161 | }); 162 | 163 | 164 | $SettingsDialog.getElementsByClassName('reset-button')[0].addEventListener('click', () => { 165 | funcResetSettings(); 166 | }); 167 | 168 | $SettingsDialog.getElementsByClassName('settings-folder-button')[0].addEventListener('click', () => { 169 | N.ElectronFramework.Shell.showItemInFolder(N.strSettingsFilePath); 170 | }); 171 | 172 | 173 | // First init of the auto update switch 174 | if ($SwitchAutoUpdate.classList.contains('is-checked') !== N.Settings.boolAutoUpdateCheck) { 175 | $SwitchAutoUpdateInput.click(); 176 | } 177 | 178 | // First init of the window frame switch 179 | if ($SwitchWindowFrame.classList.contains('is-checked') !== N.Settings.boolWindowFrame) { 180 | $SwitchWindowFrameInput.click(); 181 | } 182 | if ( ! N.Settings.boolWindowFrame) { 183 | document.body.classList.add('no-frame'); 184 | } 185 | 186 | // First init of the Linux controls side switch 187 | if ($SwitchLinuxControlsRight.classList.contains('is-checked') !== N.Settings.boolLinuxControlsRight) { 188 | $SwitchLinuxControlsRightInput.click(); 189 | } 190 | 191 | 192 | if (N.Settings.boolFirstStart) { 193 | N.$MenuQuickGuide.click(); 194 | N.Settings.boolFirstStart = false; 195 | N.Functions.IO.funcSaveSettings(); 196 | } 197 | })(); 198 | -------------------------------------------------------------------------------- /app-dev/js/tabs.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 3 | let timerHeaderVisible; 4 | const funcShowHeader = (Parameter) => { 5 | N.$Header.classList.add('active'); 6 | 7 | clearTimeout(timerHeaderVisible); 8 | timerHeaderVisible = setTimeout(() => { 9 | N.$Header.classList.remove('active'); 10 | clearTimeout(timerHeaderVisible); 11 | }, Parameter.flDelay); 12 | }; 13 | 14 | 15 | 16 | document.getElementById('menu-new-doc_unc2741').addEventListener('click', () => { 17 | new Document(); 18 | }); 19 | N.$TabsList.addEventListener('dblclick', () => { 20 | new Document(); 21 | }); 22 | N.$Header.getElementsByClassName('add-tab-button')[0].addEventListener('click', () => { 23 | new Document(); 24 | }); 25 | // bind() doesn't work in any text fields. An official plugin 'global bind' allows that. 26 | Mousetrap.bindGlobal(['mod+n', 'mod+t'], () => { 27 | new Document(); 28 | }); 29 | 30 | N.$TabsList.addEventListener('mousewheel', (Event) => { 31 | N.$TabsList.scrollLeft -= Event.wheelDelta; 32 | }); 33 | 34 | 35 | 36 | document.getElementById('menu-open-doc_unc2741').addEventListener('click', () => { 37 | N.Functions.Documents.funcOpenDialog(); 38 | }); 39 | Mousetrap.bindGlobal('mod+o', () => { 40 | N.Functions.Documents.funcOpenDialog(); 41 | }); 42 | 43 | 44 | Mousetrap.bindGlobal('mod+w', () => { 45 | N.DocActive.methClose(); 46 | }); 47 | 48 | 49 | document.getElementById('menu-close-all-docs_unc2741').addEventListener('click', () => { 50 | N.Functions.Documents.funcCloseAll(); 51 | }); 52 | Mousetrap.bindGlobal('mod+shift+w', () => { 53 | N.Functions.Documents.funcCloseAll(); 54 | }); 55 | 56 | 57 | 58 | Mousetrap.bindGlobal('ctrl+tab', () => { 59 | funcShowHeader({ flDelay: 2000 }); 60 | 61 | // Click on the concerned tab only if there are more than 1 tab opened 62 | if (N.intCurrentDocs > 1) { 63 | let $NextTab = N.DocActive.$Tab.nextElementSibling; 64 | if ( ! $NextTab) { 65 | $NextTab = N.$TabsList.firstElementChild; 66 | } 67 | N.$TabsList.scrollLeft = $NextTab.offsetLeft - 120; 68 | $NextTab.click(); 69 | } 70 | }); 71 | 72 | Mousetrap.bindGlobal('ctrl+shift+tab', () => { 73 | funcShowHeader({ flDelay: 2000 }); 74 | 75 | // Click on the concerned tab only if there are more than 1 tab opened 76 | if (N.intCurrentDocs > 1) { 77 | let $PreviousTab = N.DocActive.$Tab.previousElementSibling; 78 | if ( ! $PreviousTab) { 79 | // Warning : N.$TabsList must contain ONLY usable tabs, otherwise the script won't work. 80 | $PreviousTab = N.$TabsList.lastElementChild; 81 | } 82 | N.$TabsList.scrollLeft = $PreviousTab.offsetLeft - N.$TabsList.offsetWidth + $PreviousTab.offsetWidth + 120; 83 | $PreviousTab.click(); 84 | } 85 | }); 86 | 87 | 88 | 89 | N.$UnsavedDocsDialog.getElementsByClassName('force-close-all')[0].addEventListener('click', () => { 90 | N.Functions.Documents.funcCloseAll({ boolForceAction: true }); 91 | }); 92 | N.$UnsavedDocsDialog.getElementsByClassName('cancel-all')[0].addEventListener('click', () => { 93 | forEach(N.$TabsList.getElementsByClassName('tab'), ($Tab) => { 94 | $Tab.classList.remove('close-confirm'); 95 | }); 96 | }); 97 | 98 | 99 | 100 | Sortable.create(N.$TabsList, { 101 | delay: 10, 102 | animation: 150 103 | }); 104 | 105 | 106 | 107 | new Document(); 108 | 109 | })(); 110 | -------------------------------------------------------------------------------- /app-dev/js/window.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | 3 | N.Functions.Window.funcPlatformDetect(); 4 | 5 | N.$WindowControls.getElementsByClassName('close')[0].addEventListener('click', () => { 6 | N.Functions.Window.funcClose(); 7 | }); 8 | 9 | N.$WindowControls.getElementsByClassName('minimize')[0].addEventListener('click', () => { 10 | N.Functions.Window.funcMinimize(); 11 | }); 12 | 13 | N.$WindowControls.getElementsByClassName('toggle-maximize')[0].addEventListener('click', () => { 14 | N.Functions.Window.funcToggleMaximize(); 15 | }); 16 | 17 | N.$WindowControls.getElementsByClassName('toggle-fullscreen')[0].addEventListener('click', () => { 18 | N.Functions.Window.funcToggleFullscreen(); 19 | }); 20 | Mousetrap.bindGlobal(['f11', 'command+ctrl+f'], () => { 21 | N.Functions.Window.funcToggleFullscreen(); 22 | }); 23 | 24 | N.$WindowControls.getElementsByClassName('toggle-always-on-top')[0].addEventListener('click', () => { 25 | N.Functions.Window.funcToggleAlwaysOnTop(); 26 | }); 27 | 28 | })(); 29 | -------------------------------------------------------------------------------- /app-dev/lang/english.xml: -------------------------------------------------------------------------------- 1 | 2 | English 3 | 4 | -------------------------------------------------------------------------------- /app-dev/lib/LightRange.ES6.js: -------------------------------------------------------------------------------- 1 | // LightRange.js - A simple and lightweight selection, range and caret information library in native JavaScript, with an additional selection save & restore system. - https://github.com/n457/LightRange.js 2 | // Version 2.2.0 3 | // MIT License - Copyright (c) 2015 Bertrand Vignaud-Lerouge / n457 - https://github.com/n457 4 | 5 | 6 | // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes 7 | class LightRange { 8 | 9 | getSelectionInfo() { 10 | // Info data that will be returned by the method. 11 | const data = {}; 12 | 13 | // Modern browsers. 14 | if (window.getSelection) { 15 | const selection = window.getSelection(); 16 | 17 | const bodyScrollTop = document.body.scrollTop; 18 | const bodyScrollLeft = document.body.scrollLeft; 19 | 20 | // If something is selected. 21 | if (selection.rangeCount > 0) { 22 | // With cloneRange() we create a perfect abstract copy of the range. We do not want to modify the real one. 23 | const range = selection.getRangeAt(0).cloneRange(); 24 | 25 | // http://stackoverflow.com/questions/6846230/coordinates-of-selected-text-in-browser-page/6847328#6847328 26 | // http://stackoverflow.com/questions/12603397/calculate-width-height-of-the-selected-text-javascript/12603796#12603796 27 | // getBoundingClientRect() gives us correct information about a range but not about a caret (returns 0 as coordinates (here we test a height of 0) ). 28 | // getClientRects() gives us correct information about a caret but not about a range (returns wrong coordinates). 29 | let rect = range.getBoundingClientRect(); 30 | if (rect.height === 0) { 31 | rect = range.getClientRects()[0]; 32 | } 33 | 34 | // If the caret is on an empty line or if the range contains noting or new lines only, 'rect' will be undefined. 35 | if (rect) { 36 | data.width = rect.width; 37 | data.height = rect.height; 38 | 39 | // By default, x and y are calculated at the beginning of the range. 40 | // We have to add body scroll values 41 | data.xStart = rect.left + bodyScrollLeft; 42 | data.yStart = rect.top + bodyScrollTop; 43 | } 44 | 45 | data.text = selection.toString(); 46 | 47 | data.charStart = range.startOffset; 48 | data.charEnd = range.endOffset; 49 | 50 | // Collapse the range to its end. 51 | range.collapse(false); 52 | // We have to update the rect with getClientRects() because the range became a caret. 53 | rect = range.getClientRects()[0]; 54 | 55 | if (rect) { 56 | data.xEnd = rect.left + bodyScrollLeft; 57 | data.yEnd = rect.top + bodyScrollTop; 58 | } 59 | 60 | } 61 | } 62 | 63 | // IE 8 and other old browsers. 64 | else if (document.selection) { 65 | const selection = document.selection; 66 | const range = selection.createRange(); 67 | 68 | const bodyScrollTop = document.documentElement.scrollTop; 69 | const bodyScrollLeft = document.documentElement.scrollLeft; 70 | 71 | data.width = range.boundingWidth; 72 | data.height = range.boundingHeight; 73 | 74 | data.xStart = range.boundingLeft + bodyScrollLeft; 75 | data.yStart = range.boundingTop + bodyScrollTop; 76 | 77 | data.text = range.text; 78 | 79 | // startOffset and endOffset for IE 8 and below are not natively supported and are not so easy to implement. 80 | 81 | range.collapse(false); 82 | 83 | data.xEnd = range.boundingLeft + bodyScrollLeft; 84 | data.yEnd = range.boundingTop + bodyScrollTop; 85 | } 86 | 87 | // The browser doesn't support the feature 88 | else { 89 | return null; 90 | } 91 | 92 | if (data.text) { 93 | // From Countable JS lib : https://github.com/RadLikeWhoa/Countable 94 | // https://github.com/RadLikeWhoa/Countable/blob/master/Countable.js#L210 95 | data.characters = data.text.replace(/\s/g, '').length; 96 | data.charactersAll = data.text.replace(/[\n\r]/g, '').length; 97 | } else { 98 | data.characters = 0; 99 | data.charactersAll = 0; 100 | } 101 | 102 | 103 | // Some properties can be undefined. This is fine, it's easier to handle special cases. 104 | return data; 105 | } 106 | 107 | 108 | 109 | // http://stackoverflow.com/questions/4687808/contenteditable-selected-text-save-and-restore/4690057#4690057 110 | // http://jsfiddle.net/timdown/cCAWC/3/ 111 | saveSelection() { 112 | // Modern browsers. 113 | if (window.getSelection) { 114 | const selection = window.getSelection(); 115 | if (selection.getRangeAt && selection.rangeCount) { 116 | return selection.getRangeAt(0); 117 | } 118 | } 119 | // IE 8 and other old browsers. 120 | else if (document.selection && document.selection.createRange) { 121 | return document.selection.createRange(); 122 | } 123 | // The browser doesn't support the feature 124 | else { 125 | return null; 126 | } 127 | } 128 | 129 | 130 | restoreSelection(rangeToRestore) { 131 | if (rangeToRestore) { 132 | // Modern browsers. 133 | if (window.getSelection) { 134 | const selection = window.getSelection(); 135 | selection.removeAllRanges(); 136 | selection.addRange(rangeToRestore); 137 | } 138 | // IE 8 and other old browsers. 139 | else if (document.selection && rangeToRestore.select) { 140 | rangeToRestore.select(); 141 | } 142 | // The browser doesn't support the feature 143 | else { 144 | return null; 145 | } 146 | 147 | return rangeToRestore; 148 | } 149 | } 150 | 151 | 152 | } 153 | 154 | const lightrange = new LightRange(); 155 | -------------------------------------------------------------------------------- /app-dev/lib/emojify-base.min.css: -------------------------------------------------------------------------------- 1 | .emoji{width:1.5em;height:1.5em;display:inline-block;margin-bottom:-.25em;background-size:contain} -------------------------------------------------------------------------------- /app-dev/lib/foreach.min.js: -------------------------------------------------------------------------------- 1 | /*! foreach.js v1.1.0 | (c) 2014 @toddmotto | https://github.com/toddmotto/foreach */ 2 | var forEach=function(t,o,r){if("[object Object]"===Object.prototype.toString.call(t))for(var c in t)Object.prototype.hasOwnProperty.call(t,c)&&o.call(r,t[c],c,t);else for(var e=0,l=t.length;l>e;e++)o.call(r,t[e],e,t)}; -------------------------------------------------------------------------------- /app-dev/lib/getmdl-select.min.css: -------------------------------------------------------------------------------- 1 | .getmdl-select .mdl-icon-toggle__label{float:right;margin-top:-30px;color:rgba(0,0,0,0.4)}.getmdl-select.is-focused .mdl-icon-toggle__label{color:#3f51b5}.getmdl-select .mdl-menu__container{width:100% !important;overflow:hidden}.getmdl-select .mdl-menu__container .mdl-menu .mdl-menu__item{font-size:16px}.getmdl-select__fullwidth .mdl-menu{width:100%}.getmdl-select__fix-height .mdl-menu__container{overflow-y:auto;max-height:300px !important} 2 | 3 | /*# sourceMappingURL=getmdl-select.min.css.map */ 4 | -------------------------------------------------------------------------------- /app-dev/lib/getmdl-select.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"getmdl-select.min.css","sources":["getmdl-select.scss"],"sourcesContent":[".getmdl-select {\n\n .mdl-icon-toggle__label {\n float: right;\n margin-top: -30px;\n color: rgba(0, 0, 0, 0.4);\n }\n &.is-focused {\n .mdl-icon-toggle__label {\n color: #3f51b5;\n }\n }\n\n .mdl-menu__container {\n width: 100% !important;\n overflow: hidden;\n .mdl-menu .mdl-menu__item {\n font-size: 16px;\n }\n }\n}\n.getmdl-select__fullwidth {\n .mdl-menu {\n width: 100%;\n }\n}\n\n.getmdl-select__fix-height{\n .mdl-menu__container{\n overflow-y: auto;\n max-height: 300px !important;\n }\n}"],"mappings":"AAAA,AAEE,cAFY,CAEZ,uBAAuB,AAAC,CACtB,KAAK,CAAE,KAAM,CACb,UAAU,CAAE,KAAM,CAClB,KAAK,CAAE,eAAI,CACZ,AANH,AAQI,cARU,AAOX,WAAW,CACV,uBAAuB,AAAC,CACtB,KAAK,CAAE,OAAQ,CAChB,AAVL,AAaE,cAbY,CAaZ,oBAAoB,AAAC,CACnB,KAAK,CAAE,eAAgB,CACvB,QAAQ,CAAE,MAAO,CAIlB,AAnBH,AAgBc,cAhBA,CAaZ,oBAAoB,CAGlB,SAAS,CAAC,eAAe,AAAC,CACxB,SAAS,CAAE,IAAK,CACjB,AAGL,AACE,yBADuB,CACvB,SAAS,AAAC,CACR,KAAK,CAAE,IAAK,CACb,AAGH,AACE,0BADwB,CACxB,oBAAoB,AAAA,CAClB,UAAU,CAAE,IAAK,CACjB,UAAU,CAAE,gBAAiB,CAC9B","names":[],"sourceRoot":"/source/"} -------------------------------------------------------------------------------- /app-dev/lib/getmdl-select.min.js: -------------------------------------------------------------------------------- 1 | "use strict";window.onload=function(){getmdlSelect.init(".getmdl-select"),document.addEventListener("DOMNodeInserted",function(e){e.relatedNode.querySelectorAll(".getmdl-select").length>0&&componentHandler.upgradeDom()},!1)};var getmdlSelect={defaultValue:{width:300},addEventListeners:function(e){var t=e.querySelector("input"),n=e.querySelectorAll("li"),l=e.querySelector(".mdl-js-menu");t.onkeydown=function(e){38!=e.keyCode&&40!=e.keyCode||l.MaterialMenu.show()},l.onkeydown=function(e){13==e.keyCode&&t.focus()},[].forEach.call(n,function(n){n.onclick=function(){if(t.value=n.textContent,e.MaterialTextfield.change(n.textContent),setTimeout(function(){e.MaterialTextfield.updateClasses_()},250),t.dataset.val=n.dataset.val||"","createEvent"in document){var l=document.createEvent("HTMLEvents");l.initEvent("change",!1,!0),t.dispatchEvent(l)}else t.fireEvent("onchange")}})},init:function(e,t){var n=document.querySelectorAll(e);[].forEach.call(n,function(e){getmdlSelect.addEventListeners(e);var n=t?t:e.querySelector(".mdl-menu").offsetWidth?e.querySelector(".mdl-menu").offsetWidth:getmdlSelect.defaultValue.width;e.style.width=n+"px"})}}; 2 | //# sourceMappingURL=getmdl-select.min.js.map -------------------------------------------------------------------------------- /app-dev/lib/getmdl-select.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["src/js/getmdl-select.js"],"names":["window","onload","getmdlSelect","init","document","addEventListener","ev","relatedNode","querySelectorAll","length","componentHandler","upgradeDom","defaultValue","width","addEventListeners","dropdown","input","querySelector","list","menu","onkeydown","event","keyCode","show","focus","forEach","call","li","onclick","value","textContent","MaterialTextfield","change","setTimeout","updateClasses_","dataset","val","evt","createEvent","initEvent","dispatchEvent","fireEvent","selector","widthDef","dropdowns","i","offsetWidth","style"],"mappings":"AAAA,YAIIA,QAAOC,OAAS,WACZC,aAAaC,KAAK,kBAClBC,SAASC,iBAAiB,kBAAmB,SAAUC,GAC/CA,EAAGC,YAAYC,iBAAiB,kBAAkBC,OAAS,GAC3DC,iBAAiBC,eAEtB,GAGP,IAAIT,eACAU,cACIC,MAAO,KAEXC,kBAAmB,SAA2BC,GAC1C,GAAIC,GAAQD,EAASE,cAAc,SAC/BC,EAAOH,EAASP,iBAAiB,MACjCW,EAAOJ,EAASE,cAAc,eAGlCD,GAAMI,UAAY,SAAUC,GACH,IAAjBA,EAAMC,SAAkC,IAAjBD,EAAMC,SAC7BH,EAAmB,aAAEI,QAK7BJ,EAAKC,UAAY,SAAUC,GACF,IAAjBA,EAAMC,SACNN,EAAMQ,YAIXC,QAAQC,KAAKR,EAAM,SAAUS,GAC5BA,EAAGC,QAAU,WAUT,GATAZ,EAAMa,MAAQF,EAAGG,YACjBf,EAASgB,kBAAkBC,OAAOL,EAAGG,aACrCG,WAAW,WACPlB,EAASgB,kBAAkBG,kBAC5B,KAGHlB,EAAMmB,QAAQC,IAAMT,EAAGQ,QAAQC,KAAO,GAElC,eAAiBhC,UAAU,CAC3B,GAAIiC,GAAMjC,SAASkC,YAAY,aAC/BD,GAAIE,UAAU,UAAU,GAAO,GAC/BvB,EAAMwB,cAAcH,OAEpBrB,GAAMyB,UAAU,gBAKhCtC,KAAM,SAAcuC,EAAUC,GAC1B,GAAIC,GAAYxC,SAASI,iBAAiBkC,MACvCjB,QAAQC,KAAKkB,EAAW,SAAUC,GACjC3C,aAAaY,kBAAkB+B,EAC/B,IAAIhC,GAAQ8B,EAAWA,EAAWE,EAAE5B,cAAc,aAAa6B,YAAcD,EAAE5B,cAAc,aAAa6B,YAAc5C,aAAaU,aAAaC,KAClJgC,GAAEE,MAAMlC,MAAQA,EAAQ","file":"getmdl-select.min.js","sourceRoot":"/media/franckevva/04D48298D4828C1C/WebstormProjects/getmdl-select"} -------------------------------------------------------------------------------- /app-dev/lib/mousetrap-global-bind.min.js: -------------------------------------------------------------------------------- 1 | (function(a){var c={},d=a.prototype.stopCallback;a.prototype.stopCallback=function(e,b,a,f){return this.paused?!0:c[a]||c[f]?!1:d.call(this,e,b,a)};a.prototype.bindGlobal=function(a,b,d){this.bind(a,b,d);if(a instanceof Array)for(b=0;bl||k.hasOwnProperty(l)&&(n[k[l]]=l)}e=n[h]?"keydown":"keypress"}"keypress"==e&&g.length&&(e="keydown");return{key:c,modifiers:g,action:e}}function C(a,b){return null===a||a===t?!1:a===b?!0:C(a.parentNode,b)}function c(a){function b(a){a= 4 | a||{};var b=!1,m;for(m in n)a[m]?b=!0:n[m]=0;b||(w=!1)}function h(a,b,m,f,c,h){var g,e,k=[],l=m.type;if(!d._callbacks[a])return[];"keyup"==l&&v(a)&&(b=[a]);for(g=0;g":".","?":"/","|":"\\"},A={option:"alt",command:"meta","return":"enter", 9 | escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},n;for(g=1;20>g;++g)k[111+g]="f"+g;for(g=0;9>=g;++g)k[g+96]=g;c.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};c.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};c.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};c.prototype.reset=function(){this._callbacks={};this._directMap= 10 | {};return this};c.prototype.stopCallback=function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")||C(b,this.target)?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};c.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};c.addKeycodes=function(a){for(var b in a)a.hasOwnProperty(b)&&(k[b]=a[b]);n=null};c.init=function(){var a=c(t),b;for(b in a)"_"!==b.charAt(0)&&(c[b]=function(b){return function(){return a[b].apply(a,arguments)}}(b))}; 11 | c.init();r.Mousetrap=c;"undefined"!==typeof module&&module.exports&&(module.exports=c);"function"===typeof define&&define.amd&&define(function(){return c})}})("undefined"!==typeof window?window:null,"undefined"!==typeof window?document:null); 12 | -------------------------------------------------------------------------------- /app-dev/lib/reqwest.no-loader.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Reqwest! A general purpose XHR connection manager 3 | * license MIT (c) Dustin Diaz 2015 4 | * https://github.com/ded/reqwest 5 | */ 6 | !function(e,t,n){typeof module!="undefined"&&module.exports?t[e]=n():typeof define=="function"&&define.amd?define(n):t[e]=n()}("reqwest",this,function(){function succeed(e){var t=protocolRe.exec(e.url);return t=t&&t[1]||context.location.protocol,httpsRe.test(t)?twoHundo.test(e.request.status):!!e.request.response}function handleReadyState(e,t,n){return function(){if(e._aborted)return n(e.request);if(e._timedOut)return n(e.request,"Request is aborted: timeout");e.request&&e.request[readyState]==4&&(e.request.onreadystatechange=noop,succeed(e)?t(e.request):n(e.request))}}function setHeaders(e,t){var n=t.headers||{},r;n.Accept=n.Accept||defaultHeaders.accept[t.type]||defaultHeaders.accept["*"];var i=typeof FormData!="undefined"&&t.data instanceof FormData;!t.crossOrigin&&!n[requestedWith]&&(n[requestedWith]=defaultHeaders.requestedWith),!n[contentType]&&!i&&(n[contentType]=t.contentType||defaultHeaders.contentType);for(r in n)n.hasOwnProperty(r)&&"setRequestHeader"in e&&e.setRequestHeader(r,n[r])}function setCredentials(e,t){typeof t.withCredentials!="undefined"&&typeof e.withCredentials!="undefined"&&(e.withCredentials=!!t.withCredentials)}function generalCallback(e){lastValue=e}function urlappend(e,t){return e+(/\?/.test(e)?"&":"?")+t}function handleJsonp(e,t,n,r){var i=uniqid++,s=e.jsonpCallback||"callback",o=e.jsonpCallbackName||reqwest.getcallbackPrefix(i),u=new RegExp("((^|\\?|&)"+s+")=([^&]+)"),a=r.match(u),f=doc.createElement("script"),l=0,c=navigator.userAgent.indexOf("MSIE 10.0")!==-1;return a?a[3]==="?"?r=r.replace(u,"$1="+o):o=a[3]:r=urlappend(r,s+"="+o),context[o]=generalCallback,f.type="text/javascript",f.src=r,f.async=!0,typeof f.onreadystatechange!="undefined"&&!c&&(f.htmlFor=f.id="_reqwest_"+i),f.onload=f.onreadystatechange=function(){if(f[readyState]&&f[readyState]!=="complete"&&f[readyState]!=="loaded"||l)return!1;f.onload=f.onreadystatechange=null,f.onclick&&f.onclick(),t(lastValue),lastValue=undefined,head.removeChild(f),l=1},head.appendChild(f),{abort:function(){f.onload=f.onreadystatechange=null,n({},"Request is aborted: timeout",{}),lastValue=undefined,head.removeChild(f),l=1}}}function getRequest(e,t){var n=this.o,r=(n.method||"GET").toUpperCase(),i=typeof n=="string"?n:n.url,s=n.processData!==!1&&n.data&&typeof n.data!="string"?reqwest.toQueryString(n.data):n.data||null,o,u=!1;return(n["type"]=="jsonp"||r=="GET")&&s&&(i=urlappend(i,s),s=null),n["type"]=="jsonp"?handleJsonp(n,e,t,i):(o=n.xhr&&n.xhr(n)||xhr(n),o.open(r,i,n.async===!1?!1:!0),setHeaders(o,n),setCredentials(o,n),context[xDomainRequest]&&o instanceof context[xDomainRequest]?(o.onload=e,o.onerror=t,o.onprogress=function(){},u=!0):o.onreadystatechange=handleReadyState(this,e,t),n.before&&n.before(o),u?setTimeout(function(){o.send(s)},200):o.send(s),o)}function Reqwest(e,t){this.o=e,this.fn=t,init.apply(this,arguments)}function setType(e){if(e===null)return undefined;if(e.match("json"))return"json";if(e.match("javascript"))return"js";if(e.match("text"))return"html";if(e.match("xml"))return"xml"}function init(o,fn){function complete(e){o.timeout&&clearTimeout(self.timeout),self.timeout=null;while(self._completeHandlers.length>0)self._completeHandlers.shift()(e)}function success(resp){var type=o.type||resp&&setType(resp.getResponseHeader("Content-Type"));resp=type!=="jsonp"?self.request:resp;var filteredResponse=globalSetupOptions.dataFilter(resp.responseText,type),r=filteredResponse;try{resp.responseText=r}catch(e){}if(r)switch(type){case"json":try{resp=context.JSON?context.JSON.parse(r):eval("("+r+")")}catch(err){return error(resp,"Could not parse JSON in response",err)}break;case"js":resp=eval(r);break;case"html":resp=r;break;case"xml":resp=resp.responseXML&&resp.responseXML.parseError&&resp.responseXML.parseError.errorCode&&resp.responseXML.parseError.reason?null:resp.responseXML}self._responseArgs.resp=resp,self._fulfilled=!0,fn(resp),self._successHandler(resp);while(self._fulfillmentHandlers.length>0)resp=self._fulfillmentHandlers.shift()(resp);complete(resp)}function timedOut(){self._timedOut=!0,self.request.abort()}function error(e,t,n){e=self.request,self._responseArgs.resp=e,self._responseArgs.msg=t,self._responseArgs.t=n,self._erred=!0;while(self._errorHandlers.length>0)self._errorHandlers.shift()(e,t,n);complete(e)}this.url=typeof o=="string"?o:o.url,this.timeout=null,this._fulfilled=!1,this._successHandler=function(){},this._fulfillmentHandlers=[],this._errorHandlers=[],this._completeHandlers=[],this._erred=!1,this._responseArgs={};var self=this;fn=fn||function(){},o.timeout&&(this.timeout=setTimeout(function(){timedOut()},o.timeout)),o.success&&(this._successHandler=function(){o.success.apply(o,arguments)}),o.error&&this._errorHandlers.push(function(){o.error.apply(o,arguments)}),o.complete&&this._completeHandlers.push(function(){o.complete.apply(o,arguments)}),this.request=getRequest.call(this,success,error)}function reqwest(e,t){return new Reqwest(e,t)}function normalize(e){return e?e.replace(/\r?\n/g,"\r\n"):""}function serial(e,t){var n=e.name,r=e.tagName.toLowerCase(),i=function(e){e&&!e.disabled&&t(n,normalize(e.attributes.value&&e.attributes.value.specified?e.value:e.text))},s,o,u,a;if(e.disabled||!n)return;switch(r){case"input":/reset|button|image|file/i.test(e.type)||(s=/checkbox/i.test(e.type),o=/radio/i.test(e.type),u=e.value,(!s&&!o||e.checked)&&t(n,normalize(s&&u===""?"on":u)));break;case"textarea":t(n,normalize(e.value));break;case"select":if(e.type.toLowerCase()==="select-one")i(e.selectedIndex>=0?e.options[e.selectedIndex]:null);else for(a=0;e.length&&a -1 6 | * versionCompare('1.1', '1.1') => 0 7 | * versionCompare('1.2', '1.1') => 1 8 | * versionCompare('2.23.3', '2.22.3') => 1 9 | * 10 | * Returns: 11 | * -1 = left is LOWER than right 12 | * 0 = they are equal 13 | * 1 = left is GREATER = right is LOWER 14 | * And FALSE if one of input versions are not valid 15 | * 16 | * @function 17 | * @param {String} left Version #1 18 | * @param {String} right Version #2 19 | * @return {Integer|Boolean} 20 | * @author Alexey Bass (albass) 21 | * @since 2011-07-14 22 | */ 23 | versionCompare = function(left, right) { 24 | if (typeof left + typeof right != 'stringstring') 25 | return false; 26 | 27 | var a = left.split('.') 28 | , b = right.split('.') 29 | , i = 0, len = Math.max(a.length, b.length); 30 | 31 | for (; i < len; i++) { 32 | if ((a[i] && !b[i] && parseInt(a[i]) > 0) || (parseInt(a[i]) > parseInt(b[i]))) { 33 | return 1; 34 | } else if ((b[i] && !a[i] && parseInt(b[i]) > 0) || (parseInt(a[i]) < parseInt(b[i]))) { 35 | return -1; 36 | } 37 | } 38 | 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /app-dev/lib/zenscroll.no-loader.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"function"==typeof define&&define.amd?define([],e()):"object"==typeof module&&module.exports?t.zenscroll=e():t.zenscroll=e()}(this,function(){"use strict";if("undefined"==typeof window||!("document"in window))return{};var t=function(t,e,n){e=e||999,n||0===n||(n=9);var o,i=document.documentElement,r=function(){return"getComputedStyle"in window&&"smooth"===window.getComputedStyle(t?t:document.body)["scroll-behavior"]},c=function(){return t?t.scrollTop:window.scrollY||i.scrollTop},u=function(){return t?Math.min(t.offsetHeight,window.innerHeight):window.innerHeight||i.clientHeight},f=function(e){return t?e.offsetTop:e.getBoundingClientRect().top+c()-i.offsetTop},l=function(){clearTimeout(o),o=0},a=function(n,f,a){if(l(),r())(t||window).scrollTo(0,n),a&&a();else{var d=c(),s=Math.max(n,0)-d;f=f||Math.min(Math.abs(s),e);var m=(new Date).getTime();!function e(){o=setTimeout(function(){var n=Math.min(((new Date).getTime()-m)/f,1),o=Math.max(Math.floor(d+s*(n<.5?2*n*n:n*(4-2*n)-1)),0);t?t.scrollTop=o:window.scrollTo(0,o),n<1&&u()+o<(t||i).scrollHeight?e():(setTimeout(l,99),a&&a())},9)}()}},d=function(t,e,o){a(f(t)-n,e,o)},s=function(t,e,o){var i=t.getBoundingClientRect().height,r=f(t),l=r+i,s=u(),m=c(),h=m+s;r-ns?d(t,e,o):l+n>h?a(l-s+n,e,o):o&&o()},m=function(t,e,n,o){a(Math.max(f(t)-u()/2+(n||t.getBoundingClientRect().height/2),0),e,o)},h=function(t,o){t&&(e=t),(0===o||o)&&(n=o)};return{setup:h,to:d,toY:a,intoView:s,center:m,stop:l,moving:function(){return!!o}}},e=t();if("addEventListener"in window&&"smooth"!==document.body.style.scrollBehavior&&!window.noZensmooth){var n=function(t){try{history.replaceState({},"",window.location.href.split("#")[0]+t)}catch(t){}};window.addEventListener("click",function(t){for(var o=t.target;o&&"A"!==o.tagName;)o=o.parentNode;if(!(!o||1!==t.which||t.shiftKey||t.metaKey||t.ctrlKey||t.altKey)){var i=o.getAttribute("href")||"";if(0===i.indexOf("#"))if("#"===i)t.preventDefault(),e.toY(0),n("");else{var r=o.hash.substring(1),c=document.getElementById(r);c&&(t.preventDefault(),e.to(c),n("#"+r))}}},!1)}return{createScroller:t,setup:e.setup,to:e.to,toY:e.toY,intoView:e.intoView,center:e.center,stop:e.stop,moving:e.moving}}); 2 | -------------------------------------------------------------------------------- /app-dev/main-process.js: -------------------------------------------------------------------------------- 1 | const Electron = require('electron'); 2 | // Module to control application life. 3 | const App = Electron.app; 4 | // Module to create native browser windows. 5 | const BrowserWindow = Electron.BrowserWindow; 6 | // Module to create native window menus. 7 | const Menu = Electron.Menu; 8 | // Module to execute native commands. 9 | const Shell = Electron.shell; 10 | // "fs" for "File System" : used to read and write files on disks. 11 | const Fs = require('fs'); 12 | 13 | const strAppUserData = App.getPath('userData'); 14 | 15 | let Settings; 16 | try { 17 | // Accessing settings here for window related settings 18 | Settings = JSON.parse( Fs.readFileSync(`${strAppUserData}/settings.min.json`, 'utf8') ); 19 | } catch (Error) {} 20 | 21 | 22 | let boolWindowFrame = false; 23 | 24 | if (Settings) { 25 | if (Settings.boolWindowFrame) { 26 | boolWindowFrame = true; 27 | } 28 | } 29 | 30 | 31 | 32 | // Keep a global reference of the window object, if you don't, the window will be closed automatically when the JavaScript object is garbage collected. 33 | let MainWindow; 34 | 35 | 36 | 37 | // (Un)colored is a single instance application. Close the second instance window if it exists. 38 | // https://github.com/electron/electron/blob/master/docs/api/app.md#appmakesingleinstancecallback 39 | const boolFirstInstanceExists = App.makeSingleInstance(() => { 40 | // Someone tried to run a second instance, we should focus our first window. 41 | if (MainWindow) { 42 | if (MainWindow.isMinimized()) { 43 | MainWindow.restore(); 44 | } 45 | MainWindow.focus(); 46 | } 47 | }); 48 | if (boolFirstInstanceExists) { 49 | // Close this second instance window. 50 | App.quit(); 51 | } 52 | // If this is the first instance, continue the execution of this script. 53 | 54 | 55 | 56 | const funcCreateWindow = () => { 57 | 58 | const arrMenuTemplate = [ 59 | { 60 | label: 'Edit', 61 | submenu: [ 62 | { 63 | label: 'Undo', 64 | accelerator: 'CmdOrCtrl+Z', 65 | role: 'undo' 66 | }, 67 | { 68 | label: 'Redo', 69 | accelerator: 'CmdOrCtrl+Y', 70 | role: 'redo' 71 | }, 72 | { 73 | label: 'Redo (alias)', 74 | accelerator: 'Shift+CmdOrCtrl+Z', 75 | role: 'redo' 76 | }, 77 | { 78 | type: 'separator' 79 | }, 80 | { 81 | label: 'Cut', 82 | accelerator: 'CmdOrCtrl+X', 83 | role: 'cut' 84 | }, 85 | { 86 | label: 'Copy', 87 | accelerator: 'CmdOrCtrl+C', 88 | role: 'copy' 89 | }, 90 | { 91 | label: 'Paste', 92 | accelerator: 'CmdOrCtrl+V', 93 | role: 'paste' 94 | }, 95 | { 96 | label: 'Select All', 97 | accelerator: 'CmdOrCtrl+A', 98 | role: 'selectall' 99 | } 100 | ] 101 | }, 102 | { 103 | label: 'View', 104 | submenu: [ 105 | { 106 | label: 'Reload', 107 | accelerator: 'CmdOrCtrl+Shift+Alt+R', 108 | click(Item, FocusedWindow) { 109 | if (FocusedWindow) { 110 | FocusedWindow.reload(); 111 | } 112 | } 113 | }, 114 | // { 115 | // label: 'Toggle Full Screen', 116 | // accelerator: (() => { 117 | // if (process.platform == 'darwin') { 118 | // return 'Ctrl+Command+F'; 119 | // } else { 120 | // return 'F11'; 121 | // } 122 | // })(), 123 | // click(Item, FocusedWindow) { 124 | // if (FocusedWindow) { 125 | // FocusedWindow.setFullScreen( ! FocusedWindow.isFullScreen()); 126 | // } 127 | // } 128 | // }, 129 | { 130 | label: 'Toggle Developer Tools', 131 | accelerator: 'CmdOrCtrl+Shift+Alt+D', 132 | click(Item, FocusedWindow) { 133 | if (FocusedWindow) { 134 | FocusedWindow.webContents.toggleDevTools(); 135 | } 136 | } 137 | } 138 | ] 139 | } 140 | ]; 141 | 142 | if (process.platform === 'darwin') { 143 | arrMenuTemplate.unshift({ 144 | label: 'Application', 145 | submenu: [ 146 | { 147 | label: 'Hide', 148 | accelerator: 'Command+H', 149 | role: 'hide' 150 | }, 151 | { 152 | type: 'separator' 153 | }, 154 | { 155 | label: 'Quit', 156 | accelerator: 'Command+Q', 157 | click() { App.quit(); } 158 | } 159 | ] 160 | }); 161 | } 162 | 163 | Menu.setApplicationMenu(Menu.buildFromTemplate(arrMenuTemplate)); 164 | 165 | 166 | // Create the browser window. 167 | MainWindow = new BrowserWindow({ 168 | 'width': 960, 169 | 'height': 660, 170 | 'frame': boolWindowFrame 171 | }); 172 | 173 | 174 | // http://stackoverflow.com/questions/31670803/prevent-electron-app-from-redirecting-when-dragdropping-items-in-window 175 | MainWindow.webContents.on('will-navigate', (Event) => { 176 | Event.preventDefault(); 177 | return false; 178 | }); 179 | 180 | // From http://stackoverflow.com/questions/32402327/how-can-i-force-external-links-from-browser-window-to-open-in-a-default-browser 181 | MainWindow.webContents.on('new-window', (Event, strURL) => { 182 | Event.preventDefault(); 183 | Shell.openExternal(strURL); 184 | }); 185 | 186 | // Emitted when the window is closed. 187 | MainWindow.on('closed', function () { 188 | // Dereference the window object, usually you would store windows in an array if your app supports multi windows, this is the time when you should delete the corresponding element. 189 | MainWindow = null; 190 | }); 191 | 192 | // and load the view of the app. 193 | MainWindow.loadURL(`file://${__dirname}/views/main.html`); 194 | 195 | }; 196 | 197 | // This method will be called when Electron has finished initialization and is ready to create browser windows. 198 | // Some APIs can only be used after this event occurs. 199 | App.on('ready', () => { 200 | funcCreateWindow(); 201 | }); 202 | 203 | // Quit when all windows are closed. 204 | App.on('window-all-closed', () => { 205 | // On OS X it is common for applications and their menu bar to stay active until the user quits explicitly with Cmd + Q. 206 | // Here we choose to close the app completely in all cases. 207 | App.quit(); 208 | // Original OS X specific code here : https://github.com/electron/electron/blob/master/docs/tutorial/quick-start.md#write-your-first-electron-app 209 | }); 210 | 211 | // In this file you can include the rest of your app's specific main process code. You can also put them in separate files and require them here. 212 | App.on('activate', () => { 213 | // On OS X it's common to re-create a window in the app when the dock icon is clicked and there are no other windows open. 214 | if (MainWindow === null) { 215 | funcCreateWindow(); 216 | } 217 | }); 218 | -------------------------------------------------------------------------------- /app-dev/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uncolored", 3 | "productName": "Uncolored", 4 | "version": "0.10.2", 5 | "description": "(Un)colored — Next generation desktop rich content editor.", 6 | "homepage": "http://n457.github.io/Uncolored/", 7 | 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/n457/Uncolored.git" 11 | }, 12 | 13 | "bugs": "https://github.com/n457/Uncolored/issues", 14 | "license": "Apache License 2.0", 15 | 16 | "author": { 17 | "name": "n457 / Bertrand Vignaud-Lerouge", 18 | "email": "n457.contact@gmail.com", 19 | "url": "https://github.com/n457" 20 | }, 21 | 22 | "main": "main-process.js" 23 | } 24 | -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/lib/document.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: auto; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; } 5 | 6 | .wysiwyg-content main > :first-child { 7 | padding-top: 0 !important; 8 | margin-top: 0 !important; } 9 | .wysiwyg-content main > :last-child { 10 | padding-bottom: 0 !important; 11 | margin-bottom: 0 !important; } 12 | 13 | .wysiwyg-content img { 14 | max-width: 100%; } 15 | 16 | .wysiwyg-content iframe { 17 | display: block; 18 | max-width: 100%; 19 | border: 0; } 20 | 21 | .wysiwyg-content .emoji .emoji-text { 22 | font-size: 0; } 23 | -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/lib/document.js: -------------------------------------------------------------------------------- 1 | /*! foreach.js v1.1.0 | (c) 2014 @toddmotto | https://github.com/toddmotto/foreach */ 2 | var forEach=function(t,o,r){if("[object Object]"===Object.prototype.toString.call(t))for(var c in t)Object.prototype.hasOwnProperty.call(t,c)&&o.call(r,t[c],c,t);else for(var e=0,l=t.length;l>e;e++)o.call(r,t[e],e,t)}; 3 | 4 | 5 | (function () { 6 | 7 | // Checking for an Internet connection 8 | // From http://youmightnotneedjquery.com/#request IE8+ 9 | var Request = new XMLHttpRequest(); 10 | // Load a tiny library from a reliable source to check if a real Internet connexion is available. 11 | Request.open('GET', 'https://ajax.googleapis.com/ajax/libs/threejs/r76/three.min.js', true); 12 | Request.onreadystatechange = function () { 13 | if (this.readyState === 4) { 14 | if (this.status >= 200 && this.status < 400) { 15 | // Success. Do nothing. 16 | } else { 17 | alert('Apparently, there is no Internet connexion. Some ressources may not load, like remote images.'); 18 | } 19 | } 20 | }; 21 | Request.send(); 22 | Request = null; 23 | 24 | 25 | // Executing highlight.js if there is at least one
26 |   forEach(document.getElementsByTagName('pre'), ($CodeBlock) => {
27 |     hljs.highlightBlock($CodeBlock);
28 |   });
29 | 
30 | })();
31 | 


--------------------------------------------------------------------------------
/app-dev/themes/doc-themes/lib/normalize.custom.min.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v4.2.0 | MIT License | github.com/necolas/normalize.css */button,hr,input{overflow:visible}audio,canvas,progress,video{display:inline-block}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{color:inherit;display:table;max-width:100%;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}[hidden],template{display:none}
2 | 


--------------------------------------------------------------------------------
/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-Bold.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-Bold.woff2


--------------------------------------------------------------------------------
/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-BoldItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-BoldItalic.woff2


--------------------------------------------------------------------------------
/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-Italic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-Italic.woff2


--------------------------------------------------------------------------------
/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-Light.woff2


--------------------------------------------------------------------------------
/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-LightItalic.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-LightItalic.woff2


--------------------------------------------------------------------------------
/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/assets/fonts/Roboto-Regular.woff2


--------------------------------------------------------------------------------
/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/highlight.js-theme.min.css:
--------------------------------------------------------------------------------
1 | .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-meta,.hljs-strong{font-weight:700}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}
2 | 


--------------------------------------------------------------------------------
/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/preview.png


--------------------------------------------------------------------------------
/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/script.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 
3 | })();
4 | 


--------------------------------------------------------------------------------
/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_attitude/template.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |   
 4 |     
 5 |     
 6 | 
 7 |     
24 |   
25 |   
26 |     
27 |     
28 |     
29 | 
30 |     
31 | 32 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_github-style/highlight.js-theme.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-meta,.hljs-strong{font-weight:700}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic} 2 | -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_github-style/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_github-style/preview.png -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_github-style/script.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | })(); 4 | -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_github-style/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | 33 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/assets/fonts/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/assets/fonts/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/assets/fonts/Roboto-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/assets/fonts/Roboto-BoldItalic.woff2 -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/assets/fonts/Roboto-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/assets/fonts/Roboto-Italic.woff2 -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/assets/fonts/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/assets/fonts/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/assets/fonts/RobotoSlab-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/assets/fonts/RobotoSlab-Light.woff2 -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/highlight.js-theme.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-meta,.hljs-strong{font-weight:700}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic} 2 | -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/preview.png -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/script.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | })(); 4 | -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_horizon/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-BoldItalic.woff2 -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-Italic.woff2 -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-Light.woff2 -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-LightItalic.woff2 -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/assets/fonts/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/highlight.js-theme.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-meta,.hljs-strong{font-weight:700}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic} 2 | -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/preview.png -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/script.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 3 | })(); 4 | -------------------------------------------------------------------------------- /app-dev/themes/doc-themes/themes-dir/_ORIGINAL_white-room/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /app-dev/themes/ui-themes/_ORIGINAL_white-room/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app-dev/themes/ui-themes/_ORIGINAL_white-room/preview.png -------------------------------------------------------------------------------- /app-dev/themes/ui-themes/_ORIGINAL_white-room/style.css: -------------------------------------------------------------------------------- 1 | /* - __themename: White Room - */ 2 | /* - __authorname: n457 - */ 3 | 4 | /* Libraries styles overwriting (Material Design Lite, etc.) */ 5 | 6 | .mdl-menu__item { 7 | /* By default, a menu item text is black. */ 8 | color: #757575; } 9 | 10 | .mdl-textfield { 11 | color: #757575; } 12 | .mdl-textfield--floating-label.is-focused .mdl-textfield__label, 13 | .mdl-textfield--floating-label.is-dirty .mdl-textfield__label, 14 | .mdl-textfield--floating-label.has-placeholder .mdl-textfield__label { 15 | color: black; } 16 | .mdl-textfield__label:after { 17 | background-color: black; } 18 | 19 | .mdl-switch.is-checked .mdl-switch__track { 20 | /* #4baf4f */ 21 | background-color: rgba(75, 175, 79, 0.5); } 22 | .mdl-switch.is-checked .mdl-switch__thumb { 23 | background-color: #4baf4f; } 24 | .mdl-switch__ripple-container .mdl-ripple { 25 | background-color: #4baf4f; } 26 | 27 | .mdl-button.mdl-button--colored { 28 | color: black; } 29 | .mdl-button--raised.mdl-button--colored { 30 | color: black; 31 | background-color: transparent; } 32 | 33 | 34 | .mdl-tooltip { 35 | background: rgba(150, 150, 150, 0.9); } 36 | 37 | 38 | 39 | /* Global */ 40 | 41 | body { 42 | color: #757575; } 43 | body.no-frame { 44 | border-color: #c7c7c7; } 45 | 46 | a { 47 | color: #757575; } 48 | 49 | kbd { 50 | color: #333; 51 | text-shadow: 0 1px 0 #fff; 52 | background-color: #f7f7f7; 53 | border-color: #ccc; 54 | box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px #fff inset; } 55 | 56 | /* highlight */ 57 | .hl { 58 | color: #4a4a4a; } 59 | 60 | .hover-effects:hover { 61 | background-color: rgba(158, 158, 158, 0.2) !important; } 62 | .hover-effects:active { 63 | background-color: rgba(158, 158, 158, 0.4) !important; } 64 | 65 | ::selection { 66 | background-color: #e2e2e2; } 67 | 68 | ::-webkit-scrollbar-thumb { 69 | background-color: #c2c2c2; } 70 | 71 | 72 | 73 | /* Header */ 74 | 75 | .tab-bar { 76 | background-color: white; } 77 | 78 | 79 | body.windows-os .window-controls li { 80 | color: #999999; } 81 | body.windows-os .window-controls li:hover { 82 | background-color: rgba(158, 158, 158, 0.2); } 83 | body.windows-os .window-controls li:active { 84 | background-color: rgba(158, 158, 158, 0.4); } 85 | body.windows-os .window-controls .close:hover { 86 | color: white; 87 | background-color: #e81123; } 88 | body.windows-os .window-controls .close:active { 89 | color: white; 90 | background-color: #f1707a; } 91 | body.windows-os.fullscreen .window-controls .toggle-fullscreen, 92 | body.windows-os.always-on-top .window-controls .toggle-always-on-top { 93 | color: black; } 94 | 95 | 96 | body.osx-os .window-controls .close { 97 | color: #712b27; 98 | background-color: #ff5f56; } 99 | body.osx-os .window-controls .close:active { 100 | background-color: #d04942; } 101 | body.osx-os .window-controls .minimize { 102 | color: #3a2a0a; 103 | background-color: #ffbd2e; } 104 | body.osx-os .window-controls .minimize:active { 105 | background-color: #ce9314; } 106 | body.osx-os .window-controls .toggle-maximize { 107 | color: #147322; 108 | background-color: #27c93f; } 109 | body.osx-os .window-controls .toggle-maximize:active { 110 | background-color: #20a934; } 111 | body.osx-os .window-controls .toggle-fullscreen { 112 | color: #3657a7; 113 | background-color: #5689ff; } 114 | body.osx-os .window-controls .toggle-fullscreen:active { 115 | background-color: #4573de; } 116 | body.osx-os.fullscreen .window-controls .toggle-fullscreen { 117 | box-shadow: 0 0 14px 0 #0043ff; } 118 | body.osx-os .window-controls .toggle-always-on-top { 119 | color: #0092a9; 120 | background-color: #2ee2ff; } 121 | body.osx-os .window-controls .toggle-always-on-top:active { 122 | background-color: #29bed6; } 123 | body.osx-os.always-on-top .window-controls .toggle-always-on-top { 124 | box-shadow: 0 0 14px 0 #00adff; } 125 | 126 | 127 | body.linux-os .window-controls li { 128 | color: #999999; 129 | background-color: rgba(158, 158, 158, 0.2); } 130 | body.linux-os .window-controls li:hover { 131 | background-color: rgba(158, 158, 158, 0.4); } 132 | body.linux-os .window-controls li:active { 133 | background-color: rgba(158, 158, 158, 0.6); } 134 | body.linux-os .window-controls .close:hover { 135 | color: white; 136 | background-color: #e81123; } 137 | body.linux-os .window-controls .close:active { 138 | color: white; 139 | background-color: #f1707a; } 140 | body.linux-os.fullscreen .window-controls .toggle-fullscreen, 141 | body.linux-os.always-on-top .window-controls .toggle-always-on-top { 142 | box-shadow: 0 0 14px 0 #5a5a5a; } 143 | 144 | 145 | 146 | .tabs-list { 147 | background-color: white; } 148 | .tabs-list::-webkit-scrollbar-thumb { 149 | background-color: #e2e2e2; } 150 | 151 | .tab { 152 | color: #757575 !important; } 153 | .tab:after { 154 | background-color: #f9c11d; } 155 | .tab.active { 156 | background-color: rgba(158, 158, 158, 0.2); } 157 | /*.sortable-chosen { 158 | background-color: rgba(255, 255, 255, 0.20); }*/ 159 | 160 | .close-tab { 161 | background-color: rgba(0, 0, 0, 0.04); } 162 | .close-tab:hover { 163 | background-color: rgba(0, 0, 0, 0.08); } 164 | .close-tab:active { 165 | background-color: rgba(0, 0, 0, 0.14); } 166 | 167 | .close-tab-cancel { 168 | background-color: rgba(0, 0, 0, 0.04); } 169 | .close-tab-cancel:hover { 170 | background-color: rgba(0, 0, 0, 0.08); } 171 | .close-tab-cancel:active { 172 | background-color: rgba(0, 0, 0, 0.14); } 173 | 174 | .close-tab-confirm { 175 | color: white; 176 | background-color: #cc4d51; } 177 | .close-tab-confirm:hover { 178 | background-color: #e2565a; } 179 | .close-tab-confirm:active { 180 | background-color: #c1494d; } 181 | 182 | .tab-bar-button { 183 | color: #949494; } 184 | 185 | .main-menu { 186 | background-color: white; } 187 | .main-menu li { 188 | color: #949494; } 189 | 190 | 191 | #global-unsaved-mark_unc2741.protected-id { 192 | background-color: #f9c11d; } 193 | 194 | 195 | 196 | /* Workspace */ 197 | 198 | searchresult { 199 | color: white; 200 | background-color: #155fff; } 201 | 202 | 203 | 204 | /* WYSIWYG Content */ 205 | 206 | .wysiwyg-content h1, 207 | .wysiwyg-content h2, 208 | .wysiwyg-content h3, 209 | .wysiwyg-content h4, 210 | .wysiwyg-content h5, 211 | .wysiwyg-content h6 { 212 | /* Selected CSS properties from MDL .mdl-card__title & .mdl-card__title-text */ 213 | color: black; } 214 | 215 | .wysiwyg-content a { 216 | color: #4078c0; } 217 | 218 | .wysiwyg-content table, 219 | .wysiwyg-content table td { 220 | border-color: #e3e3e3; } 221 | 222 | .wysiwyg-content pre { 223 | color: #657b83; 224 | background-color: #f7f7f7; } 225 | 226 | 227 | 228 | /* Toolbar */ 229 | 230 | #toolbar_unc2741.protected-id { 231 | background-color: white; 232 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } 233 | #toolbar_unc2741.protected-id .toolbar-button.active { 234 | background-color: rgba(158, 158, 158, 0.4); } 235 | #toolbar_unc2741.protected-id input[type="text"].invalid { 236 | color: white; 237 | background-color: #ed6c63; } 238 | 239 | 240 | 241 | /* Context Info */ 242 | 243 | #minor-info-bar_unc2741.protected-id { 244 | color: white; 245 | background-color: rgba(150, 150, 150, 0.9); } 246 | 247 | .menu-list { 248 | background-color: white; 249 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } 250 | .menu-list .label { 251 | color: black; } 252 | 253 | 254 | 255 | /* Dialogs */ 256 | 257 | .mdl-card__supporting-text a { 258 | color: inherit !important; } 259 | 260 | .preview-list .mdl-list__item:active, 261 | .preview-list .mdl-list__item.active { 262 | background-color: #eee; } 263 | 264 | #table-content-dialog_unc2741.protected-id .mdl-list__item-secondary-action { 265 | color: #a0a0a0; } 266 | 267 | #shortcuts-dialog_unc2741.protected-id h6 { 268 | color: black; } 269 | #shortcuts-dialog_unc2741.protected-id .mdl-list__item:nth-child(even) { 270 | background-color: #eee; } 271 | 272 | #about-dialog_unc2741.protected-id .new-update { 273 | color: #f1c40f; } 274 | #about-dialog_unc2741.protected-id .ajax-error { 275 | color: white; 276 | background-color: #e27266; } 277 | #about-dialog_unc2741.protected-id .ajax-error a { 278 | color: white; } 279 | 280 | #about-dialog_unc2741.protected-id .author-info .material-icons { 281 | color: #d00633; } 282 | 283 | #about-dialog_unc2741.protected-id .donate { 284 | color: white; 285 | background-color: #3a85d4; } 286 | #about-dialog_unc2741.protected-id .donate:hover { 287 | background-color: #0dd683; } 288 | #about-dialog_unc2741.protected-id .donate:active { 289 | background-color: #10a545; } 290 | 291 | 292 | 293 | /* Context Info */ 294 | 295 | #preview-large_unc2741.protected-id { 296 | background-color: white; 297 | box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12); } 298 | #preview-large_unc2741.protected-id .label { 299 | color: black; 300 | text-align: center; 301 | background-color: #eee; } 302 | -------------------------------------------------------------------------------- /app/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/app/.keep -------------------------------------------------------------------------------- /build/background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/background.png -------------------------------------------------------------------------------- /build/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icon.icns -------------------------------------------------------------------------------- /build/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icon.ico -------------------------------------------------------------------------------- /build/icons/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icons/.keep -------------------------------------------------------------------------------- /build/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icons/128x128.png -------------------------------------------------------------------------------- /build/icons/16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icons/16x16.png -------------------------------------------------------------------------------- /build/icons/24x24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icons/24x24.png -------------------------------------------------------------------------------- /build/icons/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icons/256x256.png -------------------------------------------------------------------------------- /build/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icons/32x32.png -------------------------------------------------------------------------------- /build/icons/48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icons/48x48.png -------------------------------------------------------------------------------- /build/icons/512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icons/512x512.png -------------------------------------------------------------------------------- /build/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icons/64x64.png -------------------------------------------------------------------------------- /build/icons/96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/build/icons/96x96.png -------------------------------------------------------------------------------- /dist/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/dist/.keep -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var del = require('del'); 3 | var concat = require('gulp-concat'); 4 | var replace = require('gulp-replace'); 5 | var exec = require('child_process').exec; 6 | 7 | 8 | gulp.task('empty-app-folder', function () { 9 | del([ 10 | 'app/**/*', 11 | '!app/.keep' 12 | ]); 13 | }); 14 | 15 | 16 | gulp.task('raw-copy', ['empty-app-folder'], function () { 17 | return gulp.src([ 18 | 'app-dev/**', 19 | 20 | '!app-dev/css/**', 21 | '!app-dev/js/**', 22 | '!app-dev/lib', 23 | '!app-dev/lib/**', 24 | '!app-dev/**/assets', 25 | '!app-dev/**/assets/**' 26 | ]) 27 | .pipe(gulp.dest('app/')) 28 | }); 29 | 30 | 31 | gulp.task('css-app', ['empty-app-folder'], function () { 32 | return gulp.src([ 33 | 'app-dev/lib/normalize.custom.css', 34 | 35 | 'app-dev/lib/emojify-base.min.css', 36 | 'app-dev/lib/emojify-emoticons.min.css', 37 | 'app-dev/lib/emojify.min.css', 38 | 39 | 'app-dev/lib/material.min.css', 40 | 'app-dev/lib/getmdl-select.min.css', 41 | 42 | 'app-dev/css/lib-overwrite.css', 43 | 'app-dev/css/fonts.css', 44 | 'app-dev/css/global.css', 45 | 'app-dev/css/header.css', 46 | 'app-dev/css/workspace.css', 47 | 'app-dev/css/wysiwyg-content.css', 48 | 'app-dev/css/toolbar.css', 49 | 'app-dev/css/context-info.css', 50 | 'app-dev/css/dialogs.css' 51 | ]) 52 | .pipe(concat('style.css')) 53 | .pipe(gulp.dest('app/css/')) 54 | }); 55 | 56 | 57 | gulp.task('js-app', ['empty-app-folder'], function () { 58 | return gulp.src([ 59 | 'app-dev/js/modules.js', 60 | 61 | 'app-dev/lib/material.min.js', 62 | 'app-dev/lib/getmdl-select.min.js', 63 | 'app-dev/lib/versions-compare.js', 64 | 'app-dev/lib/foreach.min.js', 65 | 'app-dev/lib/LightRange.ES6.js', 66 | 'app-dev/lib/wysiwyg.min.js', 67 | 'app-dev/lib/Sortable.no-loader.min.js', 68 | 'app-dev/lib/mousetrap.min.js', 69 | 'app-dev/lib/mousetrap-global-bind.min.js', 70 | 'app-dev/lib/purify.no-loader.min.js', 71 | 'app-dev/lib/Countable.js', 72 | 'app-dev/lib/findAndReplaceDOMText.no-loader.js', 73 | 'app-dev/lib/emojify.min.js', 74 | 'app-dev/lib/zenscroll.no-loader.min.js', 75 | 'app-dev/lib/reqwest.no-loader.min.js', 76 | 'app-dev/lib/to-markdown.custom.js', 77 | 'app-dev/lib/marked.min.js', 78 | 'app-dev/lib/jsVideoUrlParser.min.js', 79 | 80 | 'app-dev/js/functions/IO.functions.js', 81 | 'app-dev/js/functions/Window.functions.js', 82 | 'app-dev/js/functions/Utils.functions.js', 83 | 'app-dev/js/functions/Documents.functions.js', 84 | 'app-dev/js/functions/Content.functions.js', 85 | 'app-dev/js/functions/Toolbar.functions.js', 86 | 'app-dev/js/functions/Dialogs.functions.js', 87 | 'app-dev/js/functions/Remote.functions.js', 88 | 89 | 'app-dev/js/classes/Document.class.js', 90 | 91 | 'app-dev/js/init.js', 92 | 'app-dev/js/window.js', 93 | 'app-dev/js/tabs.js', 94 | 'app-dev/js/toolbar.js', 95 | 'app-dev/js/context-info.js', 96 | 'app-dev/js/dialogs.js', 97 | 'app-dev/js/save.js', 98 | 'app-dev/js/search.js', 99 | 'app-dev/js/settings.js', 100 | 'app-dev/js/remote-check.js' 101 | ]) 102 | .pipe(concat('script.js')) 103 | .pipe(gulp.dest('app/js/')) 104 | }); 105 | 106 | 107 | gulp.task('html-app', ['raw-copy'], function () { 108 | return gulp.src([ 109 | 'app/views/main.html' 110 | ]) 111 | .pipe(replace(/[\s\S]+/, '')) 112 | .pipe(replace(/[\s\S]+/, '')) 113 | .pipe(gulp.dest('app/views/')) 114 | }); 115 | 116 | 117 | 118 | 119 | gulp.task('default', ['empty-app-folder', 'raw-copy', 'css-app', 'js-app', 'html-app']); 120 | 121 | 122 | 123 | 124 | gulp.task('empty-dist-folder', function () { 125 | del([ 126 | 'dist/**/*', 127 | '!dist/.keep' 128 | ]); 129 | }); 130 | 131 | 132 | gulp.task('dist', ['empty-dist-folder'], function (cb) { 133 | 134 | var strCommand = ''; 135 | if (/^win/.test(process.platform)) { 136 | strCommand = 'node_modules\\.bin\\build'; 137 | } else { 138 | strCommand = 'node_modules/.bin/build'; 139 | } 140 | 141 | // https://github.com/robrich/gulp-exec#usage 142 | exec(strCommand, function (err, stdout, stderr) { 143 | if (err) { 144 | cb(err); 145 | } else { 146 | console.log(stdout); 147 | } 148 | // console.log(stderr); 149 | }); 150 | }); 151 | -------------------------------------------------------------------------------- /node_modules/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/node_modules/.keep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "build": { 3 | "appId": "com.n457.Uncolored", 4 | "copyright": "Copyright © 2016 n457 / Bertrand Vignaud-Lerouge", 5 | "asar": false, 6 | "compression": "maximum", 7 | "win": { 8 | "target": [ 9 | "nsis", 10 | "zip" 11 | ] 12 | }, 13 | "mac": { 14 | "category": "public.app-category.productivity", 15 | "target": [ 16 | "dmg", 17 | "zip" 18 | ] 19 | }, 20 | "dmg": { 21 | "contents": [ 22 | { 23 | "x": 448, 24 | "y": 344, 25 | "type": "link", 26 | "path": "/Applications" 27 | }, 28 | { 29 | "x": 192, 30 | "y": 344, 31 | "type": "file" 32 | } 33 | ] 34 | }, 35 | "linux": { 36 | "category": "Office", 37 | "packageCategory": "editors", 38 | "target": [ 39 | "deb", 40 | "zip", 41 | "AppImage" 42 | ], 43 | "synopsis": "(Un)colored — Next generation desktop rich content editor that saves documents with themes. HTML & Markdown compatible. For Windows, OS X & Linux.", 44 | "desktop": { 45 | "Type": "Application", 46 | "Encoding": "UTF-8", 47 | "Name": "(Un)colored", 48 | "Comment": "Rich content editor", 49 | "Exec": "Uncolored", 50 | "Terminal": false 51 | } 52 | } 53 | }, 54 | "scripts": { 55 | "gulp": "gulp", 56 | "dist": "gulp dist" 57 | }, 58 | "devDependencies": { 59 | "del": "^2.2.2", 60 | "electron": "^1.4.5", 61 | "electron-builder": "^6.7.7", 62 | "gulp": "^3.9.1", 63 | "gulp-concat": "^2.6.0", 64 | "gulp-replace": "^0.5.4" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-BoldItalic.woff2 -------------------------------------------------------------------------------- /project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-Italic.woff2 -------------------------------------------------------------------------------- /project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-Light.woff2 -------------------------------------------------------------------------------- /project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-LightItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-LightItalic.woff2 -------------------------------------------------------------------------------- /project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/doc-theme-creation-pack/assets/fonts/Roboto/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /project/doc-theme-creation-pack/assets/img/sun-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/doc-theme-creation-pack/assets/img/sun-blue.png -------------------------------------------------------------------------------- /project/doc-theme-creation-pack/assets/lib/document.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: auto; 3 | -webkit-font-smoothing: antialiased; 4 | -moz-osx-font-smoothing: grayscale; } 5 | 6 | .wysiwyg-content main > :first-child { 7 | padding-top: 0 !important; 8 | margin-top: 0 !important; } 9 | .wysiwyg-content main > :last-child { 10 | padding-bottom: 0 !important; 11 | margin-bottom: 0 !important; } 12 | 13 | .wysiwyg-content img { 14 | max-width: 100%; } 15 | 16 | .wysiwyg-content iframe { 17 | display: block; 18 | max-width: 100%; 19 | border: 0; } 20 | 21 | .wysiwyg-content .emoji .emoji-text { 22 | font-size: 0; } 23 | -------------------------------------------------------------------------------- /project/doc-theme-creation-pack/assets/lib/document.js: -------------------------------------------------------------------------------- 1 | /*! foreach.js v1.1.0 | (c) 2014 @toddmotto | https://github.com/toddmotto/foreach */ 2 | var forEach=function(t,o,r){if("[object Object]"===Object.prototype.toString.call(t))for(var c in t)Object.prototype.hasOwnProperty.call(t,c)&&o.call(r,t[c],c,t);else for(var e=0,l=t.length;l>e;e++)o.call(r,t[e],e,t)}; 3 | 4 | 5 | (function () { 6 | 7 | // Checking for an Internet connection 8 | // From http://youmightnotneedjquery.com/#request IE8+ 9 | var Request = new XMLHttpRequest(); 10 | // Load a tiny library from a reliable source to check if a real Internet connexion is available. 11 | Request.open('GET', 'https://ajax.googleapis.com/ajax/libs/threejs/r76/three.min.js', true); 12 | Request.onreadystatechange = function () { 13 | if (this.readyState === 4) { 14 | if (this.status >= 200 && this.status < 400) { 15 | // Success. Do nothing. 16 | } else { 17 | alert('Apparently, there is no Internet connexion. Some ressources may not load, like remote images.'); 18 | } 19 | } 20 | }; 21 | Request.send(); 22 | Request = null; 23 | 24 | 25 | // Executing highlight.js if there is at least one
26 |   forEach(document.getElementsByTagName('pre'), ($CodeBlock) => {
27 |     hljs.highlightBlock($CodeBlock);
28 |   });
29 | 
30 | })();
31 | 


--------------------------------------------------------------------------------
/project/doc-theme-creation-pack/assets/lib/normalize.custom.min.css:
--------------------------------------------------------------------------------
1 | /*! normalize.css v4.2.0 | MIT License | github.com/necolas/normalize.css */button,hr,input{overflow:visible}audio,canvas,progress,video{display:inline-block}[type=checkbox],[type=radio],legend{box-sizing:border-box;padding:0}html{font-family:sans-serif;line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}a:active,a:hover{outline-width:0}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}audio:not([controls]){display:none;height:0}img{border-style:none}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{color:inherit;display:table;max-width:100%;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}[hidden],template{display:none}
2 | 


--------------------------------------------------------------------------------
/project/doc-theme-creation-pack/highlight.js-theme.min.css:
--------------------------------------------------------------------------------
1 | .hljs{display:block;overflow-x:auto;padding:.5em;color:#333;background:#f8f8f8}.hljs-comment,.hljs-quote{color:#998;font-style:italic}.hljs-keyword,.hljs-selector-tag,.hljs-subst{color:#333;font-weight:700}.hljs-literal,.hljs-number,.hljs-tag .hljs-attr,.hljs-template-variable,.hljs-variable{color:teal}.hljs-doctag,.hljs-string{color:#d14}.hljs-section,.hljs-selector-id,.hljs-title{color:#900;font-weight:700}.hljs-subst{font-weight:400}.hljs-class .hljs-title,.hljs-type{color:#458;font-weight:700}.hljs-attribute,.hljs-name,.hljs-tag{color:navy;font-weight:400}.hljs-meta,.hljs-strong{font-weight:700}.hljs-link,.hljs-regexp{color:#009926}.hljs-bullet,.hljs-symbol{color:#990073}.hljs-built_in,.hljs-builtin-name{color:#0086b3}.hljs-meta{color:#999}.hljs-deletion{background:#fdd}.hljs-addition{background:#dfd}.hljs-emphasis{font-style:italic}
2 | 


--------------------------------------------------------------------------------
/project/doc-theme-creation-pack/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/doc-theme-creation-pack/preview.png


--------------------------------------------------------------------------------
/project/doc-theme-creation-pack/script.js:
--------------------------------------------------------------------------------
1 | (function () {
2 | 
3 | })();
4 | 


--------------------------------------------------------------------------------
/project/doc-theme-creation-pack/template.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |   
 4 |     
 5 |     
 6 | 
 7 |     
 8 |     
 9 |     
10 |     
11 |     
12 | 
13 |     
30 |   
31 |   
32 |     
33 |     
34 | 
35 |     
36 | 37 |

The afterglow of the daylight

In addition to those fine engravings from Garnery, there are two other French engravings worthy of note, by some one who subscribes himself "H. Durand." One of them, though not precisely adapted to our present purpose, nevertheless deserves mention on other accounts. It is a quiet noon-scene among the isles of the Pacific; a French whaler anchored, inshore, in a calm, and lazily taking water on board; the loosened sails of the ship :sailboat: , and the long leaves of the palms in the background, both drooping together in the breezeless air. The effect is very fine, when considered with reference to

Finally there were but three of us left, a great green warrior of some far northern horde, Kantos Kan, and myself.

They say that men who have seen the world, thereby become quite at ease in manner, quite self-possessed in company. Not always, though: Ledyard, the great New England traveller, and Mungo Park, the Scotch one; of all men,

  • they possessed the least assurance in the parlor.
  • But perhaps the mere crossing of Siberia :snowflake: in a edge drawn by dogs as Ledyard did,
  • or the taking a long solitary walk on an empty stomach, in the negro heart of Africa,

which was the sum of poor Mungo's performances—this kind of travel, I say, may not be the very best mode of attaining a high social polish. Still, for the most part, that sort of thing is to be had anywhere.

Why not, after all ? It's a chance perhaps the only one

He had commenced by saying to himself, "What folly!" and then he repeated, "Why not, after all? It's a chance perhaps the only one; and with such sots!" Thinking thus, he slipped, with the suppleness of a serpent :snake: , to the lowest branches, the ends of which bent almost to the ground.

Twenty minutes before nine

Phileas Fogg and his servant seated themselves in a first-class carriage at twenty minutes before nine; five minutes later the whistle screamed, and the train slowly glided out of the station.

The occasional howling of the Martians had ceased; they took up their positions in the huge crescent about their cylinders in absolute silence. It was a crescent with twelve miles between its horns. Never since the devising of gunpowder was the beginning of a battle so still. To us and to an observer about Ripley it would have had precisely the same effect--the Martians seemed in solitary possession of the darkling night, lit only as it was by the slender moon, the stars, the afterglow of the daylight, and the ruddy glare from St. George's Hill and the woods of Painshill.

They left the cottage and walked through the trees

They left the cottage and walked through the trees until they found a little spring of clear water, where Dorothy drank and bathed and ate her breakfast. She saw there was not much bread left in the basket, and the girl was thankful the Scarecrow did not have to eat anything, for there was scarcely enough for herself and Toto for the day.

42 = 16, Go and gaze upon the iron emblematical harpoons round yonder lofty mansion, and your question will be answered. Yes; all these brave houses and flowery gardens came from the Atlantic, Pacific, and Indian oceans :ocean: . One and all, they were harpooned and dragged up hither from the bottom of the sea. Can Herr Alexander perform a feat like that?

This wild outbreak

This wild outbreak on the part of Sola so greatly surprised and shocked the other women, that, after a few words of general reprimand, they all lapsed into silence and were soon asleep. One thing the episode had accomplished was to assure me of Sola's friendliness toward the C6H4  :pill: poor girl, and also to convince me that :

  1. I had been extremely fortunate in falling into her hands
  2. rather than those
  3. of some of the other females.

I knew that she was fond of me, and now that I had discovered that she hated cruelty and barbarity I was confident that I could depend upon her to aid me and the girl captive to escape, provided of course that such a thing was within the range of possibilities.

"They are monstrous beasts with bodies like bears and heads like tigers :tiger:," replied the Lion, "and with claws so long and sharp that they could tear me in two as easily as I could kill Toto. I'm terribly afraid :scream: of the Kalidahs."

"But," he continued, in his fierce guttural tones, "if you run off with the red girl it is I who shall have to account to Tal Hajus; it is I who shall have to face Tars Tarkas, and either demonstrate my right to command, or the metal from my dead carcass will go to a better man, for such is the custom of the Tharks.

"And you can't see these germ-things, Granser," Hare-Lip objected, "and here you gabble, gabble, gabble about them as if they was anything, when they're nothing at all. Anything you can't see, ain't, that's what. Fighting things that ain't with things that ain't! They must have been all fools in them days. That's why they croaked. I ain't goin' to believe in such rot, I tell you that."

"So he is," said the green man

"So he is," said the green man, "and he rules the Emerald City wisely and well. But to those who are not honest, or who approach him from curiosity, he is most terrible, and few have ever dared ask to see his face. I am the Guardian of the Gates, and since you demand to see the Great Oz I must take you to his Palace. But first you must put on the spectacles."

It was this, as much as anything

It was this, as much as anything, that gave people courage, and I suppose the new arrivals from Woking also helped to restore confidence. At any rate, as the dusk came on a slow, intermittent movement upon the sand pits began, a movement that seemed to gather force as the stillness of the evening about the cylinder remained unbroken. Vertical black figures in twos and threes would advance, stop, watch, and advance again, spreading out as they did so in a thin irregular crescent that promised to enclose the pit in its attenuated horns. I, too, on my side began to move towards the pit.

At last the Mouse

At last the Mouse, who seemed to be a person of authority among them, called out, 'Sit down, all of you, and listen to me! I'LL soon make you dry enough!' They all sat down at once, in a large ring, with the Mouse in the middle. Alice kept her eyes anxiously fixed on it, for she felt sure she would catch a bad cold if she did not get dry very soon.

38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /project/docs/README.md: -------------------------------------------------------------------------------- 1 | ## Full Documentation 2 | 3 | 4 | ### Launch (Un)colored Development Version 5 | 6 | 7 | 8 | ### Build (Un)colored 9 | 10 | #### Windows 11 | - *(recommended)* Install [cmder](http://cmder.net/) full version 12 | - *(recommended)* Install [Scoop](http://scoop.sh/) 13 | - Update Scoop & its apps if needed (`scoop update && scoop update *`) 14 | - Install Node.js (`scoop install nodejs` or https://nodejs.org/en/ "Current" version) 15 | - `npm rm --global gulp` [if needed](https://github.com/gulpjs/gulp/blob/master/docs/getting-started.md#1-install-gulp-globally) 16 | - `npm install --global gulp-cli` 17 | - *(in the repo)* `npm install` 18 | - *(in the repo)* `gulp` 19 | - *(in the repo)* in root `package.json`, remove unnecessary output format in `win.target` array 20 | - *(in the repo)* gulp dist 21 | 22 | #### Ubuntu 23 | - Install Ubuntu 16.04 LTS 64-bit (used exact v16.04.7) (from https://releases.ubuntu.com) 24 | - `sudo apt-get install git curl` 25 | - Install Node.js v7.x (used exact v7.10.1), NPM v4.x (used exact v4.2.0): `curl -fsSL https://deb.nodesource.com/setup_7.x | sudo -E bash -`, then `sudo apt-get install -y nodejs` (from https://github.com/nodesource/distributions/blob/master/README.md#deb) 26 | - Clone the repository. 27 | - *(in the repo)* `npm install` 28 | - *(in the repo)* in root `package.json`, remove incompatible & unnecessary output format in `linux.target` array 29 | - *(in the repo)* `npm run gulp`, then `npm run dist` 30 | 31 | 32 | if you modify the icon, don't forget to buid the .ico as a multi-layer version with GIMP. 33 | 34 | 35 | 36 | ### How (Un)colored is made 37 | 38 | 39 | 40 | ### Useful links 41 | 42 | For future updates of this full doc. 43 | 44 | - https://docs.npmjs.com/files/package.json 45 | - http://toddmotto.com/ditch-the-array-foreach-call-nodelist-hack/ 46 | - https://www.giftofspeed.com/base64-encoder/ 47 | - http://catalyst.net.nz/news/creating-multi-resolution-favicon-including-transparency-gimp 48 | - http://www.visualpharm.com/articles/icon_sizes.html 49 | - http://www.thewindowsclub.com/rebuild-the-icon-cache-windows 50 | - https://github.com/electron-userland/electron-builder/wiki/Options 51 | - https://github.com/electron-userland/electron-builder/issues/239#issuecomment-224365515 52 | - https://github.com/electron-userland/electron-builder/issues/239#issuecomment-224495871 53 | - https://github.com/LinusU/node-appdmg#example 54 | - https://www.gimp.org/ 55 | - https://github.com/lemonmojo/IconComposer2x 56 | -------------------------------------------------------------------------------- /project/docs/assets/uncolored-large-screenshot-win.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/docs/assets/uncolored-large-screenshot-win.png -------------------------------------------------------------------------------- /project/docs/assets/uncolored-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/docs/assets/uncolored-logo.png -------------------------------------------------------------------------------- /project/docs/priority-info.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/docs/priority-info.md -------------------------------------------------------------------------------- /project/document-examples/sun-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/document-examples/sun-blue.png -------------------------------------------------------------------------------- /project/images/README.md: -------------------------------------------------------------------------------- 1 | ## (Un)colored images project files 2 | 3 | ### Requirements 4 | 5 | - [Adobe Photoshop CC](https://www.adobe.com/fr/products/photoshop.html) 6 | - [Adobe Illustrator CC](https://www.adobe.com/fr/products/illustrator.html) 7 | - [GIMP](https://www.gimp.org/) 8 | -------------------------------------------------------------------------------- /project/images/app-dmg-background-osx.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/app-dmg-background-osx.ai -------------------------------------------------------------------------------- /project/images/app-icon.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/app-icon.ai -------------------------------------------------------------------------------- /project/images/app-splash-screen.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/app-splash-screen.ai -------------------------------------------------------------------------------- /project/images/app-splash-screen_anim.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/app-splash-screen_anim.psd -------------------------------------------------------------------------------- /project/images/assets/Lato-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/assets/Lato-Light.ttf -------------------------------------------------------------------------------- /project/images/assets/README.md: -------------------------------------------------------------------------------- 1 | https://www.fontsquirrel.com/fonts/lato 2 | https://www.fontsquirrel.com/fonts/roboto 3 | https://github.com/LinusU/node-appdmg/blob/master/test/assets/TestBkg.png 4 | http://www.visualpharm.com/articles/icon_sizes.html 5 | https://cloudconvert.com 6 | -------------------------------------------------------------------------------- /project/images/assets/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/assets/Roboto-Light.ttf -------------------------------------------------------------------------------- /project/images/assets/TestBkg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/assets/TestBkg.png -------------------------------------------------------------------------------- /project/images/icon_windows_multi-layered.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/icon_windows_multi-layered.xcf -------------------------------------------------------------------------------- /project/images/render/1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/render/1024x1024.png -------------------------------------------------------------------------------- /project/images/render/old/install-spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/render/old/install-spinner.gif -------------------------------------------------------------------------------- /project/images/render/uncolored-logo-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/render/uncolored-logo-mini.png -------------------------------------------------------------------------------- /project/images/website/focus-on-content.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/website/focus-on-content.psd -------------------------------------------------------------------------------- /project/images/website/markdown-compatible.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/website/markdown-compatible.psd -------------------------------------------------------------------------------- /project/images/website/rich-content-tools.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/website/rich-content-tools.psd -------------------------------------------------------------------------------- /project/images/website/softpedia-certificate.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/website/softpedia-certificate.psd -------------------------------------------------------------------------------- /project/images/website/uncolored-large-screenshot.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/n457/Uncolored/129f927a289c51d69aa51102a6414a53a74fa518/project/images/website/uncolored-large-screenshot.psd -------------------------------------------------------------------------------- /project/launcher-models/linux.sh: -------------------------------------------------------------------------------- 1 | /path-to-electron/electron /path-to-repo/app-dev 2 | -------------------------------------------------------------------------------- /project/launcher-models/osx.sh: -------------------------------------------------------------------------------- 1 | /path-to-electron/Electron.app/Contents/MacOS/Electron /path-to-repo/app-dev 2 | -------------------------------------------------------------------------------- /project/launcher-models/win.bat: -------------------------------------------------------------------------------- 1 | start "" "path-to-electron\electron.exe" "path-to-repo\app-dev" 2 | --------------------------------------------------------------------------------