├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── makebeta ├── makerelease ├── screenshots ├── contextmenu.png ├── page.png ├── page_with_bg.png ├── properties_main.png ├── settings_cells.png ├── settings_grid.png └── settings_page.png └── src ├── _locales ├── de │ └── messages.json ├── en │ └── messages.json ├── es │ └── messages.json ├── fr │ └── messages.json ├── he_IL │ └── messages.json ├── it │ └── messages.json ├── pl │ └── messages.json ├── pt_BR │ └── messages.json ├── ru │ └── messages.json ├── sr │ └── messages.json └── uk │ └── messages.json ├── dial ├── html ├── properties.html └── settings.html ├── img ├── 24.png ├── 32.png ├── README.md ├── back.png ├── folder.png └── throbber.gif ├── js ├── background.js ├── dial.js ├── properties.js └── settings.js └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | **/src.zip 2 | /*.zip 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | * Open an issue before work to hard 4 | * Work on the develop branch 5 | * Use tab char for identation 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quickdial-webext 2 | 3 | QuickDial is a WebExt Dial page for Firefox 4 | 5 | [QuickDial on addons.mozilla.org](https://addons.mozilla.org/fr/firefox/addon/quick-dial/?src=search) 6 | 7 | ## Contribute and evolution :
8 | * [CONTRIBUTING.md](https://github.com/MatMoul/quickdial-webext/blob/develop/CONTRIBUTING.md) 9 | 10 | -------------------------------------------------------------------------------- /makebeta: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$1" == "" ]; then 4 | echo "Error: No version provided" 5 | echo "./makebeta 0.0.1b1" 6 | exit 1 7 | fi 8 | 9 | clear 10 | branch=$(git rev-parse --abbrev-ref HEAD) 11 | read -p "Current branch is $branch. Continue ? (y/N)" choice 12 | case "$choice" in 13 | n|N|'' ) 14 | echo "Cancel !" 15 | exit 1 16 | ;; 17 | y|Y ) echo "Make beta...";; 18 | * ) 19 | echo "Cancel !" 20 | exit 1 21 | ;; 22 | esac 23 | 24 | 25 | 26 | # Ready to update : 27 | 28 | git branch beta 29 | git checkout beta 30 | 31 | version=$1 32 | 33 | sed -i "/\"version\":/c\ \ \"version\": \"$version\"," src/manifest.json 34 | sed -i 's/quickdial@matmoul.com/quickdialtest@matmoul.com/' src/manifest.json 35 | 36 | git commit -a -m "Beta Version $version" 37 | 38 | cd src 39 | zip -r ../quickdial-beta-$version.zip . -x src.zip 40 | cd .. 41 | 42 | git checkout $branch 43 | git branch -D beta 44 | 45 | -------------------------------------------------------------------------------- /makerelease: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$1" == "" ]; then 4 | echo "Error: No version provided" 5 | echo "./makerelease 0.0.1" 6 | exit 1 7 | fi 8 | 9 | ssh -T git@github.com 10 | if [ ! "$?" = "1" ]; then 11 | echo "No Github ssh key loaded exiting..." 12 | exit 1 13 | fi 14 | 15 | clear 16 | branch=$(git rev-parse --abbrev-ref HEAD) 17 | read -p "Current branch is $branch. Continue ? (y/N)" choice 18 | case "$choice" in 19 | n|N|'' ) 20 | echo "Cancel !" 21 | exit 1 22 | ;; 23 | y|Y ) echo "Make release...";; 24 | * ) 25 | echo "Cancel !" 26 | exit 1 27 | ;; 28 | esac 29 | 30 | 31 | 32 | # Ready to update : 33 | 34 | version=$1 35 | 36 | sed -i "/\"version\":/c\ \ \"version\": \"$version\"," src/manifest.json 37 | 38 | git commit -a -m "Version $version" 39 | git push 40 | 41 | git checkout master 42 | git merge develop 43 | git push 44 | 45 | git tag -a "v$version" -m "Version $version" 46 | git push --tags 47 | 48 | git checkout $branch 49 | 50 | cd src 51 | zip -r ../quickdial-$version.zip . -x src.zip 52 | cd .. 53 | -------------------------------------------------------------------------------- /screenshots/contextmenu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/screenshots/contextmenu.png -------------------------------------------------------------------------------- /screenshots/page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/screenshots/page.png -------------------------------------------------------------------------------- /screenshots/page_with_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/screenshots/page_with_bg.png -------------------------------------------------------------------------------- /screenshots/properties_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/screenshots/properties_main.png -------------------------------------------------------------------------------- /screenshots/settings_cells.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/screenshots/settings_cells.png -------------------------------------------------------------------------------- /screenshots/settings_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/screenshots/settings_grid.png -------------------------------------------------------------------------------- /screenshots/settings_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/screenshots/settings_page.png -------------------------------------------------------------------------------- /src/_locales/de/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "menuAddToQuickDial": { 3 | "message": "Zu Quick Dial hinzufügen", 4 | "description": "Text of add bookmark menu item." 5 | }, 6 | 7 | "menuNew": { 8 | "message": "Neu", 9 | "description": "Text of new menu item." 10 | }, 11 | 12 | "menuNewBookmark": { 13 | "message": "Lesezeichen", 14 | "description": "Text of add bookmark menu item." 15 | }, 16 | 17 | "AddBookmarkPrompt": { 18 | "message": "Neue Lesezeichen URL eingeben:", 19 | "description": "Text of the add bookmark prompt." 20 | }, 21 | 22 | "menuNewFolder": { 23 | "message": "Ordner", 24 | "description": "Text of add folder menu item." 25 | }, 26 | 27 | "AddFolderPrompt": { 28 | "message": "Neuen Ordner Namen eingeben:", 29 | "description": "Text of the add folder prompt." 30 | }, 31 | 32 | "menuProperties": { 33 | "message": "Bearbeiten", 34 | "description": "Text of properties menu item." 35 | }, 36 | 37 | "menuRefreshItem": { 38 | "message": "Neu laden", 39 | "description": "Text of refresh menu item." 40 | }, 41 | 42 | "menuCaptureHere": { 43 | "message": "Hier aufnehmen", 44 | "description": "Text of capture here menu item." 45 | }, 46 | 47 | "menuCapturePage": { 48 | "message": "In einem neuen Tab aufnehmen", 49 | "description": "Text of capture menu item." 50 | }, 51 | 52 | "menuDeleteItem": { 53 | "message": "Löschen", 54 | "description": "Text of delete menu item." 55 | }, 56 | 57 | "deleteItemConfimation": { 58 | "message": "Löschen $1?", 59 | "description": "Text of delete confirmation." 60 | }, 61 | 62 | "menuSettings": { 63 | "message": "Quick Dial Einstellungen", 64 | "description": "Text of settings menu item." 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "menuAddToQuickDial": { 3 | "message": "Add to Quick Dial", 4 | "description": "Text of add bookmark menu item." 5 | }, 6 | 7 | "menuNew": { 8 | "message": "New", 9 | "description": "Text of new menu item." 10 | }, 11 | 12 | "menuNewBookmark": { 13 | "message": "Bookmark", 14 | "description": "Text of add bookmark menu item." 15 | }, 16 | 17 | "AddBookmarkPrompt": { 18 | "message": "Enter the new bookmark url :", 19 | "description": "Text of the add bookmark prompt." 20 | }, 21 | 22 | "menuNewFolder": { 23 | "message": "Folder", 24 | "description": "Text of add folder menu item." 25 | }, 26 | 27 | "AddFolderPrompt": { 28 | "message": "Enter the new folder name :", 29 | "description": "Text of the add folder prompt." 30 | }, 31 | 32 | "menuProperties": { 33 | "message": "Properties", 34 | "description": "Text of properties menu item." 35 | }, 36 | 37 | "menuRefreshItem": { 38 | "message": "Refresh", 39 | "description": "Text of refresh menu item." 40 | }, 41 | 42 | "menuCaptureHere": { 43 | "message": "Capture here", 44 | "description": "Text of capture here menu item." 45 | }, 46 | 47 | "menuCapturePage": { 48 | "message": "Capture in a new tab", 49 | "description": "Text of capture menu item." 50 | }, 51 | 52 | "menuDeleteItem": { 53 | "message": "Delete", 54 | "description": "Text of delete menu item." 55 | }, 56 | 57 | "deleteItemConfimation": { 58 | "message": "Delete $1 ?", 59 | "description": "Text of delete confirmation." 60 | }, 61 | 62 | "menuSettings": { 63 | "message": "Quick Dial Settings", 64 | "description": "Text of settings menu item." 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/_locales/es/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "menuAddToQuickDial": { 3 | "message": "Agregar a Quick Dial", 4 | "description": "Text of add bookmark menu item." 5 | }, 6 | 7 | "menuNew": { 8 | "message": "Nuevo", 9 | "description": "Text of new menu item." 10 | }, 11 | 12 | "menuNewBookmark": { 13 | "message": "Marcador", 14 | "description": "Text of add bookmark menu item." 15 | }, 16 | 17 | "AddBookmarkPrompt": { 18 | "message": "Dirección del nuevo marcador:", 19 | "description": "Text of the add bookmark prompt." 20 | }, 21 | 22 | "menuNewFolder": { 23 | "message": "Carpeta", 24 | "description": "Text of add folder menu item." 25 | }, 26 | 27 | "AddFolderPrompt": { 28 | "message": "Nombre de la nueva carpeta:", 29 | "description": "Text of the add folder prompt." 30 | }, 31 | 32 | "menuProperties": { 33 | "message": "Propiedades", 34 | "description": "Text of properties menu item." 35 | }, 36 | 37 | "menuRefreshItem": { 38 | "message": "Actualizar", 39 | "description": "Text of refresh menu item." 40 | }, 41 | 42 | "menuCaptureHere": { 43 | "message": "Capturar aquí", 44 | "description": "Text of capture here menu item." 45 | }, 46 | 47 | "menuCapturePage": { 48 | "message": "Capturar en una nueva pestaña", 49 | "description": "Text of capture menu item." 50 | }, 51 | 52 | "menuDeleteItem": { 53 | "message": "Eliminar", 54 | "description": "Text of delete menu item." 55 | }, 56 | 57 | "deleteItemConfimation": { 58 | "message": "¿Eliminar $1 ?", 59 | "description": "Text of delete confirmation." 60 | }, 61 | 62 | "menuSettings": { 63 | "message": "Propiedades de Quick Dial", 64 | "description": "Text of settings menu item." 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/_locales/fr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "menuAddToQuickDial": { 3 | "message": "Ajouter à Quick Dial", 4 | "description": "Text of add bookmark menu item." 5 | }, 6 | 7 | "menuNew": { 8 | "message": "Nouveau", 9 | "description": "Text of new menu item." 10 | }, 11 | 12 | "menuNewBookmark": { 13 | "message": "Marque-page", 14 | "description": "Text of add bookmark menu item." 15 | }, 16 | 17 | "AddBookmarkPrompt": { 18 | "message": "Entrez l'url du nouveau marque-page :", 19 | "description": "Text of the add bookmark prompt." 20 | }, 21 | 22 | "menuNewFolder": { 23 | "message": "Dossier", 24 | "description": "Text of add folder menu item." 25 | }, 26 | 27 | "AddFolderPrompt": { 28 | "message": "Entrez le nom du nouveau dossier :", 29 | "description": "Text of the add folder prompt." 30 | }, 31 | 32 | "menuProperties": { 33 | "message": "Propriétés", 34 | "description": "Text of properties menu item." 35 | }, 36 | 37 | "menuRefreshItem": { 38 | "message": "Actualiser", 39 | "description": "Text of refresh menu item." 40 | }, 41 | 42 | "menuCaptureHere": { 43 | "message": "Capturer ici", 44 | "description": "Text of capture here menu item." 45 | }, 46 | 47 | "menuCapturePage": { 48 | "message": "Capturer dans un nouvel onglet", 49 | "description": "Text of capture menu item." 50 | }, 51 | 52 | "menuDeleteItem": { 53 | "message": "Supprimer", 54 | "description": "Text of delete menu item." 55 | }, 56 | 57 | "deleteItemConfimation": { 58 | "message": "Supprimer $1 ?", 59 | "description": "Text of delete confirmation." 60 | }, 61 | 62 | "menuSettings": { 63 | "message": "Paramètres Quick Dial", 64 | "description": "Text of settings menu item." 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/_locales/he_IL/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "menuAddToQuickDial": { 3 | "message": "הוסף לחיוג מהיר", 4 | "description": "Text of add bookmark menu item." 5 | }, 6 | 7 | "menuNew": { 8 | "message": "חדש", 9 | "description": "Text of new menu item." 10 | }, 11 | 12 | "menuNewBookmark": { 13 | "message": "מועדף", 14 | "description": "Text of add bookmark menu item." 15 | }, 16 | 17 | "AddBookmarkPrompt": { 18 | "message": "הכנס קישור חדש למועדף :", 19 | "description": "Text of the add bookmark prompt." 20 | }, 21 | 22 | "menuNewFolder": { 23 | "message": "תיקייה", 24 | "description": "Text of add folder menu item." 25 | }, 26 | 27 | "AddFolderPrompt": { 28 | "message": "הכנס שם חדש לתיקייה :", 29 | "description": "Text of the add folder prompt." 30 | }, 31 | 32 | "menuProperties": { 33 | "message": "מאפיינים", 34 | "description": "Text of properties menu item." 35 | }, 36 | 37 | "menuRefreshItem": { 38 | "message": "רענן", 39 | "description": "Text of refresh menu item." 40 | }, 41 | 42 | "menuCaptureHere": { 43 | "message": "צלם מסך כאן", 44 | "description": "Text of capture here menu item." 45 | }, 46 | 47 | "menuCapturePage": { 48 | "message": "צלם מסך בלשונית חדשה", 49 | "description": "Text of capture menu item." 50 | }, 51 | 52 | "menuDeleteItem": { 53 | "message": "מחק", 54 | "description": "Text of delete menu item." 55 | }, 56 | 57 | "deleteItemConfimation": { 58 | "message": "האם למחוק $1 ?", 59 | "description": "Text of delete confirmation." 60 | }, 61 | 62 | "menuSettings": { 63 | "message": "הגדרות חיוג מהיר", 64 | "description": "Text of settings menu item." 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/_locales/it/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "menuAddToQuickDial": { 3 | "message": "Aggiungi a Quick Dial", 4 | "description": "Text of add bookmark menu item." 5 | }, 6 | 7 | "menuNew": { 8 | "message": "Nuovo", 9 | "description": "Text of new menu item." 10 | }, 11 | 12 | "menuNewBookmark": { 13 | "message": "Segnalibro", 14 | "description": "Text of add bookmark menu item." 15 | }, 16 | 17 | "AddBookmarkPrompt": { 18 | "message": "Scrivi l'url del nuovo segnalibro:", 19 | "description": "Text of the add bookmark prompt." 20 | }, 21 | 22 | "menuNewFolder": { 23 | "message": "Cartella", 24 | "description": "Text of add folder menu item." 25 | }, 26 | 27 | "AddFolderPrompt": { 28 | "message": "Scrivi il nome della nuova cartella:", 29 | "description": "Text of the add folder prompt." 30 | }, 31 | 32 | "menuProperties": { 33 | "message": "Proprietà", 34 | "description": "Text of properties menu item." 35 | }, 36 | 37 | "menuRefreshItem": { 38 | "message": "Aggiorna", 39 | "description": "Text of refresh menu item." 40 | }, 41 | 42 | "menuCapturePage": { 43 | "message": "Cattura in una nuova scheda", 44 | "description": "Text of capture menu item." 45 | }, 46 | 47 | "menuDeleteItem": { 48 | "message": "Elimina", 49 | "description": "Text of delete menu item." 50 | }, 51 | 52 | "deleteItemConfimation": { 53 | "message": "Eliminare $1?", 54 | "description": "Text of delete confirmation." 55 | }, 56 | 57 | "menuSettings": { 58 | "message": "Impostazioni Quick Dial", 59 | "description": "Text of settings menu item." 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/_locales/pl/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "menuAddToQuickDial": { 3 | "message": "Dodaj do Quick Dial", 4 | "description": "Text of add bookmark menu item." 5 | }, 6 | 7 | "menuNew": { 8 | "message": "Nowy", 9 | "description": "Text of new menu item." 10 | }, 11 | 12 | "menuNewBookmark": { 13 | "message": "Zakładka", 14 | "description": "Text of add bookmark menu item." 15 | }, 16 | 17 | "AddBookmarkPrompt": { 18 | "message": "Wprowadź nowy adres URL zakładki:", 19 | "description": "Text of the add bookmark prompt." 20 | }, 21 | 22 | "menuNewFolder": { 23 | "message": "Folder", 24 | "description": "Text of add folder menu item." 25 | }, 26 | 27 | "AddFolderPrompt": { 28 | "message": "Wprowadź nazwę nowego folderu:", 29 | "description": "Text of the add folder prompt." 30 | }, 31 | 32 | "menuProperties": { 33 | "message": "Właściwości", 34 | "description": "Text of properties menu item." 35 | }, 36 | 37 | "menuRefreshItem": { 38 | "message": "Odśwież", 39 | "description": "Text of refresh menu item." 40 | }, 41 | 42 | "menuCaptureHere": { 43 | "message": "Zdjęcie tutaj", 44 | "description": "Text of capture here menu item." 45 | }, 46 | 47 | "menuCapturePage": { 48 | "message": "Zdjęcie na nowej karcie", 49 | "description": "Text of capture menu item." 50 | }, 51 | 52 | "menuDeleteItem": { 53 | "message": "Usuń", 54 | "description": "Text of delete menu item." 55 | }, 56 | 57 | "deleteItemConfimation": { 58 | "message": "Usuń $1 ?", 59 | "description": "Text of delete confirmation." 60 | }, 61 | 62 | "menuSettings": { 63 | "message": "Ustawienia Quick Dial", 64 | "description": "Text of settings menu item." 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/_locales/pt_BR/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "menuAddToQuickDial": { 3 | "message": "Adicionar ao Quick Dial", 4 | "description": "Texto do iten de menu adicionar ao Quick Dial." 5 | }, 6 | 7 | "menuNew": { 8 | "message": "Novo", 9 | "description": "Texto do item de menu novo." 10 | }, 11 | 12 | "menuNewBookmark": { 13 | "message": "Favorito", 14 | "description": "Texto do iten de menu adicionar favorito." 15 | }, 16 | 17 | "AddBookmarkPrompt": { 18 | "message": "Entre com a url do novo favorito :", 19 | "description": "Texto do rótulo adicionar favorito." 20 | }, 21 | 22 | "menuNewFolder": { 23 | "message": "Pasta", 24 | "description": "Texto do item de menu adicionar pasta." 25 | }, 26 | 27 | "AddFolderPrompt": { 28 | "message": "Entre com o nome da nova pasta :", 29 | "description": "Texto do rótulo adicionar pasta." 30 | }, 31 | 32 | "menuProperties": { 33 | "message": "Propriedades", 34 | "description": "Texto do item de menu propriedades." 35 | }, 36 | 37 | "menuRefreshItem": { 38 | "message": "Atualizar", 39 | "description": "Texto do item de menu atualizar." 40 | }, 41 | 42 | "menuCaptureHere": { 43 | "message": "Capturar aqui", 44 | "description": "Texto do item de menu capturar aqui." 45 | }, 46 | 47 | "menuCapturePage": { 48 | "message": "Capturar em uma nova guia", 49 | "description": "Texto do item de menu capturar em uma nova guia." 50 | }, 51 | 52 | "menuDeleteItem": { 53 | "message": "Excluir", 54 | "description": "Texto do item de menu excluir." 55 | }, 56 | 57 | "deleteItemConfimation": { 58 | "message": "Excluir $1 ?", 59 | "description": "Texto de confirmação da exclusão." 60 | }, 61 | 62 | "menuSettings": { 63 | "message": " Configurações do Quick Dial", 64 | "description": "Texto do item de menu configurações." 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/_locales/ru/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "menuAddToQuickDial": { 3 | "message": "Добавить в Quick Dial", 4 | "description": "Text of add bookmark menu item." 5 | }, 6 | 7 | "menuNew": { 8 | "message": "Добавить", 9 | "description": "Text of new menu item." 10 | }, 11 | 12 | "menuNewBookmark": { 13 | "message": "Закладку", 14 | "description": "Text of add bookmark menu item." 15 | }, 16 | 17 | "AddBookmarkPrompt": { 18 | "message": "Введите URL-адрес новой закладки:", 19 | "description": "Text of the add bookmark prompt." 20 | }, 21 | 22 | "menuNewFolder": { 23 | "message": "Папку", 24 | "description": "Text of add folder menu item." 25 | }, 26 | 27 | "AddFolderPrompt": { 28 | "message": "Введите имя новой папки:", 29 | "description": "Text of the add folder prompt." 30 | }, 31 | 32 | "menuProperties": { 33 | "message": "Свойства", 34 | "description": "Text of properties menu item." 35 | }, 36 | 37 | "menuRefreshItem": { 38 | "message": "Обновить", 39 | "description": "Text of refresh menu item." 40 | }, 41 | 42 | "menuCaptureHere": { 43 | "message": "Захватить здесь", 44 | "description": "Text of capture here menu item." 45 | }, 46 | 47 | "menuCapturePage": { 48 | "message": "Захватить на новой вкладке", 49 | "description": "Text of capture menu item." 50 | }, 51 | 52 | "menuDeleteItem": { 53 | "message": "Удалить", 54 | "description": "Text of delete menu item." 55 | }, 56 | 57 | "deleteItemConfimation": { 58 | "message": "Удалить $1?", 59 | "description": "Text of delete confirmation." 60 | }, 61 | 62 | "menuSettings": { 63 | "message": "Настройки Quick Dial", 64 | "description": "Text of settings menu item." 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/_locales/sr/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "menuAddToQuickDial": { 3 | "message": "Додај у брзо бирање", 4 | "description": "Text of add bookmark menu item." 5 | }, 6 | 7 | "menuNew": { 8 | "message": "Нова", 9 | "description": "Text of new menu item." 10 | }, 11 | 12 | "menuNewBookmark": { 13 | "message": "Забелешка", 14 | "description": "Text of add bookmark menu item." 15 | }, 16 | 17 | "AddBookmarkPrompt": { 18 | "message": "Унесите УРЛ нове забелешке :", 19 | "description": "Text of the add bookmark prompt." 20 | }, 21 | 22 | "menuNewFolder": { 23 | "message": "Фасцикла", 24 | "description": "Text of add folder menu item." 25 | }, 26 | 27 | "AddFolderPrompt": { 28 | "message": "Унесите име нове фасцикле :", 29 | "description": "Text of the add folder prompt." 30 | }, 31 | 32 | "menuProperties": { 33 | "message": "Својства", 34 | "description": "Text of properties menu item." 35 | }, 36 | 37 | "menuRefreshItem": { 38 | "message": "Обнови", 39 | "description": "Text of refresh menu item." 40 | }, 41 | 42 | "menuCaptureHere": { 43 | "message": "Ухвати овде", 44 | "description": "Text of capture here menu item." 45 | }, 46 | 47 | "menuCapturePage": { 48 | "message": "Ухвати у новом језичку", 49 | "description": "Text of capture menu item." 50 | }, 51 | 52 | "menuDeleteItem": { 53 | "message": "Обриши", 54 | "description": "Text of delete menu item." 55 | }, 56 | 57 | "deleteItemConfimation": { 58 | "message": "Обрисати $1 ?", 59 | "description": "Text of delete confirmation." 60 | }, 61 | 62 | "menuSettings": { 63 | "message": "Подешавање брзог бирања", 64 | "description": "Text of settings menu item." 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/_locales/uk/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "menuAddToQuickDial": { 3 | "message": "Додати до Quick Dial", 4 | "description": "Text of add bookmark menu item." 5 | }, 6 | 7 | "menuNew": { 8 | "message": "Додати", 9 | "description": "Text of new menu item." 10 | }, 11 | 12 | "menuNewBookmark": { 13 | "message": "Закладку", 14 | "description": "Text of add bookmark menu item." 15 | }, 16 | 17 | "AddBookmarkPrompt": { 18 | "message": "Введіть URL-адресу нової закладки:", 19 | "description": "Text of the add bookmark prompt." 20 | }, 21 | 22 | "menuNewFolder": { 23 | "message": "Папку", 24 | "description": "Text of add folder menu item." 25 | }, 26 | 27 | "AddFolderPrompt": { 28 | "message": "Введіть им'я нової папки:", 29 | "description": "Text of the add folder prompt." 30 | }, 31 | 32 | "menuProperties": { 33 | "message": "Властивості", 34 | "description": "Text of properties menu item." 35 | }, 36 | 37 | "menuRefreshItem": { 38 | "message": "Оновити", 39 | "description": "Text of refresh menu item." 40 | }, 41 | 42 | "menuCaptureHere": { 43 | "message": "Захватити тут", 44 | "description": "Text of capture here menu item." 45 | }, 46 | 47 | "menuCapturePage": { 48 | "message": "Захватити на новій вкладці", 49 | "description": "Text of capture menu item." 50 | }, 51 | 52 | "menuDeleteItem": { 53 | "message": "Видалити", 54 | "description": "Text of delete menu item." 55 | }, 56 | 57 | "deleteItemConfimation": { 58 | "message": "Видалити $1?", 59 | "description": "Text of delete confirmation." 60 | }, 61 | 62 | "menuSettings": { 63 | "message": "Налаштування Quick Dial", 64 | "description": "Text of settings menu item." 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/dial: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/html/properties.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Quick Dial Node Propoerties 7 | 8 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
Main
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
Title :
Url :
Image : 47 | 54 | 55 | 56 | 57 | 58 | 59 |
69 |
70 | 72 |
73 |
74 | 75 |
76 | 77 | 78 | 79 |
80 | 81 | -------------------------------------------------------------------------------- /src/html/settings.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Quick Dial Settings 7 | 8 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
PageGridCells
32 |
33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 49 | 50 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 |
Background Color :
Background Mode : 42 | 48 |
Background Image : 53 | 54 | 55 |
Preview :
Show "Add to ..." :
Open QD in new page :
Startpage timeout :
78 |
79 | 161 | 244 |
245 |
246 | 247 |
248 | 249 | 250 | 251 |
252 | 253 | 254 | -------------------------------------------------------------------------------- /src/img/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/src/img/24.png -------------------------------------------------------------------------------- /src/img/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/src/img/32.png -------------------------------------------------------------------------------- /src/img/README.md: -------------------------------------------------------------------------------- 1 | # About Image licence : 2 | 3 | These images provide from fast dial extention : http://www.userlogos.org/extensions/fastdial 4 | -------------------------------------------------------------------------------- /src/img/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/src/img/back.png -------------------------------------------------------------------------------- /src/img/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/src/img/folder.png -------------------------------------------------------------------------------- /src/img/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MatMoul/quickdial-webext/806e134b52a7e881630a0579c1c8d27644f296c8/src/img/throbber.gif -------------------------------------------------------------------------------- /src/js/background.js: -------------------------------------------------------------------------------- 1 | var app = {}; // App object 2 | 3 | app.init = function(){ // Init module 4 | app.Settings.init(function(){ 5 | app.Messages.init(); 6 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.settingsChanged }); 7 | browser.browserAction.onClicked.addListener(function(e){ 8 | if(app.settings.openQuickDialInNewPage) browser.tabs.create({}); 9 | else browser.tabs.update(e.id, {url: '/dial'}).then(); 10 | }); 11 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.gridNodesLoaded }); 12 | app.ContextMenus.initMenu(); 13 | window.setTimeout(function(){ 14 | app.GridNodes.sync(app.node, app.settings.grid.root, function(){ 15 | app.Bookmarks.initListener(); 16 | }); 17 | }, 500); 18 | 19 | // Start page workaround : 20 | if(app.settings.startpageTimeout>0){ 21 | setTimeout(function(){ 22 | browser.tabs.query({}).then( function(tabs) { 23 | tabs.forEach(function(itm){ 24 | if(itm.url=='about:blank'){ 25 | browser.tabs.update(itm.id, {url: browser.extension.getURL('dial')}); 26 | } 27 | }); 28 | }); 29 | }, app.settings.startpageTimeout); 30 | } 31 | 32 | }); 33 | }; 34 | 35 | app.Messages = {}; // Messages helper object 36 | app.Messages.Commands = { 37 | getSettings: 0, 38 | setSettings: 1, 39 | getNode: 2, 40 | getNodeByID: 3, 41 | updateNode: 4, 42 | setNodeIndex: 5, 43 | createBookmark: 6, 44 | createFolder: 7, 45 | deleteNode: 8, 46 | refreshNode: 9, 47 | capturePage: 10, 48 | settingsChanged: 100, 49 | gridNodesLoaded: 101 50 | }; 51 | app.Messages.init = function(){ // Init Messages Listeners 52 | browser.runtime.onMessage.addListener(function(request, sender, sendResponse){ 53 | switch(request.cmd){ 54 | case app.Messages.Commands.getSettings: 55 | sendResponse(app.settings); 56 | break; 57 | case app.Messages.Commands.setSettings: 58 | let rootChanged = (app.settings.grid.root!=request.settings.grid.root); 59 | app.settings = request.settings; 60 | app.Settings.save(); 61 | sendResponse(app.settings); 62 | browser.runtime.sendMessage( { cmd: app.Messages.Commands.settingsChanged } ); 63 | if(rootChanged){ 64 | app.node = { children: [] }; 65 | app.GridNodes.sync(app.node, app.settings.grid.root, function(){ browser.runtime.sendMessage({ cmd: app.Messages.Commands.gridNodesLoaded }); }); 66 | } else { 67 | browser.runtime.sendMessage( { cmd: app.Messages.Commands.gridNodesLoaded } ); 68 | } 69 | app.ContextMenus.updateMenu(); 70 | break; 71 | case app.Messages.Commands.getNodeByID: 72 | var nodes = app.GridNodes.getNodeWithParents(request.id); 73 | if(nodes) sendResponse(nodes[nodes.length-1]); 74 | else sendResponse(null); 75 | break; 76 | case app.Messages.Commands.updateNode: 77 | app.GridNodes.updateNode(app.GridNodes.getNodeById(request.id), request.value, function(){ 78 | browser.runtime.sendMessage( { cmd: app.Messages.Commands.gridNodesLoaded } ); 79 | }); 80 | break; 81 | case app.Messages.Commands.getNode: 82 | sendResponse(app.GridNodes.getNode(app.node, request.path.substr(1))); 83 | break; 84 | case app.Messages.Commands.setNodeIndex: 85 | app.GridNodes.setNodeIndex(app.GridNodes.getNode(app.node, request.path.substr(1)), request.index, request.newIndex, function(){ 86 | browser.runtime.sendMessage( { cmd: app.Messages.Commands.gridNodesLoaded } ); 87 | }); 88 | break; 89 | case app.Messages.Commands.createBookmark: 90 | app.GridNodes.createBookmark(app.GridNodes.getNode(app.node, request.path.substr(1)), request.url, request.title, function(){ 91 | browser.runtime.sendMessage( { cmd: app.Messages.Commands.gridNodesLoaded } ); 92 | }); 93 | break; 94 | case app.Messages.Commands.createFolder: 95 | app.GridNodes.createFolder(app.GridNodes.getNode(app.node, request.path.substr(1)), request.name, function(){ 96 | browser.runtime.sendMessage( { cmd: app.Messages.Commands.gridNodesLoaded } ); 97 | }); 98 | break; 99 | case app.Messages.Commands.deleteNode: 100 | app.GridNodes.deleteNode(app.GridNodes.getNode(app.node, request.path.substr(1)), request.id, function(){ 101 | browser.runtime.sendMessage( { cmd: app.Messages.Commands.gridNodesLoaded } ); 102 | }); 103 | break; 104 | case app.Messages.Commands.refreshNode: 105 | app.GridNodes.refreshNode(app.GridNodes.getChildNode(app.GridNodes.getNode(app.node, request.path.substr(1)), request.id), function(){ 106 | browser.runtime.sendMessage( { cmd: app.Messages.Commands.gridNodesLoaded } ); 107 | }); 108 | break; 109 | case app.Messages.Commands.capturePage: 110 | app.GridNodes.capturePage(app.GridNodes.getChildNode(app.GridNodes.getNode(app.node, request.path.substr(1)), request.id), function(){ 111 | browser.runtime.sendMessage( { cmd: app.Messages.Commands.gridNodesLoaded } ); 112 | }); 113 | break; 114 | } 115 | }); 116 | }; 117 | 118 | app.Settings = {}; // Settings helper object 119 | app.Settings.init = function(callback){ // Load settings and nodes 120 | browser.storage.local.get().then(function(data){ 121 | if(Object.keys(data).length == 0 || ! data.settings) { 122 | data = { 123 | version: 3, 124 | settings: { 125 | backgroundColor: '#3c4048', 126 | backgroundImage: null, 127 | backgroundMode: 0, 128 | menuShowAdd: true, 129 | openQuickDialInNewPage: true, 130 | grid: { 131 | margin: 10, 132 | rows: 4, 133 | columns: 5, 134 | ratioX: 4, 135 | ratioY: 3, 136 | backNode: true, 137 | backIcon: 'url(/img/back.png)', 138 | backIconMode: 3, 139 | folderIcon: 'url(/img/folder.png)', 140 | folderIconMode: 0, 141 | loadingIcon: 'url(/img/throbber.gif)', 142 | openBookmarkMethod: 0, 143 | openFolderMethod: 0, 144 | cells: { 145 | margin: 4, 146 | marginHover: 4, 147 | opacity: 1, 148 | opacityHover: 1, 149 | backgroundColor: null, 150 | backgroundColorHover: null, 151 | borderColor: '#333333', 152 | borderColorHover: '#a9a9a9', 153 | borderRadius: 4, 154 | borderRadiusHover: 4, 155 | borderSize: 1, 156 | borderSizeHover: 1, 157 | title: true, 158 | titleHover: true, 159 | titleHeight: 16, 160 | titleHeightHover: 16, 161 | titleFontSize: 10, 162 | titleFontSizeHover: 10, 163 | titleFont: 'Arial, Verdana, Sans-serif', 164 | titleColor: '#ffffff', 165 | titleColorHover: '#33ccff', 166 | titleBackgroundColor: null, 167 | titleBackgroundColorHover: null, 168 | titleBorderSize: 1, 169 | titleBorderSizeHover: 1, 170 | previewWidth: 1200, 171 | previewHeight: 710, 172 | snapshotDelay: 2000 173 | }, 174 | root: 'Quick Dial', 175 | } 176 | }, 177 | node: { children: [] } 178 | } 179 | } 180 | if(!data.version){ // Upgrade Data Version 181 | data.version = 2; 182 | data.grid.backNode = true; 183 | data.grid.backIcon = 'url(/img/back.png)'; 184 | data.grid.folderIcon = 'url(/img/folder.png)'; 185 | data.grid.loadingIcon = 'url(/img/throbber.gif)'; 186 | data.grid.cells.backgroundColor = null; 187 | data.grid.cells.backgroundColorHover = null; 188 | data.grid.cells.titleBackgroundColor = null; 189 | data.grid.cells.titleBackgroundColorHover = null; 190 | delete data.grid.cells.backIcon; 191 | delete data.grid.cells.folderIcon; 192 | delete data.grid.cells.loadingIcon; 193 | delete data.grid.cells.backPanel; 194 | } 195 | if(data.version == 2){ // Upgrade Data Version 196 | var oldData = data; 197 | data = {}; 198 | data.version = 3; 199 | data.settings = oldData; 200 | data.node = oldData.grid.node; 201 | delete data.settings.version; 202 | delete data.settings.grid.node; 203 | app.settings = data.settings; 204 | app.node = data.node; 205 | browser.storage.local.clear().then(function(){ 206 | app.Settings.save(); 207 | }); 208 | } 209 | if(data.version == 3){ // Upgrade Data Version 210 | data.version = 4; 211 | data.settings.backgroundMode = 0; 212 | data.settings.grid.backIconMode = 3; 213 | data.settings.grid.folderIconMode = 0; 214 | data.settings.grid.cells.opacity = 1; 215 | data.settings.grid.cells.opacityHover = 1; 216 | data.settings.grid.cells.borderSize = 1; 217 | data.settings.grid.cells.borderSizeHover = data.settings.grid.cells.borderSize; 218 | data.settings.grid.cells.titleHover = data.settings.grid.cells.title; 219 | data.settings.grid.cells.titleHeightHover = data.settings.grid.cells.titleHeight; 220 | data.settings.grid.cells.titleFontSizeHover = data.settings.grid.cells.titleFontSize; 221 | data.settings.grid.cells.titleBorderSize = 1; 222 | data.settings.grid.cells.titleBorderSizeHover = data.settings.grid.cells.titleBorderSize; 223 | app.Settings.save(); 224 | } 225 | if(data.version == 4){ // Upgrade Data Version 226 | if(!data.settings.grid.cells.snapshotDelay) data.settings.grid.cells.snapshotDelay = 2000; 227 | if(data.settings.grid.ratioAuto == true || data.settings.grid.ratioAuto == false){ 228 | delete data.settings.grid.ratioAuto; 229 | data.settings.grid.ratioX = 4; 230 | data.settings.grid.ratioY = 3; 231 | } 232 | if(!data.settings.grid.ratioX){ 233 | data.settings.grid.ratioX = 4; 234 | data.settings.grid.ratioY = 3; 235 | } 236 | if(!(data.settings.menuShowAdd == true) && !(data.settings.menuShowAdd == false)){ 237 | data.settings.menuShowAdd = true; 238 | } 239 | if(!data.settings.grid.openBookmarkMethod && data.settings.grid.openBookmarkMethod != 0){ 240 | data.settings.grid.openBookmarkMethod = 0; 241 | data.settings.grid.openFolderMethod = 0; 242 | } 243 | if(!data.settings.openQuickDialInNewPage && data.settings.openQuickDialInNewPage != false){ 244 | data.settings.openQuickDialInNewPage = true; 245 | } 246 | if(!data.settings.startpageTimeout){ 247 | data.settings.startpageTimeout = 500; 248 | } 249 | //app.Settings.save(); 250 | } 251 | app.settings = data.settings; 252 | app.node = data.node; 253 | if(callback) callback(); 254 | }, function(){ console.log('Error loading data'); }); 255 | }; 256 | app.Settings.update = function(settings, callback){ // Save new settings 257 | app.settings = settings; 258 | app.Settings.save(callback); 259 | }; 260 | app.Settings.save = function(callback){ // Save settings 261 | if(! app.settings) return; 262 | var data = { version: 4 }; 263 | data.settings = app.settings; 264 | data.node = app.node; 265 | browser.storage.local.set(data).then(function(){ 266 | if(callback) callback(); 267 | }, function(){ console.log('Error saving settings'); }); 268 | }; 269 | 270 | app.init(); 271 | 272 | app.ContextMenus = {} // ContextMenu helper Object 273 | app.ContextMenus.menuItemClicked = function(info, tab){ 274 | if (info.menuItemId == "AddToQuickDial") app.GridNodes.createBookmark(app.node, info.pageUrl, tab.title, function(){ 275 | browser.runtime.sendMessage( { cmd: app.Messages.Commands.gridNodesLoaded } ); 276 | }); 277 | }; 278 | app.ContextMenus.initMenu = function(){ // (Called from app.init) Init context menu in all pages 279 | // Create Add Context menu for all pages 280 | if(app.settings.menuShowAdd){ 281 | browser.contextMenus.create({ 282 | id: 'AddToQuickDial', 283 | title: browser.i18n.getMessage("menuAddToQuickDial"), 284 | contexts: ["all"], 285 | documentUrlPatterns: [ 'http://*/*', 'https://*/*', 'file://*/*', 'ftp://*/*' ], 286 | onclick(info, tab) { app.ContextMenus.menuItemClicked(info, tab) } 287 | }); 288 | } 289 | // Create WebExt Page Context menu 290 | browser.contextMenus.create({ 291 | id: "pagemenu", 292 | title: "Quick Dial", 293 | documentUrlPatterns: [ 'moz-extension://*/dial', 'moz-extension://*/dial?*' ], 294 | contexts: ["page"] 295 | }); 296 | browser.contextMenus.create({ 297 | id: "pagemenunew", 298 | parentId: "pagemenu", 299 | title: browser.i18n.getMessage("menuNew") 300 | }); 301 | browser.contextMenus.create({ 302 | parentId: "pagemenunew", 303 | title: browser.i18n.getMessage("menuNewBookmark"), 304 | onclick(info, tab) { 305 | browser.tabs.executeScript(tab.id, { 306 | code: "window.dial.createBookmark();" 307 | }); 308 | } 309 | }); 310 | browser.contextMenus.create({ 311 | parentId: "pagemenunew", 312 | title: browser.i18n.getMessage("menuNewFolder"), 313 | onclick(info, tab) { 314 | browser.tabs.executeScript(tab.id, { 315 | code: "window.dial.createFolder();" 316 | }); 317 | } 318 | }); 319 | browser.contextMenus.create({ parentId: "pagemenu", type: "separator" }); 320 | browser.contextMenus.create({ 321 | parentId: "pagemenu", 322 | title: browser.i18n.getMessage("menuSettings"), 323 | onclick(info, tab) { 324 | browser.tabs.executeScript(tab.id, { 325 | code: "window.dial.editSettings();" 326 | }); 327 | } 328 | }); 329 | // Create WebExt Link Context menu 330 | browser.contextMenus.create({ 331 | id: "itemmenu", 332 | title: "Quick Dial", 333 | documentUrlPatterns: [ 'moz-extension://*/dial', 'moz-extension://*/dial?*' ], 334 | contexts: ["link"] 335 | }); 336 | browser.contextMenus.create({ 337 | id: "itemmenunew", 338 | parentId: "itemmenu", 339 | title: browser.i18n.getMessage("menuNew") 340 | }); 341 | browser.contextMenus.create({ 342 | parentId: "itemmenunew", 343 | title: browser.i18n.getMessage("menuNewBookmark"), 344 | onclick(info, tab) { 345 | browser.tabs.executeScript(tab.id, { 346 | code: "window.dial.createBookmark();" 347 | }); 348 | } 349 | }); 350 | browser.contextMenus.create({ 351 | parentId: "itemmenunew", 352 | title: browser.i18n.getMessage("menuNewFolder"), 353 | onclick(info, tab) { 354 | browser.tabs.executeScript(tab.id, { 355 | code: "window.dial.createFolder();" 356 | }); 357 | } 358 | }); 359 | browser.contextMenus.create({ parentId: "itemmenu", type: "separator" }); 360 | browser.contextMenus.create({ 361 | parentId: "itemmenu", 362 | title: browser.i18n.getMessage("menuProperties"), 363 | onclick(info, tab) { 364 | browser.tabs.executeScript(tab.id, { 365 | code: "window.dial.editProperties(window.dial._selectedItem);" 366 | }); 367 | } 368 | }); 369 | browser.contextMenus.create({ 370 | parentId: "itemmenu", 371 | title: browser.i18n.getMessage("menuRefreshItem"), 372 | onclick(info, tab) { 373 | browser.tabs.executeScript(tab.id, { 374 | code: "window.dial.refreshNode(window.dial._selectedItem);" 375 | }); 376 | } 377 | }); 378 | browser.contextMenus.create({ 379 | parentId: "itemmenu", 380 | title: browser.i18n.getMessage("menuCaptureHere"), 381 | visible: false, 382 | onclick(info, tab) { 383 | browser.tabs.executeScript(tab.id, { 384 | code: "window.dial.captureHere(window.dial._selectedItem);" 385 | }); 386 | } 387 | }); 388 | browser.contextMenus.create({ 389 | parentId: "itemmenu", 390 | title: browser.i18n.getMessage("menuCapturePage"), 391 | onclick(info, tab) { 392 | browser.tabs.executeScript(tab.id, { 393 | code: "window.dial.capturePage(window.dial._selectedItem);" 394 | }); 395 | } 396 | }); 397 | browser.contextMenus.create({ 398 | parentId: "itemmenu", 399 | title: browser.i18n.getMessage("menuDeleteItem"), 400 | onclick(info, tab) { 401 | browser.tabs.executeScript(tab.id, { 402 | code: "window.dial.deleteNode();" 403 | }); 404 | } 405 | }); 406 | browser.contextMenus.create({ parentId: "itemmenu", type: "separator" }); 407 | browser.contextMenus.create({ 408 | parentId: "itemmenu", 409 | title: browser.i18n.getMessage("menuSettings"), 410 | onclick(info, tab) { 411 | browser.tabs.executeScript(tab.id, { 412 | code: "window.dial.editSettings();" 413 | }); 414 | } 415 | }); 416 | }; 417 | app.ContextMenus.updateMenu = function(){ 418 | browser.contextMenus.onClicked.removeListener(app.ContextMenus.menuItemClicked); 419 | browser.contextMenus.removeAll(); 420 | app.ContextMenus.initMenu(); 421 | }; 422 | 423 | app.Bookmarks = {} // Bookmarks helper object 424 | app.Bookmarks._onCreated = function(){ app.GridNodes.sync(app.node, app.settings.grid.root, function(){ browser.runtime.sendMessage({ cmd: app.Messages.Commands.gridNodesLoaded }); }); } 425 | app.Bookmarks._onChanged = function(){ app.GridNodes.sync(app.node, app.settings.grid.root, function(){ browser.runtime.sendMessage({ cmd: app.Messages.Commands.gridNodesLoaded }); }); } 426 | app.Bookmarks._onMoved = function(){ app.GridNodes.sync(app.node, app.settings.grid.root, function(){ browser.runtime.sendMessage({ cmd: app.Messages.Commands.gridNodesLoaded }); }); } 427 | app.Bookmarks._onRemoved = function(){ app.GridNodes.sync(app.node, app.settings.grid.root, function(){ browser.runtime.sendMessage({ cmd: app.Messages.Commands.gridNodesLoaded }); }); } 428 | app.Bookmarks._onImportBegan = function(){ 429 | browser.bookmarks.onCreated.removeListener(app.Bookmarks._onCreated); 430 | browser.bookmarks.onChanged.removeListener(app.Bookmarks._onChanged); 431 | browser.bookmarks.onMoved.removeListener(app.Bookmarks._onMoved); 432 | browser.bookmarks.onRemoved.removeListener(app.Bookmarks._onRemoved); 433 | } 434 | app.Bookmarks._onImportEnded = function(){ 435 | app.GridNodes.sync(app.node, app.settings.grid.root, function(){ browser.runtime.sendMessage({ cmd: app.Messages.Commands.gridNodesLoaded }); }); 436 | app.Bookmarks.initListener(); 437 | } 438 | app.Bookmarks.initListener = function(){ // (Called from app.init) (/!\ Need filter to root tree only) Init listener of bookmarks 439 | browser.bookmarks.onCreated.addListener(app.Bookmarks._onCreated); 440 | browser.bookmarks.onChanged.addListener(app.Bookmarks._onChanged); 441 | browser.bookmarks.onMoved.addListener(app.Bookmarks._onMoved); 442 | browser.bookmarks.onRemoved.addListener(app.Bookmarks._onRemoved); 443 | } 444 | app.Bookmarks.load = function(rootPath, callback){ // Load root bookmark and create it if not exist 445 | if(!callback) return; 446 | var root = 'menu________'; 447 | if(rootPath.substr(0,1)=='/') root = 'root________'; 448 | browser.bookmarks.getSubTree(root).then(function(bookmarkItems){ 449 | function getChildItem(bookmarkItem, path, callback){ 450 | if(path.length == 0){ 451 | callback(bookmarkItem); 452 | return; 453 | } 454 | for(var child of bookmarkItem.children) 455 | if((path + '/').startsWith(child.title + '/')) 456 | return getChildItem(child, path.substr(child.title.length + 1), callback); 457 | browser.bookmarks.create({ 458 | parentId: bookmarkItem.id, 459 | title: path.substr(0, (path + '/').indexOf('/')) 460 | }).then(function(bookmarkItem){ 461 | return getChildItem(bookmarkItem, path.substr(bookmarkItem.title.length + 1), callback); 462 | }, function(){ callback(); }); 463 | } 464 | if(rootPath.substr(0,1)=='/') getChildItem(bookmarkItems[0], rootPath.substr(1), callback); 465 | else getChildItem(bookmarkItems[0], rootPath, callback); 466 | }, function(){ callback(); }); 467 | } 468 | 469 | app.SiteInfos = {} // Siteinfos helper object 470 | app.SiteInfos.fromNewTab = function(url, callback){ // Retrieve infos from a new tab. callback( { url, title, icon, screenshot } || error: callback() ) 471 | browser.tabs.create({url: url, active: false}).then(function(tab){ 472 | browser.tabs.update(tab.id, {muted: true}).then(); 473 | function whaitLoaded(){ 474 | browser.tabs.get(tab.id).then(function(tab){ 475 | if(tab.status == 'loading') setTimeout(whaitLoaded, 300); 476 | else { 477 | browser.tabs.update(tab.id, {active: true}).then(function(){ 478 | setTimeout(function(){ 479 | browser.tabs.captureVisibleTab().then(function(img){ 480 | browser.tabs.remove(tab.id); 481 | 482 | var imgObj = new Image; 483 | imgObj.src = img; 484 | 485 | var previewWidth = 1200; // Need to be linked to settings 486 | var previewHeight = previewWidth / app.settings.grid.ratioX * app.settings.grid.ratioY; 487 | if(app.settings.grid.title == true) previewHeight -= app.settings.grid.titleHeight; 488 | 489 | var canvas = document.createElement('canvas'); 490 | canvas.style.width = previewWidth.toString() + 'px'; 491 | canvas.style.height = previewHeight.toString() + 'px'; 492 | canvas.width = previewWidth / 2; 493 | canvas.height = previewHeight / 2; 494 | 495 | var ctx = canvas.getContext('2d'); 496 | ctx.clearRect(0, 0, previewWidth, previewHeight); 497 | ctx.save(); 498 | ctx.scale(0.5, 0.5); 499 | setTimeout(function(){ 500 | if(tab.height * previewWidth / previewHeight >= tab.width){ 501 | // Cut the bottom of the page 502 | ctx.drawImage(imgObj, 0, 0, tab.width, tab.width / previewWidth * previewHeight, 0, 0, previewWidth, previewHeight); 503 | } else { 504 | // Stretch or cutting right part of the page ? actualy Stretch 505 | ctx.drawImage(imgObj, 0, 0, tab.width, tab.height, 0, 0, previewWidth, previewHeight); 506 | //ctx.drawImage(imgObj, 0, 0, tab.height / previewHeight * previewWidth, tab.height, 0, 0, previewWidth, previewHeight); 507 | } 508 | ctx.restore(); 509 | img = canvas.toDataURL(); 510 | if(callback) callback( { url: tab.url, title: tab.title, icon: tab.favIconUrl, screenshot: img } ); 511 | }, 100); 512 | }, function(){ 513 | browser.tabs.remove(tab.id); 514 | if(callback) callback(); 515 | }); 516 | }, app.settings.grid.cells.snapshotDelay); 517 | }, function(){ if(callback) callback(); }); 518 | } 519 | }, function(){ if(callback) callback(); }); 520 | } 521 | setTimeout(whaitLoaded, 300); 522 | }, function(){ if(callback) callback(); }); 523 | } 524 | app.SiteInfos.fromFrame = function(url, callback){ // Retrieve infos from an iframe. callback( { url, title, (/!\ Not handled now)icon, screenshot } || error: callback() ) 525 | function pageLoaded(){ 526 | if(!iframe) return; 527 | var docTitle = iframe.contentWindow.document.title; 528 | var docIcon = null; 529 | var docScreenshot = null; 530 | //title 531 | if(docTitle == '') docTitle = url; 532 | //icon 533 | //screenshot 534 | var canvas = document.createElement('canvas'); 535 | canvas.style.width = previewWidth.toString() + 'px'; 536 | canvas.style.height = previewHeight.toString() + 'px'; 537 | canvas.width = previewWidth / 2; 538 | canvas.height = previewHeight / 2; 539 | var ctx = canvas.getContext('2d'); 540 | ctx.clearRect(0, 0, previewWidth, previewHeight); 541 | ctx.save(); 542 | ctx.scale(0.5, 0.5); 543 | ctx.drawWindow(iframe.contentWindow, 0, 0, previewWidth, previewHeight, 'rgb(255, 255, 255)'); 544 | ctx.restore(); 545 | docScreenshot = canvas.toDataURL(); 546 | 547 | document.body.removeChild(iframe); 548 | iframe = null; 549 | if(callback) callback({ url: url, title: docTitle, icon: docIcon, screenshot:docScreenshot }); 550 | } 551 | 552 | var previewWidth = 1200; // Need to be linked to settings 553 | var previewHeight = previewWidth / app.settings.grid.ratioX * app.settings.grid.ratioY; 554 | if(app.settings.grid.title == true) previewHeight -= app.settings.grid.titleHeight; 555 | var iframe; 556 | var xmlHttp = new XMLHttpRequest(); 557 | xmlHttp.timeout = 10000 558 | xmlHttp.open('GET', url, true); 559 | xmlHttp.onload = function(){ 560 | iframe = document.createElement('iframe'); 561 | iframe.width = previewWidth 562 | iframe.height = previewHeight 563 | iframe.style.position = 'absolute'; 564 | iframe.scrolling = 'no'; 565 | var content = xmlHttp.responseText.replace('', ''); 566 | iframe.onload = function(){ pageLoaded(); } 567 | document.body.appendChild(iframe); 568 | iframe.srcdoc = content; 569 | setTimeout(function(){ pageLoaded(); }, 6000); 570 | } 571 | xmlHttp.onabort = function(){ if(callback) callback(); } 572 | xmlHttp.onerror = function(){ if(callback) callback(); } 573 | xmlHttp.ontimeout = function(){ if(callback) callback(); } 574 | xmlHttp.send(); 575 | } 576 | app.SiteInfos.fromWS = function(url, callback){ // Retrieve infos from a Web Service. callback( { url, title, (/!\ Not handled now)icon, screenshot } || error: callback() ) 577 | console.log('Not implemented'); 578 | return app.SiteInfos.fromFrame(url, callback); 579 | } 580 | 581 | app.GridNodes = {}; // GridNodes helper object 582 | app.GridNodes.GridNodeType = { // GridNodeType 583 | back: -1, 584 | empty: 0, 585 | folder: 1, 586 | bookmark: 2 587 | } 588 | app.GridNodes.sync = function(gridNode, rootPath, callback){ // Sync GridNodes with Bookmarks 589 | if(app.GridNodes._syncing) { 590 | app.GridNodes._needSync = true; 591 | return; 592 | } 593 | app.GridNodes._syncing = true; 594 | app.Bookmarks.load(rootPath, function(bookmarkItem){ 595 | function syncNode(gridNode, bookmarkItem){ 596 | gridNode.id = bookmarkItem.id; 597 | gridNode.title = bookmarkItem.title; 598 | switch(bookmarkItem.type){ 599 | case 'bookmark': 600 | gridNode.type = app.GridNodes.GridNodeType.bookmark; 601 | if(gridNode.url != bookmarkItem.url){ 602 | gridNode.url = bookmarkItem.url; 603 | delete gridNode.image; 604 | } 605 | break; 606 | case 'folder': 607 | gridNode.type = app.GridNodes.GridNodeType.folder; 608 | var EmptyNodes = []; 609 | if(! gridNode.children) gridNode.children = []; 610 | else { 611 | for(var i=gridNode.children.length-1; i>=0; i--){ 612 | if(!gridNode.children[i]) gridNode.children[i] = { type: app.GridNodes.GridNodeType.empty }; 613 | if(gridNode.children[i].type==app.GridNodes.GridNodeType.empty){ 614 | EmptyNodes.unshift(gridNode.children[i]); 615 | } else { 616 | var found = false; 617 | for(var child of bookmarkItem.children){ 618 | if(child.id==gridNode.children[i].id){ 619 | found = true; 620 | break; 621 | } 622 | } 623 | if(! found){ 624 | if(i0){ 638 | childGridNode = EmptyNodes[0]; 639 | EmptyNodes.shift(); 640 | }else { 641 | childGridNode = {}; 642 | gridNode.children.push(childGridNode) 643 | } 644 | } 645 | syncNode(childGridNode, child); 646 | } 647 | EmptyNodes.length = 0; 648 | break; 649 | default: 650 | gridNode.type = app.GridNodes.GridNodeType.empty; 651 | break; 652 | } 653 | } 654 | 655 | syncNode(gridNode, bookmarkItem); 656 | delete app.GridNodes._syncing; 657 | if(app.GridNodes._needSync == true) { 658 | delete app.GridNodes._needSync; 659 | app.GridNodes.sync(gridNode, rootPath, callback); 660 | } else { 661 | app.GridNodes.save(); 662 | if(callback) callback(); 663 | } 664 | }); 665 | } 666 | app.GridNodes.save = function(callback){ // Save GridNodes 667 | app.Settings.save(callback); 668 | } 669 | app.GridNodes.getNode = function(gridNode, path){ // Return GridNode from RootGridNode path 670 | if(path.length == 0 || path == '/') return gridNode; 671 | for(var child of gridNode.children) 672 | if(path.startsWith(child.title + '/')) 673 | return app.GridNodes.getNode(child, path.substr(child.title.length + 1)); 674 | return null; 675 | } 676 | app.GridNodes.getNodeById = function(id){ 677 | var nodes = app.GridNodes.getNodeWithParents(id); 678 | if(nodes) return nodes[nodes.length-1]; 679 | return null; 680 | } 681 | app.GridNodes.getNodeWithParents = function(id){ 682 | 683 | var parents = []; 684 | 685 | function findNode(gridNode){ 686 | if(gridNode.id == id){ 687 | parents.unshift(gridNode); 688 | return gridNode; 689 | } 690 | if(gridNode.children){ 691 | for(var i=0; i0) return parents; 704 | return null; 705 | } 706 | app.GridNodes.updateNode = function(gridNode, value, callback){ 707 | if(value){ 708 | if(value.title) gridNode.title = value.title; 709 | if(value.titleLocked!=null) gridNode.titleLocked = value.titleLocked; 710 | if(value.imageLocked!=null){ 711 | gridNode.imageLocked = value.imageLocked; 712 | if(value.image) gridNode.image = value.image; 713 | else delete gridNode.image; 714 | } else if(gridNode.imageLocked != true){ 715 | if(value.image) gridNode.image = value.image; 716 | else delete gridNode.image; 717 | } 718 | if(value.imageMode || value.imageMode == 0) { 719 | if(value.imageMode == -1) delete gridNode.imageMode; 720 | else gridNode.imageMode = value.imageMode; 721 | } 722 | if(gridNode.type == app.GridNodes.GridNodeType.bookmark && value.url && gridNode.url != value.url){ 723 | gridNode.url = value.url; 724 | app.GridNodes.refreshNode(gridNode, function(){ 725 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.gridNodesLoaded }); 726 | app.GridNodes.saveNode(gridNode); 727 | var data = { title: gridNode.title }; 728 | //if(gridNode.imageMode) data.imageMode = gridNode.imageMode; 729 | //if(gridNode.type == app.GridNodes.GridNodeType.bookmark) data.url = gridNode.url; 730 | data.url = gridNode.url; 731 | browser.bookmarks.onChanged.removeListener(app.Bookmarks._onChanged); 732 | browser.bookmarks.update(gridNode.id, data).then(function(){ 733 | browser.bookmarks.onChanged.addListener(app.Bookmarks._onChanged); 734 | }); 735 | }); 736 | } else { 737 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.gridNodesLoaded }); 738 | app.GridNodes.saveNode(gridNode); 739 | var data = { title: gridNode.title }; 740 | //if(gridNode.imageMode) data.imageMode = gridNode.imageMode; 741 | browser.bookmarks.onChanged.removeListener(app.Bookmarks._onChanged); 742 | browser.bookmarks.update(gridNode.id, data).then(function(){ 743 | browser.bookmarks.onChanged.addListener(app.Bookmarks._onChanged); 744 | }); 745 | } 746 | } 747 | if(callback) callback(gridNode); 748 | } 749 | app.GridNodes.getChildNode = function(gridNode, id){ // Return child node by ID 750 | for(var child of gridNode.children) if(child.id == id) return child; 751 | return null; 752 | } 753 | app.GridNodes.saveNode = function(gridNode, callback){ // Save GridNode 754 | app.Settings.save(callback); 755 | } 756 | app.GridNodes.setNodeIndex = function(gridNode, index, newIndex, callback){ // Set Child GridNodeIndex. callback(gridNode, node1, node2) 757 | while(newIndex>=gridNode.children.length) 758 | gridNode.children.push({ type: app.GridNodes.GridNodeType.empty }); 759 | var node1 = gridNode.children[index]; 760 | var node2 = gridNode.children[newIndex]; 761 | gridNode.children[index] = node2; 762 | gridNode.children[newIndex] = node1; 763 | for(var i=gridNode.children.length-1; i>=0; i--){ 764 | if(gridNode.children[i].type != app.GridNodes.GridNodeType.empty) break; 765 | gridNode.children.pop(); 766 | } 767 | app.GridNodes.saveNode(gridNode); 768 | if(callback) callback(gridNode, node1, node2); 769 | } 770 | app.GridNodes.createBookmark = function(gridNode, url, title, callback){ // Create a new Bookmark in a GridNode. callback(gridNode, newGridNode) 771 | browser.bookmarks.onCreated.removeListener(app.Bookmarks._onCreated); 772 | var prefix = ''; 773 | if(url.indexOf('://')<0) prefix = 'http://'; 774 | browser.bookmarks.create({ 775 | parentId: gridNode.id, 776 | title: title || url, 777 | url: prefix + url 778 | }).then(function(bookmarkItem){ 779 | if(!gridNode) return; // ??? Why this method are called a second time with gridNode = null ??? 780 | browser.bookmarks.onCreated.addListener(app.Bookmarks._onCreated); 781 | var newGridNode = { id: bookmarkItem.id, type: app.GridNodes.GridNodeType.bookmark, url: prefix + url, title }; 782 | var EmptyCellFound = false; 783 | for(var i=0; i=0; i--){ 830 | if(gridNode.children[i].type != app.GridNodes.GridNodeType.empty) break; 831 | gridNode.children.pop(); 832 | } 833 | browser.bookmarks.onRemoved.removeListener(app.Bookmarks._onRemoved); 834 | browser.bookmarks.removeTree(id).then(function(){ 835 | browser.bookmarks.onRemoved.addListener(app.Bookmarks._onRemoved); 836 | }, function(){ 837 | browser.bookmarks.onRemoved.addListener(app.Bookmarks._onRemoved); 838 | }); 839 | if(callback) callback(gridNode, id); 840 | } 841 | 842 | app.GridNodes.refreshNode = function(gridNode, callback){ // Refresh content of a GridNode 843 | if(gridNode.__isLoading == true) return; 844 | gridNode.__isLoading = true; 845 | switch(gridNode.type){ 846 | case app.GridNodes.GridNodeType.folder: 847 | delete gridNode.image; 848 | delete gridNode.__isLoading; 849 | app.GridNodes.saveNode(gridNode); 850 | if(callback) callback({ title: gridNode.title, screenshot: gridNode.image }); 851 | /* 852 | browser.bookmarks.onChanged.removeListener(app.Bookmarks._onChanged); 853 | browser.bookmarks.update(gridNode.id, { title: gridNode.title }).then(function(){ 854 | browser.bookmarks.onChanged.addListener(app.Bookmarks._onChanged); 855 | }); 856 | */ 857 | break; 858 | case app.GridNodes.GridNodeType.bookmark: 859 | app.SiteInfos.fromFrame(gridNode.url, function(infos){ 860 | if(gridNode.imageLocked!=true){ 861 | if(infos){ 862 | if(gridNode.titleLocked!=true) gridNode.title = infos.title; 863 | gridNode.image = infos.screenshot; 864 | } else { 865 | gridNode.image = '0'; 866 | } 867 | } 868 | 869 | delete gridNode.__isLoading; 870 | app.GridNodes.saveNode(gridNode); 871 | if(callback) callback(infos); 872 | browser.bookmarks.onChanged.removeListener(app.Bookmarks._onChanged); 873 | browser.bookmarks.update(gridNode.id, { title: gridNode.title, url: gridNode.url }).then(function(){ 874 | browser.bookmarks.onChanged.addListener(app.Bookmarks._onChanged); 875 | }); 876 | }); 877 | break; 878 | } 879 | } 880 | app.GridNodes.capturePage = function(gridNode, callback){ 881 | if(gridNode.__isLoading == true) return; 882 | gridNode.__isLoading = true; 883 | switch(gridNode.type){ 884 | case app.GridNodes.GridNodeType.folder: 885 | var nodes = app.GridNodes.getNodeWithParents(gridNode.id); 886 | if(nodes){ 887 | var path = ''; 888 | for(var i=1; i 0){ 24 | if(dial.page < dial.maxpage){ 25 | dial.page += 1; 26 | dial.populateGrid(); 27 | } 28 | } else if(e.deltaY < 0){ 29 | if(dial.page > 1){ 30 | dial.page -= 1; 31 | dial.populateGrid(); 32 | } 33 | } 34 | } 35 | }); 36 | window.addEventListener('keyup', function(e){ 37 | switch(e.key){ 38 | case 'PageDown': 39 | if(dial.page < dial.maxpage){ 40 | dial.page += 1; 41 | dial.populateGrid(); 42 | } 43 | break; 44 | case 'PageUp': 45 | if(dial.page > 1){ 46 | dial.page -= 1; 47 | dial.populateGrid(); 48 | } 49 | break; 50 | } 51 | }); 52 | 53 | 54 | 55 | utils.getBackgroundColor = function(){ 56 | return new URL(window.location).searchParams.get('bg'); 57 | }; 58 | utils.getPath = function(){ 59 | var path = new URL(window.location).searchParams.get('path'); 60 | if(path) return path + '/'; 61 | else return '/'; 62 | }; 63 | 64 | app.init = function(){ 65 | app.Messages.getSettings(function(settings){ 66 | if(settings && settings.grid) app.Settings._changed(settings); 67 | dial.path = utils.getPath(); 68 | app.Messages.getNode(dial.path, app.GridNodes._changed); 69 | app.Messages.init(); 70 | }); 71 | }; 72 | 73 | app.Messages = {}; 74 | app.Messages.Commands = { 75 | getSettings: 0, 76 | setSettings: 1, 77 | getNode: 2, 78 | getNodeByID: 3, 79 | updateNode: 4, 80 | setNodeIndex: 5, 81 | createBookmark: 6, 82 | createFolder: 7, 83 | deleteNode: 8, 84 | refreshNode: 9, 85 | capturePage: 10, 86 | settingsChanged: 100, 87 | gridNodesLoaded: 101 88 | }; 89 | app.Messages.init = function(){ 90 | browser.runtime.onMessage.addListener(function(request, sender, sendResponse){ 91 | switch(request.cmd){ 92 | case app.Messages.Commands.settingsChanged: 93 | app.Messages.getSettings(app.Settings._changed); 94 | break; 95 | case app.Messages.Commands.gridNodesLoaded: 96 | if(dial.skipUpdate!=true) app.Messages.getNode(dial.path, app.GridNodes._changed); 97 | break; 98 | } 99 | }); 100 | }; 101 | app.Messages.getSettings = function(callback){ 102 | browser.runtime.getBackgroundPage().then(function(page){ 103 | if(page){ 104 | if(callback) callback(page.app.settings); 105 | } else { 106 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.getSettings }).then(callback, callback); 107 | } 108 | }, callback); 109 | }; 110 | app.Messages.getNode = function(path, callback){ 111 | browser.runtime.getBackgroundPage().then(function(page){ 112 | if(page){ 113 | if(callback) callback(page.app.GridNodes.getNode(page.app.node, dial.path.substr(1))); 114 | } else { 115 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.getNode, path: path }).then(callback); 116 | } 117 | }); 118 | }; 119 | app.Messages.updateNode = function(id, value, callback){ 120 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.updateNode, id: id, value: value }).then(callback); 121 | }; 122 | app.Messages.setNodeIndex = function(index, newIndex, callback){ 123 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.setNodeIndex, path: dial.path, index: index, newIndex: newIndex }).then(callback); 124 | }; 125 | app.Messages.createBookmark = function(url, callback){ 126 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.createBookmark, path: dial.path, url: url, title: url }).then(callback); 127 | }; 128 | app.Messages.createFolder = function(name, callback){ 129 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.createFolder, path: dial.path, name: name }).then(callback); 130 | }; 131 | app.Messages.deleteNode = function(id, callback){ 132 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.deleteNode, path: dial.path, id: id }).then(callback); 133 | }; 134 | app.Messages.refreshNode = function(id, callback){ 135 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.refreshNode, path: dial.path, id: id }).then(callback); 136 | } 137 | app.Messages.capturePage = function(id, callback){ 138 | browser.runtime.sendMessage({ cmd: app.Messages.Commands.capturePage, path: dial.path, id: id }).then(callback); 139 | } 140 | 141 | 142 | app.Settings = {}; 143 | app.Settings._changed = function(settings){ 144 | app.settings = settings; 145 | dial.initStyles(); 146 | dial.initGrid(); 147 | }; 148 | 149 | app.GridNodes = {}; 150 | app.GridNodes.GridNodeType = { // GridNodeType 151 | back: -1, 152 | empty: 0, 153 | folder: 1, 154 | bookmark: 2 155 | } 156 | app.GridNodes._changed = function(node){ 157 | app.node = node; 158 | dial.Title.innerText = app.node.title; 159 | dial.populateGrid(); 160 | }; 161 | 162 | 163 | 164 | 165 | dial.init = function(){ 166 | //dial.initMenus(); 167 | dial.Title = document.createElement('title'); 168 | document.head.appendChild(dial.Title); 169 | }; 170 | /* 171 | dial.initMenus = function(){ 172 | document.body.setAttribute('contextmenu', 'page'); 173 | dial.PageMenu = document.createElement('menu'); 174 | dial.PageMenu.type = 'context'; 175 | dial.PageMenu.id = 'page' 176 | dial.PageMenuNew = document.createElement('menu'); 177 | dial.PageMenuNew.label = browser.i18n.getMessage("menuNew"); 178 | dial.PageMenuCreateBookmark = document.createElement('menuitem'); 179 | dial.PageMenuCreateBookmark.label = browser.i18n.getMessage("menuNewBookmark"); 180 | dial.PageMenuCreateBookmark.onclick = dial.createBookmark; 181 | dial.PageMenuCreateFolder = document.createElement('menuitem'); 182 | dial.PageMenuCreateFolder.label = browser.i18n.getMessage("menuNewFolder"); 183 | dial.PageMenuCreateFolder.onclick = dial.createFolder; 184 | dial.PageMenuSettings = document.createElement('menuitem'); 185 | dial.PageMenuSettings.label = browser.i18n.getMessage("menuSettings"); 186 | dial.PageMenuSettings.onclick = dial.editSettings; 187 | 188 | dial.PageMenu.appendChild(dial.PageMenuNew); 189 | dial.PageMenuNew.appendChild(dial.PageMenuCreateBookmark); 190 | dial.PageMenuNew.appendChild(dial.PageMenuCreateFolder); 191 | dial.PageMenu.appendChild(document.createElement('hr')); 192 | dial.PageMenu.appendChild(dial.PageMenuSettings); 193 | document.body.appendChild(dial.PageMenu); 194 | 195 | dial.ItemMenu = document.createElement('menu'); 196 | dial.ItemMenu.type = 'context'; 197 | dial.ItemMenu.id = 'item' 198 | 199 | dial.ItemMenuNew = document.createElement('menu'); 200 | dial.ItemMenuNew.label = browser.i18n.getMessage("menuNew"); 201 | 202 | dial.ItemMenuCreateBookmark = document.createElement('menuitem'); 203 | dial.ItemMenuCreateBookmark.label = browser.i18n.getMessage("menuNewBookmark"); 204 | dial.ItemMenuCreateBookmark.onclick = dial.createBookmark; 205 | dial.ItemMenuCreateFolder = document.createElement('menuitem'); 206 | dial.ItemMenuCreateFolder.label = browser.i18n.getMessage("menuNewFolder"); 207 | dial.ItemMenuCreateFolder.onclick = dial.createFolder; 208 | 209 | dial.ItemMenuProperties = document.createElement('menuitem'); 210 | dial.ItemMenuProperties.label = browser.i18n.getMessage("menuProperties"); 211 | dial.ItemMenuProperties.onclick = function(){ 212 | dial.editProperties(dial._selectedItem); 213 | }; 214 | 215 | dial.ItemMenuRefresh = document.createElement('menuitem'); 216 | dial.ItemMenuRefresh.label = browser.i18n.getMessage("menuRefreshItem"); 217 | dial.ItemMenuRefresh.onclick = function(){ 218 | dial.refreshNode(dial._selectedItem); 219 | }; 220 | 221 | dial.ItemMenuCaptureHere = document.createElement('menuitem'); 222 | dial.ItemMenuCaptureHere.label = browser.i18n.getMessage("menuCaptureHere"); 223 | dial.ItemMenuCaptureHere.onclick = function(){ 224 | dial.captureHere(dial._selectedItem); 225 | }; 226 | 227 | dial.ItemMenuCapture = document.createElement('menuitem'); 228 | dial.ItemMenuCapture.label = browser.i18n.getMessage("menuCapturePage"); 229 | dial.ItemMenuCapture.onclick = function(){ 230 | dial.capturePage(dial._selectedItem); 231 | }; 232 | 233 | dial.ItemMenuDelete = document.createElement('menuitem'); 234 | dial.ItemMenuDelete.label = browser.i18n.getMessage("menuDeleteItem"); 235 | dial.ItemMenuDelete.onclick = dial.deleteNode; 236 | 237 | dial.ItemMenuSettings = document.createElement('menuitem'); 238 | dial.ItemMenuSettings.label = browser.i18n.getMessage("menuSettings"); 239 | dial.ItemMenuSettings.onclick = dial.editSettings; 240 | 241 | dial.ItemMenu.appendChild(dial.ItemMenuNew); 242 | dial.ItemMenuNew.appendChild(dial.ItemMenuCreateBookmark); 243 | dial.ItemMenuNew.appendChild(dial.ItemMenuCreateFolder); 244 | dial.ItemMenu.appendChild(document.createElement('hr')); 245 | dial.ItemMenu.appendChild(dial.ItemMenuProperties); 246 | dial.ItemMenu.appendChild(dial.ItemMenuRefresh); 247 | dial.ItemMenu.appendChild(dial.ItemMenuCaptureHere); 248 | dial.ItemMenu.appendChild(dial.ItemMenuCapture); 249 | dial.ItemMenu.appendChild(dial.ItemMenuDelete); 250 | dial.ItemMenu.appendChild(document.createElement('hr')); 251 | dial.ItemMenu.appendChild(dial.ItemMenuSettings); 252 | document.body.appendChild(dial.ItemMenu); 253 | } 254 | */ 255 | dial.initStyles = function(){ 256 | function applyImageMode(imageMode, target){ 257 | switch(imageMode){ 258 | case 0: 259 | target.backgroundRepeat = 'no-repeat'; 260 | target.backgroundSize = '100% 100%'; 261 | break; 262 | case 1: 263 | target.backgroundRepeat = 'no-repeat'; 264 | target.backgroundSize = 'cover'; 265 | target.backgroundPosition = 'center'; 266 | break; 267 | case 2: 268 | target.backgroundRepeat = 'no-repeat'; 269 | target.backgroundSize = 'contain'; 270 | target.backgroundPosition = 'center'; 271 | break; 272 | case 3: 273 | target.backgroundRepeat = 'no-repeat'; 274 | target.backgroundPosition = 'center'; 275 | break; 276 | } 277 | } 278 | 279 | var oldStyle = dial.Style; 280 | dial.Style = document.createElement('style'), StyleSheet; 281 | document.head.appendChild(dial.Style); 282 | dial.styles = {}; 283 | dial.styles.html = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('html { height: 100%; }')].style; 284 | dial.styles.body = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('body { user-select: none; -moz-user-select: none; display: flex; width: 100%; height: 100%; margin: 0px; padding: 0px; background-color: ' + app.settings.backgroundColor + '; background-image: ' + app.settings.backgroundImage + '; }')].style; 285 | applyImageMode(app.settings.backgroundMode, dial.styles.body); 286 | dial.styles.grid = {}; 287 | dial.styles.grid.grid = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid { border-collapse: collapse; margin: auto; }')].style; 288 | dial.styles.grid.cell = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td { margin: 0px; padding: 0px; }')].style; 289 | dial.styles.grid.cellHover = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td:hover {}')].style; 290 | dial.styles.grid.link = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td>a { display: block; outline: none; overflow: hidden; text-decoration: none; margin: ' + app.settings.grid.cells.margin + 'px; opacity: ' + app.settings.grid.cells.opacity + '; border: ' + app.settings.grid.cells.borderSize + 'px solid ' + app.settings.grid.cells.borderColor + '; border-radius: ' + app.settings.grid.cells.borderRadius + 'px; }')].style; 291 | dial.styles.grid.linkHover = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td>a:hover { border-color: ' + app.settings.grid.cells.borderColorHover + '; border-width: ' + app.settings.grid.cells.borderSizeHover + 'px; margin: ' + app.settings.grid.cells.marginHover + 'px; opacity: ' + app.settings.grid.cells.opacityHover + '; border-radius: ' + app.settings.grid.cells.borderRadiusHover + 'px; }')].style; 292 | dial.styles.grid.linkPanel = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td>a>div:first-child { background-repeat: no-repeat; }')].style; 293 | if(app.settings.grid.cells.backgroundColor) dial.styles.grid.linkPanel.backgroundColor = app.settings.grid.cells.backgroundColor; 294 | dial.styles.grid.linkPanelHover = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td>a:hover>div:first-child { }')].style; 295 | if(app.settings.grid.cells.backgroundColorHover) dial.styles.grid.linkPanelHover.backgroundColor = app.settings.grid.cells.backgroundColorHover; 296 | else dial.styles.grid.linkPanelHover.backgroundColor = 'transparent'; 297 | dial.styles.grid.linkTitle = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td>a>div:last-child { height: ' + app.settings.grid.cells.titleHeight + 'px; font-size: ' + app.settings.grid.cells.titleFontSize + 'pt; font-family: ' + app.settings.grid.cells.titleFont + '; text-align: center; overflow: hidden; color: ' + app.settings.grid.cells.titleColor + '; border-top: ' + app.settings.grid.cells.titleBorderSize + 'px solid ' + app.settings.grid.cells.borderColor + '; }')].style; 298 | if(app.settings.grid.cells.titleBackgroundColor) dial.styles.grid.linkTitle.backgroundColor = app.settings.grid.cells.titleBackgroundColor; 299 | else dial.styles.grid.linkTitle.backgroundColor = 'transparent'; 300 | dial.styles.grid.linkTitleHover = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td>a:hover>div:last-child { font-size: ' + app.settings.grid.cells.titleFontSizeHover + 'pt; color: ' + app.settings.grid.cells.titleColorHover + '; border-top-width: ' + app.settings.grid.cells.titleBorderSizeHover + 'px; border-top-color: ' + app.settings.grid.cells.borderColorHover + ' }')].style; 301 | if(app.settings.grid.cells.titleBackgroundColorHover) dial.styles.grid.linkTitleHover.backgroundColor = app.settings.grid.cells.titleBackgroundColorHover; 302 | else dial.styles.grid.linkTitleHover.backgroundColor = 'transparent'; 303 | dial.styles.grid.linkEmpty = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td>a.Empty { display: none; }')].style; 304 | dial.styles.grid.linkBack = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td>a.Back :first-child { background-image: ' + app.settings.grid.backIcon + '; }')].style; 305 | applyImageMode(app.settings.grid.backIconMode, dial.styles.grid.linkBack); 306 | dial.styles.grid.linkFolder = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td>a.Folder :first-child { background-image: ' + app.settings.grid.folderIcon + '; background-repeat: no-repeat; background-size: 100% 100%; }')].style; 307 | applyImageMode(app.settings.grid.folderIconMode, dial.styles.grid.linkFolder); 308 | dial.styles.grid.linkBookmark = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td>a.Bookmark :first-child { background-repeat: no-repeat; background-size: 100% 100%; }')].style; 309 | dial.styles.grid.linkBookmarkLoading = dial.Style.sheet.cssRules[dial.Style.sheet.insertRule('.Grid td>a.BookmarkLoading :first-child { background-image: url("' + app.settings.grid.cells.loadingIcon + '"); background-repeat: no-repeat; background-position: center center; }')].style; 310 | if(oldStyle) document.head.removeChild(oldStyle); 311 | }; 312 | dial.initGrid = function(){ 313 | var oldGrid = dial.Grid; 314 | dial.Grid = document.createElement('table'); 315 | var grid = document.createElement('table'); 316 | dial.Grid.className = 'Grid'; 317 | dial.Grid.getLink = function(index){ 318 | var num_columns = dial.Grid.rows[0].cells.length; 319 | return dial.Grid.rows[Math.floor(index/num_columns)].cells[index % num_columns].childNodes[0]; 320 | } 321 | for(var i=0; i=0) link.childNodes[0].style.backgroundImage = node.image; 510 | else link.childNodes[0].style.backgroundImage = 'url(' + node.image + ')'; 511 | } else link.childNodes[0].style.backgroundImage = ''; 512 | link.childNodes[1].innerText = node.title; 513 | switch(app.settings.grid.openFolderMethod){ 514 | case 0: 515 | if(link.target) delete link.target; 516 | break; 517 | case 1: 518 | link.target = '_blank'; 519 | break; 520 | } 521 | if(dial.path) link.href = '?' + 'bg=' + encodeURIComponent(app.settings.backgroundColor) + '&path=' + encodeURIComponent(dial.path + node.title); 522 | else link.href = '?' + 'bg=' + encodeURIComponent(app.settings.backgroundColor) + '&path=' + encodeURIComponent(node.title); 523 | link.onclick = null; 524 | link.setAttribute('contextmenu', 'item'); 525 | } 526 | populateBookmark = function(link, node){ 527 | link.Node = node; 528 | if(node.imageMode || node.imageMode == 0) applyImageMode(node.imageMode, link.childNodes[0].style); 529 | else applyImageMode(-1, link.childNodes[0].style); 530 | if(node.image){ 531 | link.className = 'Bookmark'; 532 | if(node.image.indexOf('url(')>=0) link.childNodes[0].style.backgroundImage = node.image; 533 | else link.childNodes[0].style.backgroundImage = 'url(' + node.image + ')'; 534 | } else { 535 | link.className = 'BookmarkLoading'; 536 | link.childNodes[0].style.backgroundImage = ''; 537 | dial.refreshNode(link); 538 | } 539 | link.childNodes[1].innerText = node.title; 540 | switch(app.settings.grid.openBookmarkMethod){ 541 | case 0: 542 | if(link.target) delete link.target; 543 | break; 544 | case 1: 545 | link.target = '_blank'; 546 | break; 547 | } 548 | link.href = node.url; 549 | link.onclick = null; 550 | link.setAttribute('contextmenu', 'item'); 551 | } 552 | 553 | var iBase = 0; 554 | var linkItem = 0; 555 | var allCells = app.settings.grid.rows * app.settings.grid.columns; 556 | var maxCells = allCells; 557 | if(app.settings.grid.backNode && dial.path != '/'){ 558 | populateBack(dial.Grid.getLink(linkItem)); 559 | linkItem++; 560 | maxCells -= 1; 561 | } 562 | dial.maxpage = Math.floor(app.node.children.length / maxCells); 563 | if(dial.maxpage != app.node.children.length / maxCells) dial.maxpage += 1; 564 | if(dial.page > dial.maxpage) dial.page = dial.maxpage; 565 | if(dial.page > 1) iBase = (dial.page -1) * maxCells; 566 | for(var i = iBase; i= 0; i--) { 605 | switch(e.responseHeaders[i].name.toLowerCase()){ 606 | case 'x-frame-options': 607 | case 'frame-options': 608 | case 'content-security-policy': 609 | e.responseHeaders.splice(i, 1); 610 | break; 611 | } 612 | } 613 | return { responseHeaders: e.responseHeaders }; 614 | }; 615 | function pageLoaded(){ 616 | if(!iframe) return; 617 | function clean(){ 618 | if(!iframe) return; 619 | selectedItem.children[0].removeChild(iframe); 620 | dial.capture -= 1; 621 | if(dial.capture == 0){ 622 | browser.webRequest.onHeadersReceived.removeListener(headersReceived); 623 | browser.tabs.update(tab.id, {muted: false}).then(); 624 | } 625 | iframe = null; 626 | } 627 | setTimeout(function(){ 628 | browser.tabs.captureVisibleTab().then(function(img){ 629 | var imgObj = new Image; 630 | imgObj.src = img; 631 | var canvas = document.createElement('canvas'); 632 | canvas.style.width = rect.width.toString() + 'px'; 633 | canvas.style.height = rect.height.toString() + 'px'; 634 | canvas.width = rect.width; 635 | canvas.height = rect.height; 636 | var ctx = canvas.getContext('2d'); 637 | ctx.clearRect(0, 0, rect.width, rect.height); 638 | ctx.save(); 639 | setTimeout(function(){ 640 | ctx.drawImage(imgObj, rect.left, rect.top, rect.width, rect.height, 0, 0, rect.width, rect.height); 641 | ctx.restore(); 642 | img = canvas.toDataURL(); 643 | selectedItem.children[0].style.backgroundImage = 'url(' + img + ')'; 644 | clean(); 645 | app.Messages.updateNode(selectedItem.Node.id, { image: img }, function(){ 646 | setTimeout(function(){ 647 | if(dial.capture == 0) dial.skipUpdate = false; 648 | }, 500); 649 | }); 650 | }, 500); 651 | }, clean); 652 | }, 3000); 653 | 654 | 655 | }; 656 | 657 | var tab = null; 658 | var previewWidth = 1200; // Need to be linked to settings 659 | var previewHeight = 710; // Need to be linked to settings 660 | var iframe = document.createElement('iframe'); 661 | var rect = selectedItem.children[0].getBoundingClientRect(); 662 | browser.tabs.getCurrent().then(function(currentTab){ 663 | tab = currentTab; 664 | var ratioX = previewWidth / selectedItem.children[0].offsetWidth; 665 | var ratioY = previewHeight / selectedItem.children[0].offsetHeight; 666 | iframe.style.width = ratioX * selectedItem.children[0].offsetWidth + 'px'; 667 | iframe.style.height = ratioY * selectedItem.children[0].offsetHeight + 'px'; 668 | iframe.style.position = 'absolute'; 669 | iframe.style.MozTransform = 'scale(' + (1/ratioX) + ', ' + (1/ratioY) + ')'; 670 | iframe.style.MozTransformOrigin = 'top left'; 671 | iframe.sandbox = 'allow-scripts allow-same-origin'; 672 | iframe.onload = function(){ pageLoaded(); } 673 | dial.capture += 1; 674 | if(dial.capture == 1){ 675 | dial.skipUpdate = true; 676 | browser.webRequest.onHeadersReceived.addListener(headersReceived, { urls:['*://*/*'], types:['sub_frame'] }, ['blocking', 'responseHeaders']); 677 | browser.tabs.update(tab.id, {muted: true}).then(); 678 | } 679 | iframe.src = selectedItem.Node.url; 680 | selectedItem.children[0].appendChild(iframe); 681 | //setTimeout(function(){ pageLoaded(); }, 6000); 682 | }); 683 | } 684 | dial.capturePage = function(selectedItem){ 685 | selectedItem.className = 'BookmarkLoading'; 686 | selectedItem.childNodes[0].style.backgroundImage = app.settings.grid.loadingIcon; 687 | app.Messages.capturePage(selectedItem.Node.id); 688 | } 689 | 690 | 691 | 692 | 693 | dial.PopupPanel = function(width, height, modal){ // PopupPanel Object 694 | this.panelContainer = document.createElement('div'); 695 | this.panelContainer.style.position = 'fixed'; 696 | this.panelContainer.style.left = '0pt'; 697 | this.panelContainer.style.top = '0pt'; 698 | this.panelContainer.style.width = '100%'; 699 | this.panelContainer.style.height = '100%'; 700 | 701 | this.panel = document.createElement('div'); 702 | this.panel.style.margin = 'auto'; 703 | this.panel.style.marginTop = '30pt'; 704 | this.panel.style.width = width + 'px'; 705 | this.panel.style.height = height + 'px'; 706 | this.panel.style.backgroundColor = '#FFFFFF'; 707 | this.panelContainer.appendChild(this.panel); 708 | this.frame = this.panel; 709 | if(modal == true){ 710 | this.modal = document.createElement('div'); 711 | this.modal.style.position = 'fixed'; 712 | this.modal.style.left = '0px'; 713 | this.modal.style.top = '0px'; 714 | this.modal.style.width = '100%'; 715 | this.modal.style.height = '100%'; 716 | this.modal.style.backgroundColor = '#404040'; 717 | this.modal.style.opacity = 0.5; 718 | this._contextMenuHandler = function(e){ e.preventDefault(); } 719 | this.popup = function(){ 720 | window.addEventListener('contextmenu', this._contextMenuHandler, false); 721 | document.body.appendChild(this.modal); 722 | document.body.appendChild(this.panelContainer); 723 | } 724 | this.close = function(){ 725 | document.body.removeChild(this.modal); 726 | document.body.removeChild(this.panelContainer); 727 | window.removeEventListener('contextmenu', this._contextMenuHandler, false); 728 | } 729 | } else { 730 | this.popup = function(){ 731 | document.body.appendChild(this.panelContainer); 732 | } 733 | this.close = function(){ 734 | document.body.removeChild(this.panelContainer); 735 | } 736 | } 737 | } 738 | 739 | dial.editSettings = function(){ 740 | var popup = new dial.PopupPanel(500, 440, true); 741 | var iframe = document.createElement('iframe'); 742 | iframe.style.width = '100%'; 743 | iframe.style.height = '100%'; 744 | iframe.style.backgroundColor = 'transparent'; 745 | iframe.style.border = '0px none transparent'; 746 | iframe.style.padding = '0px'; 747 | iframe.style.overflow = 'hidden'; 748 | popup.frame.appendChild(iframe); 749 | iframe.src = '/html/settings.html'; 750 | iframe.popup = popup; 751 | popup.popup(); 752 | iframe.focus(); 753 | } 754 | 755 | dial.editProperties = function(selectedItem){ 756 | var popup = new dial.PopupPanel(500, 420, true); 757 | var iframe = document.createElement('iframe'); 758 | iframe.style.width = '100%'; 759 | iframe.style.height = '100%'; 760 | iframe.style.backgroundColor = 'transparent'; 761 | iframe.style.border = '0px none transparent'; 762 | iframe.style.padding = '0px'; 763 | iframe.style.overflow = 'hidden'; 764 | popup.frame.appendChild(iframe); 765 | iframe.src = '/html/properties.html?id=' + selectedItem.Node.id; 766 | iframe.popup = popup; 767 | popup.popup(); 768 | iframe.focus(); 769 | } 770 | -------------------------------------------------------------------------------- /src/js/properties.js: -------------------------------------------------------------------------------- 1 | var app = {}; 2 | 3 | var Image = null; 4 | 5 | document.addEventListener("DOMContentLoaded", function(event) { 6 | app.init(); 7 | }); 8 | 9 | app.init = function(){ 10 | document.addEventListener('keyup', function(e){ 11 | switch(e.key){ 12 | case 'Escape': 13 | window.frameElement.popup.close(); 14 | break; 15 | } 16 | }); 17 | app.Messages.getSettings(function(settings){ 18 | app.settings = settings; 19 | app.Messages.getNodeByID(new URL(window.location).searchParams.get('id'), function(node){ 20 | app.node = node; 21 | Title.value = node.title; 22 | if(node.imageMode == 0) ImageMode.value = 0; 23 | else if(node.imageMode) ImageMode.value = node.imageMode; 24 | ImagePreview.style.backgroundRepeat = 'no-repeat'; 25 | ImagePreview.style.backgroundSize = '100% 100%'; 26 | switch(node.type){ 27 | case app.GridNodes.GridNodeType.folder: 28 | TitleLocked.parentNode.style.display = 'none'; 29 | ImageLocked.parentNode.style.display = 'none'; 30 | Url.parentNode.parentNode.style.display = 'none'; 31 | if(node.image){ 32 | if(node.image.indexOf('url(')>=0) Image = node.image; 33 | else Image = 'url(' + node.image + ')'; 34 | } else Image = null; 35 | if(Image==null) ImagePreview.style.backgroundImage = app.settings.grid.folderIcon; 36 | else ImagePreview.style.backgroundImage = Image; 37 | break; 38 | case app.GridNodes.GridNodeType.bookmark: 39 | TitleLocked.checked = (node.titleLocked==true); 40 | ImageLocked.checked = (node.imageLocked==true); 41 | ImageDefault.style.display = 'none'; 42 | Url.value = node.url; 43 | if(node.image.indexOf('url(')>=0) Image = node.image; 44 | else Image = 'url(' + node.image + ')'; 45 | ImagePreview.style.backgroundImage = Image; 46 | break; 47 | } 48 | 49 | ImageReset.onclick = function(){ 50 | switch(node.type){ 51 | case app.GridNodes.GridNodeType.folder: 52 | if(node.image){ 53 | Image = node.image; 54 | ImagePreview.style.backgroundImage = 'url(' + Image + ')'; 55 | } else { 56 | Image = null; 57 | ImagePreview.style.backgroundImage = app.settings.grid.folderIcon; 58 | } 59 | break; 60 | case app.GridNodes.GridNodeType.bookmark: 61 | Image = node.image; 62 | ImagePreview.style.backgroundImage = 'url(' + Image + ')'; 63 | break; 64 | } 65 | }; 66 | 67 | ImageDefault.onclick = function(){ 68 | switch(node.type){ 69 | case app.GridNodes.GridNodeType.folder: 70 | Image = null; 71 | ImagePreview.style.backgroundImage = app.settings.grid.folderIcon; 72 | break; 73 | case app.GridNodes.GridNodeType.bookmark: 74 | break; 75 | } 76 | }; 77 | 78 | 79 | ImageFile.onclick = function(){ 80 | this.value = null; 81 | } 82 | ImageFile.onchange = function(){ 83 | var fileReader = new FileReader(); 84 | fileReader.onload = function(e){ 85 | Image = e.target.result; 86 | ImageFile.value = null; 87 | ImagePreview.style.backgroundImage = 'url(' + Image + ')'; 88 | } 89 | fileReader.readAsDataURL(ImageFile.files[0]); 90 | } 91 | 92 | 93 | }); 94 | }); 95 | 96 | 97 | BtnOk.onclick = function(){ 98 | BtnApply.onclick(); 99 | window.frameElement.popup.close(); 100 | } 101 | BtnApply.onclick = function(){ 102 | switch(app.node.type){ 103 | case app.GridNodes.GridNodeType.folder: 104 | app.Messages.updateNode(app.node.id, { title: Title.value, image: Image, imageMode: +(ImageMode.value) }) 105 | break; 106 | case app.GridNodes.GridNodeType.bookmark: 107 | app.Messages.updateNode(app.node.id, { title: Title.value, titleLocked: TitleLocked.checked, imageLocked: ImageLocked.checked, url: Url.value, image: Image, imageMode: +(ImageMode.value) }) 108 | break; 109 | } 110 | } 111 | BtnCancel.onclick = function(){ 112 | window.frameElement.popup.close(); 113 | } 114 | 115 | var tabButtons = Tabs.children[0].children[0].children[0]; 116 | for(var i=0; i" 26 | ], 27 | 28 | "background": { 29 | "scripts": ["js/background.js"] 30 | }, 31 | 32 | "browser_action": { 33 | "default_icon": { 34 | "24": "img/24.png", 35 | "32": "img/32.png" 36 | } 37 | }, 38 | 39 | "chrome_url_overrides" : { 40 | "newtab": "dial" 41 | }, 42 | 43 | "applications": { 44 | "gecko": { 45 | "id": "quickdial@matmoul.com", 46 | "strict_min_version": "70.0" 47 | } 48 | }, 49 | 50 | "default_locale": "en" 51 | } 52 | --------------------------------------------------------------------------------