├── .gitignore ├── Changelog.md ├── Jakefile ├── LICENSE ├── README.md ├── package.json └── src ├── .eslintignore ├── .eslintrc.json ├── bootstrap.js ├── chrome.manifest ├── content └── icons │ ├── extension-32.png │ ├── extension-64.png │ └── togglebutton │ ├── LICENSE │ ├── icon-16.png │ ├── icon-32.png │ ├── icon-inverted-16.png │ └── icon-inverted-32.png ├── data ├── action_creators.js ├── assets │ └── css │ │ └── groupspanel.css ├── components │ ├── app.js │ ├── group.js │ ├── groupaddbutton.js │ ├── groupcontrols.js │ ├── grouplist.js │ ├── tab.js │ └── tablist.js ├── groupspanel.html ├── groupspanel.js ├── reducer.js └── vendor │ ├── css │ └── font-awesome.css │ ├── fonts │ ├── fontawesome.eot │ ├── fontawesome.svg │ ├── fontawesome.ttf │ └── fontawesome.woff │ └── js │ ├── classnames.min.js │ ├── immutable.min.js │ ├── react-dom.min.js │ ├── react-redux.min.js │ ├── react.min.js │ └── redux.min.js ├── index.js ├── install.rdf ├── lib ├── storage │ └── session.js ├── tabmanager.js └── utils.js ├── locale ├── de-DE.properties ├── en-US.properties ├── es-AR.properties ├── es-ES.properties ├── fr-FR.properties └── nl-NL.properties └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | node_modules/ 3 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # 0.6.0 2 | 3 | ## Features 4 | 5 | * Added a timeout when closing a group to restore a group when closed by accident (PR [34](https://github.com/denschub/firefox-tabgroups/pull/34)) 6 | 7 | ## Bug fixes 8 | 9 | * Show the correct icon when using the dark compact theme 10 | * Fix invalid group selection after closing tabs (PR [51](https://github.com/denschub/firefox-tabgroups/pull/51)) 11 | 12 | # 0.5.1 13 | 14 | Set `multiprocessCompatible` to `true`! 15 | 16 | # 0.5.0 17 | 18 | ## Bug fixes 19 | 20 | * Use the inverted toolbar icon if needed on high resolutions. 21 | 22 | ## Refactorings 23 | 24 | * Use the default favicon instead of no favicon at all. (PR [35](https://github.com/denschub/firefox-tabgroups/pull/35)) 25 | * Removed the Panorama migration to avoid breakage in Firefox 52 26 | 27 | # 0.4.0 28 | 29 | ## Features 30 | 31 | * You can now drag and drop tabs in the panel to move them between groups. Dragging a tab onto the "Create new group" button will create a new group with that tab. (PR [32](https://github.com/denschub/firefox-tabgroups/pull/32)) 32 | 33 | # 0.3.0 34 | 35 | ## Bug fixes 36 | 37 | * Clicking the input field while renaming will no longer select the group. (PR [28](https://github.com/denschub/firefox-tabgroups/pull/28)) 38 | * Use `label` instead of `visibleLabel` since the latter was removed in bug 1247920. 39 | 40 | # 0.2.1 41 | 42 | * Don't try to set the groups active tab if an app tab is active. (Issue [#27](https://github.com/denschub/firefox-tabgroups/issues/27)) 43 | 44 | # 0.2.0 45 | 46 | ## Features 47 | 48 | * Added a migration override so tab groups won't be migrated away. 49 | * Added compatiblity with Quicksavers Tab Groups add-on. 50 | * Added keyboard shortcuts to switch between groups. (PR [#23](https://github.com/denschub/firefox-tabgroups/pull/23)) 51 | * Added option for alphabetic tab group sorting. (PR [#24](https://github.com/denschub/firefox-tabgroups/pull/24)) 52 | 53 | ## Refactorings 54 | 55 | * `minVersion` is now set to 44 so people have a chance to install the addon before the migration kicks in. 56 | * `Tab Groups` was renamed to `Simplified Tab Groups` to avoid confusion and conflicts with other add-ons. 57 | * Simpilified development by adding `jake` and some basic scripts. 58 | 59 | # 0.1.1 60 | 61 | This was the first public release. 62 | -------------------------------------------------------------------------------- /Jakefile: -------------------------------------------------------------------------------- 1 | /* global desc, task, jake, complete */ 2 | /* vim: set fdl=0: */ 3 | "use strict"; 4 | 5 | const SRC_DIR = "./src"; 6 | const DIST_DIR = "./dist"; 7 | 8 | desc("Builds the source"); 9 | task("build", ["cleanup"], () => { 10 | jake.cpR(SRC_DIR, DIST_DIR); 11 | }); 12 | 13 | desc("Removes all build-related files"); 14 | task("cleanup", () => { 15 | jake.rmRf(DIST_DIR); 16 | }); 17 | 18 | desc("runs wslint on the built source"); 19 | task("lint", ["build"], {async: true}, () => { 20 | jake.exec([`cd ${DIST_DIR}; eslint .`], { 21 | interactive: true 22 | }, complete); 23 | }); 24 | 25 | desc("Builds the source and starts a test installation. You can specify jpm parameters with 'JPM_PARAMS=\"...\" jake run'"); 26 | task("run", ["build"], {async: true}, () => { 27 | jake.exec([`cd ${DIST_DIR}; jpm run ${process.env.JPM_PARAMS}`], { 28 | interactive: true 29 | }, complete); 30 | }); 31 | 32 | desc("Builds the source and generates a XPI"); 33 | task("xpi", ["build"], {async: true}, () => { 34 | jake.exec([ 35 | `cd ${DIST_DIR}; jpm xpi`, 36 | `cd ${DIST_DIR}; find . -not -name "*.xpi" -delete` 37 | ], {printStderr: true}, () => { 38 | console.log(`.xpi was created in ${DIST_DIR}`); 39 | complete(); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | NOTE: The menu button icons were imported from Firefox. Please see 2 | data/assets/images/LICENSE for further information! 3 | 4 | -------------------------------------------------------------------------------- 5 | 6 | Copyright (c) 2015 Dennis Schubert 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of 9 | this software and associated documentation files (the "Software"), to deal in 10 | the Software without restriction, including without limitation the rights to 11 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 | of the Software, and to permit persons to whom the Software is furnished to do 13 | so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Simplified Tab Groups for Firefox 2 | ================================= 3 | 4 | **NOTE**: This project is currently unmaintained. If someone wants to take over, [check this discussion](https://github.com/denschub/firefox-tabgroups/issues/60#issuecomment-388541616) and drop me a note. 5 | 6 | --- 7 | 8 | This project aims to provide a simple add-on to replace some functionalities 9 | from TabView/Tab Groups/Panorama which were removed from Firefox due to a lot 10 | of open bugs and a very low overall usage. 11 | 12 | Installation 13 | ------------ 14 | 15 | The add-on is available at [addons.mozilla.org][amo] and should be installed 16 | there to ensure the add-on stays updated. 17 | 18 | Warning 19 | ------- 20 | 21 | Please note that this extension is currently in a very unstable and untested 22 | state and may kill your tabs or small kittens. While it may get improved and 23 | secured in the future, I strongly advise you to make a backup of your important 24 | tabs... 25 | 26 | Building 27 | -------- 28 | 29 | Assuming you have Node.js v5 installed on your machine, building this project 30 | is rather easy. 31 | 32 | 1. Install the dependencies: `npm install`. 33 | 2. Run `./node_modules/.bin/jake build` to build all source files into the 34 | `dist/` directory or run `./node_modules/.bin/jake run` to build the add-on 35 | and start a Firefox instance for testing. 36 | 37 | `jake run` uses `jpm` and you can pass additional parameters to it by setting 38 | an environment variable, for example: `JPM_PARAMS="-b nightly" jake run` 39 | 40 | Contributing 41 | ------------ 42 | 43 | Feel free to [fix some open issues][issues] and submit a pull request. Please 44 | make sure to file the pull request against the `develop` branch, which should 45 | be the default. Please make sure your code passes the coding styleguides by 46 | running `jake lint` before submitting the PR. 47 | 48 | If you want to help translating this add-on, feel free to alter or add new 49 | files in `src/locale`. The extensions name and descriptions have to be changed 50 | in `src/install.rdf`. 51 | 52 | License 53 | ------- 54 | 55 | MIT. 56 | 57 | [amo]: https://addons.mozilla.org/en-US/firefox/addon/tab-groups/ 58 | [issues]: https://github.com/denschub/firefox-tabgroups/issues 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tabgroups", 3 | "author": "", 4 | "license": "MIT", 5 | "private": true, 6 | "devDependencies": { 7 | "jake": "^8.0.12", 8 | "jpm": "^1.0.3" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/.eslintignore: -------------------------------------------------------------------------------- 1 | data/vendor/ 2 | bootstrap.js 3 | -------------------------------------------------------------------------------- /src/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module" 5 | }, 6 | "env": { 7 | "browser": true, 8 | "es6": true, 9 | "mocha": true, 10 | "node": true 11 | }, 12 | "globals": { 13 | "Cc": true, 14 | "Ci": true, 15 | "Components": true, 16 | "Cr": true, 17 | "Cu": true, 18 | "EventEmitter": true, 19 | "Services": true, 20 | "Task": true, 21 | "XPCNativeWrapper": true, 22 | "XPCOMUtils": true, 23 | "console": true, 24 | "console": true, 25 | "devtools": true, 26 | "dump": true, 27 | "exports": true, 28 | "exports": true, 29 | "loader": true, 30 | "module": true, 31 | "require": true, 32 | "require": true, 33 | 34 | "addon": true, 35 | "ActionCreators": true, 36 | "App": true, 37 | "classNames": true, 38 | "Group": true, 39 | "GroupAddButton": true, 40 | "GroupControls": true, 41 | "GroupList": true, 42 | "Immutable": true, 43 | "React": true, 44 | "ReactDOM": true, 45 | "ReactRedux": true, 46 | "Reducer": true, 47 | "Redux": true, 48 | "Tab": true, 49 | "TabList": true 50 | }, 51 | "rules": { 52 | "block-scoped-var": 0, 53 | "brace-style": [2, "1tbs", {"allowSingleLine": false}], 54 | "camelcase": 2, 55 | "comma-dangle": 1, 56 | "comma-spacing": [2, {"before": false, "after": true}], 57 | "comma-style": [2, "last"], 58 | "complexity": 1, 59 | "consistent-return": 2, 60 | "consistent-this": 0, 61 | "curly": 2, 62 | "default-case": 0, 63 | "dot-location": [1, "property"], 64 | "dot-notation": 2, 65 | "eol-last": 2, 66 | "eqeqeq": 0, 67 | "func-names": 0, 68 | "func-style": 0, 69 | "generator-star": 0, 70 | "generator-star-spacing": [1, "after"], 71 | "global-strict": 0, 72 | "guard-for-in": 0, 73 | "handle-callback-err": 0, 74 | "indent": [2, 2, {"SwitchCase": 1}], 75 | "key-spacing": [1, {"beforeColon": false, "afterColon": true}], 76 | "keyword-spacing": 1, 77 | "linebreak-style": 0, 78 | "max-depth": 0, 79 | "max-len": [1, 80], 80 | "max-nested-callbacks": [2, 3], 81 | "max-params": 0, 82 | "max-statements": 0, 83 | "new-cap": [2, {"capIsNew": false}], 84 | "new-parens": 2, 85 | "newline-after-var": 0, 86 | "no-alert": 0, 87 | "no-array-constructor": 2, 88 | "no-bitwise": 0, 89 | "no-caller": 2, 90 | "no-catch-shadow": 1, 91 | "no-comma-dangle": 0, 92 | "no-cond-assign": 2, 93 | "no-console": 0, 94 | "no-constant-condition": 0, 95 | "no-continue": 0, 96 | "no-control-regex": 2, 97 | "no-debugger": 2, 98 | "no-delete-var": 2, 99 | "no-div-regex": 0, 100 | "no-dupe-args": 2, 101 | "no-dupe-keys": 2, 102 | "no-duplicate-case": 2, 103 | "no-else-return": 2, 104 | "no-empty": 2, 105 | "no-empty-character-class": 2, 106 | "no-eq-null": 0, 107 | "no-eval": 2, 108 | "no-ex-assign": 2, 109 | "no-extend-native": 2, 110 | "no-extra-bind": 2, 111 | "no-extra-boolean-cast": 2, 112 | "no-extra-parens": 0, 113 | "no-extra-semi": 2, 114 | "no-extra-strict": 0, 115 | "no-fallthrough": 2, 116 | "no-floating-decimal": 0, 117 | "no-func-assign": 0, 118 | "no-implied-eval": 0, 119 | "no-inline-comments": 1, 120 | "no-inner-declarations": 0, 121 | "no-invalid-regexp": 0, 122 | "no-irregular-whitespace": 0, 123 | "no-iterator": 0, 124 | "no-label-var": 0, 125 | "no-labels": 0, 126 | "no-lone-blocks": 0, 127 | "no-lonely-if": 2, 128 | "no-loop-func": 0, 129 | "no-mixed-requires": 0, 130 | "no-mixed-spaces-and-tabs": 2, 131 | "no-multi-spaces": 1, 132 | "no-multi-str": 1, 133 | "no-multiple-empty-lines": [1, {"max": 1}], 134 | "no-native-reassign": 2, 135 | "no-negated-in-lhs": 0, 136 | "no-nested-ternary": 2, 137 | "no-new": 0, 138 | "no-new-func": 0, 139 | "no-new-object": 0, 140 | "no-new-require": 0, 141 | "no-new-wrappers": 0, 142 | "no-obj-calls": 0, 143 | "no-octal": 1, 144 | "no-octal-escape": 0, 145 | "no-param-reassign": 0, 146 | "no-path-concat": 0, 147 | "no-plusplus": 0, 148 | "no-process-env": 0, 149 | "no-process-exit": 0, 150 | "no-proto": 2, 151 | "no-redeclare": 2, 152 | "no-regex-spaces": 2, 153 | "no-reserved-keys": 0, 154 | "no-restricted-modules": 0, 155 | "no-return-assign": 2, 156 | "no-script-url": 0, 157 | "no-self-compare": 2, 158 | "no-sequences": 2, 159 | "no-shadow": 1, 160 | "no-shadow-restricted-names": 2, 161 | "no-space-before-semi": 0, 162 | "no-spaced-func": 1, 163 | "no-sparse-arrays": 2, 164 | "no-sync": 0, 165 | "no-ternary": 0, 166 | "no-throw-literal": 2, 167 | "no-trailing-spaces": 2, 168 | "no-undef": 2, 169 | "no-undef-init": 0, 170 | "no-undefined": 0, 171 | "no-underscore-dangle": 0, 172 | "no-unneeded-ternary": 2, 173 | "no-unreachable": 2, 174 | "no-unused-expressions": 0, 175 | "no-unused-vars": 1, 176 | "no-use-before-define": 0, 177 | "no-var": 0, 178 | "no-void": 0, 179 | "no-warning-comments": 0, 180 | "no-with": 2, 181 | "no-wrap-func": 0, 182 | "object-shorthand": 0, 183 | "one-var": 0, 184 | "operator-assignment": 0, 185 | "operator-linebreak": 0, 186 | "padded-blocks": [1, "never"], 187 | "quote-props": 0, 188 | "quotes": [1, "double", "avoid-escape"], 189 | "radix": 2, 190 | "semi": [1, "always"], 191 | "semi-spacing": [1, {"before": false, "after": true}], 192 | "sort-vars": 0, 193 | "space-before-blocks": [1, "always"], 194 | "space-before-function-paren": [1, "never"], 195 | "space-before-function-parentheses": 0, 196 | "space-in-brackets": 0, 197 | "space-in-parens": [1, "never"], 198 | "space-infix-ops": [1, {"int32Hint": true}], 199 | "space-unary-ops": [1, { "words": true, "nonwords": false }], 200 | "space-unary-word-ops": 0, 201 | "spaced-comment": [1, "always"], 202 | "strict": [2, "global"], 203 | "use-isnan": 2, 204 | "valid-jsdoc": 0, 205 | "valid-typeof": 2, 206 | "vars-on-top": 0, 207 | "wrap-iife": 0, 208 | "wrap-regex": 0, 209 | "yoda": 2 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This Source Code is based on the original bootstrap.js generated by the 3 | * add-on sdk which is subject to the terms of the Mozilla Public License, v. 4 | * 2.0. You can obtain a copy of this license at http://mozilla.org/MPL/2.0/. 5 | */ 6 | 7 | "use strict"; 8 | 9 | const { utils: Cu } = Components; 10 | const rootURI = __SCRIPT_URI_SPEC__.replace("bootstrap.js", ""); 11 | const COMMONJS_URI = "resource://gre/modules/commonjs"; 12 | const { require } = Cu.import(COMMONJS_URI + "/toolkit/require.js", {}); 13 | const { Bootstrap } = require(COMMONJS_URI + "/sdk/addon/bootstrap.js"); 14 | 15 | var { startup, shutdown, install, uninstall } = new Bootstrap(rootURI); 16 | -------------------------------------------------------------------------------- /src/chrome.manifest: -------------------------------------------------------------------------------- 1 | content simplified-tabgroups content/ 2 | -------------------------------------------------------------------------------- /src/content/icons/extension-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denschub/firefox-tabgroups/b92a0224ac0cb6f0eddc170cca9c08b98894ea63/src/content/icons/extension-32.png -------------------------------------------------------------------------------- /src/content/icons/extension-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denschub/firefox-tabgroups/b92a0224ac0cb6f0eddc170cca9c08b98894ea63/src/content/icons/extension-64.png -------------------------------------------------------------------------------- /src/content/icons/togglebutton/LICENSE: -------------------------------------------------------------------------------- 1 | NOTE: Icons were imported from Firefox as the original TabView was 2 | removed and those icons are no longer needed. 3 | 4 | -------------------------------------------------------------------------------- 5 | 6 | Mozilla Public License Version 2.0 7 | ================================== 8 | 9 | 1. Definitions 10 | -------------- 11 | 12 | 1.1. "Contributor" 13 | means each individual or legal entity that creates, contributes to 14 | the creation of, or owns Covered Software. 15 | 16 | 1.2. "Contributor Version" 17 | means the combination of the Contributions of others (if any) used 18 | by a Contributor and that particular Contributor's Contribution. 19 | 20 | 1.3. "Contribution" 21 | means Covered Software of a particular Contributor. 22 | 23 | 1.4. "Covered Software" 24 | means Source Code Form to which the initial Contributor has attached 25 | the notice in Exhibit A, the Executable Form of such Source Code 26 | Form, and Modifications of such Source Code Form, in each case 27 | including portions thereof. 28 | 29 | 1.5. "Incompatible With Secondary Licenses" 30 | means 31 | 32 | (a) that the initial Contributor has attached the notice described 33 | in Exhibit B to the Covered Software; or 34 | 35 | (b) that the Covered Software was made available under the terms of 36 | version 1.1 or earlier of the License, but not also under the 37 | terms of a Secondary License. 38 | 39 | 1.6. "Executable Form" 40 | means any form of the work other than Source Code Form. 41 | 42 | 1.7. "Larger Work" 43 | means a work that combines Covered Software with other material, in 44 | a separate file or files, that is not Covered Software. 45 | 46 | 1.8. "License" 47 | means this document. 48 | 49 | 1.9. "Licensable" 50 | means having the right to grant, to the maximum extent possible, 51 | whether at the time of the initial grant or subsequently, any and 52 | all of the rights conveyed by this License. 53 | 54 | 1.10. "Modifications" 55 | means any of the following: 56 | 57 | (a) any file in Source Code Form that results from an addition to, 58 | deletion from, or modification of the contents of Covered 59 | Software; or 60 | 61 | (b) any new file in Source Code Form that contains any Covered 62 | Software. 63 | 64 | 1.11. "Patent Claims" of a Contributor 65 | means any patent claim(s), including without limitation, method, 66 | process, and apparatus claims, in any patent Licensable by such 67 | Contributor that would be infringed, but for the grant of the 68 | License, by the making, using, selling, offering for sale, having 69 | made, import, or transfer of either its Contributions or its 70 | Contributor Version. 71 | 72 | 1.12. "Secondary License" 73 | means either the GNU General Public License, Version 2.0, the GNU 74 | Lesser General Public License, Version 2.1, the GNU Affero General 75 | Public License, Version 3.0, or any later versions of those 76 | licenses. 77 | 78 | 1.13. "Source Code Form" 79 | means the form of the work preferred for making modifications. 80 | 81 | 1.14. "You" (or "Your") 82 | means an individual or a legal entity exercising rights under this 83 | License. For legal entities, "You" includes any entity that 84 | controls, is controlled by, or is under common control with You. For 85 | purposes of this definition, "control" means (a) the power, direct 86 | or indirect, to cause the direction or management of such entity, 87 | whether by contract or otherwise, or (b) ownership of more than 88 | fifty percent (50%) of the outstanding shares or beneficial 89 | ownership of such entity. 90 | 91 | 2. License Grants and Conditions 92 | -------------------------------- 93 | 94 | 2.1. Grants 95 | 96 | Each Contributor hereby grants You a world-wide, royalty-free, 97 | non-exclusive license: 98 | 99 | (a) under intellectual property rights (other than patent or trademark) 100 | Licensable by such Contributor to use, reproduce, make available, 101 | modify, display, perform, distribute, and otherwise exploit its 102 | Contributions, either on an unmodified basis, with Modifications, or 103 | as part of a Larger Work; and 104 | 105 | (b) under Patent Claims of such Contributor to make, use, sell, offer 106 | for sale, have made, import, and otherwise transfer either its 107 | Contributions or its Contributor Version. 108 | 109 | 2.2. Effective Date 110 | 111 | The licenses granted in Section 2.1 with respect to any Contribution 112 | become effective for each Contribution on the date the Contributor first 113 | distributes such Contribution. 114 | 115 | 2.3. Limitations on Grant Scope 116 | 117 | The licenses granted in this Section 2 are the only rights granted under 118 | this License. No additional rights or licenses will be implied from the 119 | distribution or licensing of Covered Software under this License. 120 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 121 | Contributor: 122 | 123 | (a) for any code that a Contributor has removed from Covered Software; 124 | or 125 | 126 | (b) for infringements caused by: (i) Your and any other third party's 127 | modifications of Covered Software, or (ii) the combination of its 128 | Contributions with other software (except as part of its Contributor 129 | Version); or 130 | 131 | (c) under Patent Claims infringed by Covered Software in the absence of 132 | its Contributions. 133 | 134 | This License does not grant any rights in the trademarks, service marks, 135 | or logos of any Contributor (except as may be necessary to comply with 136 | the notice requirements in Section 3.4). 137 | 138 | 2.4. Subsequent Licenses 139 | 140 | No Contributor makes additional grants as a result of Your choice to 141 | distribute the Covered Software under a subsequent version of this 142 | License (see Section 10.2) or under the terms of a Secondary License (if 143 | permitted under the terms of Section 3.3). 144 | 145 | 2.5. Representation 146 | 147 | Each Contributor represents that the Contributor believes its 148 | Contributions are its original creation(s) or it has sufficient rights 149 | to grant the rights to its Contributions conveyed by this License. 150 | 151 | 2.6. Fair Use 152 | 153 | This License is not intended to limit any rights You have under 154 | applicable copyright doctrines of fair use, fair dealing, or other 155 | equivalents. 156 | 157 | 2.7. Conditions 158 | 159 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 160 | in Section 2.1. 161 | 162 | 3. Responsibilities 163 | ------------------- 164 | 165 | 3.1. Distribution of Source Form 166 | 167 | All distribution of Covered Software in Source Code Form, including any 168 | Modifications that You create or to which You contribute, must be under 169 | the terms of this License. You must inform recipients that the Source 170 | Code Form of the Covered Software is governed by the terms of this 171 | License, and how they can obtain a copy of this License. You may not 172 | attempt to alter or restrict the recipients' rights in the Source Code 173 | Form. 174 | 175 | 3.2. Distribution of Executable Form 176 | 177 | If You distribute Covered Software in Executable Form then: 178 | 179 | (a) such Covered Software must also be made available in Source Code 180 | Form, as described in Section 3.1, and You must inform recipients of 181 | the Executable Form how they can obtain a copy of such Source Code 182 | Form by reasonable means in a timely manner, at a charge no more 183 | than the cost of distribution to the recipient; and 184 | 185 | (b) You may distribute such Executable Form under the terms of this 186 | License, or sublicense it under different terms, provided that the 187 | license for the Executable Form does not attempt to limit or alter 188 | the recipients' rights in the Source Code Form under this License. 189 | 190 | 3.3. Distribution of a Larger Work 191 | 192 | You may create and distribute a Larger Work under terms of Your choice, 193 | provided that You also comply with the requirements of this License for 194 | the Covered Software. If the Larger Work is a combination of Covered 195 | Software with a work governed by one or more Secondary Licenses, and the 196 | Covered Software is not Incompatible With Secondary Licenses, this 197 | License permits You to additionally distribute such Covered Software 198 | under the terms of such Secondary License(s), so that the recipient of 199 | the Larger Work may, at their option, further distribute the Covered 200 | Software under the terms of either this License or such Secondary 201 | License(s). 202 | 203 | 3.4. Notices 204 | 205 | You may not remove or alter the substance of any license notices 206 | (including copyright notices, patent notices, disclaimers of warranty, 207 | or limitations of liability) contained within the Source Code Form of 208 | the Covered Software, except that You may alter any license notices to 209 | the extent required to remedy known factual inaccuracies. 210 | 211 | 3.5. Application of Additional Terms 212 | 213 | You may choose to offer, and to charge a fee for, warranty, support, 214 | indemnity or liability obligations to one or more recipients of Covered 215 | Software. However, You may do so only on Your own behalf, and not on 216 | behalf of any Contributor. You must make it absolutely clear that any 217 | such warranty, support, indemnity, or liability obligation is offered by 218 | You alone, and You hereby agree to indemnify every Contributor for any 219 | liability incurred by such Contributor as a result of warranty, support, 220 | indemnity or liability terms You offer. You may include additional 221 | disclaimers of warranty and limitations of liability specific to any 222 | jurisdiction. 223 | 224 | 4. Inability to Comply Due to Statute or Regulation 225 | --------------------------------------------------- 226 | 227 | If it is impossible for You to comply with any of the terms of this 228 | License with respect to some or all of the Covered Software due to 229 | statute, judicial order, or regulation then You must: (a) comply with 230 | the terms of this License to the maximum extent possible; and (b) 231 | describe the limitations and the code they affect. Such description must 232 | be placed in a text file included with all distributions of the Covered 233 | Software under this License. Except to the extent prohibited by statute 234 | or regulation, such description must be sufficiently detailed for a 235 | recipient of ordinary skill to be able to understand it. 236 | 237 | 5. Termination 238 | -------------- 239 | 240 | 5.1. The rights granted under this License will terminate automatically 241 | if You fail to comply with any of its terms. However, if You become 242 | compliant, then the rights granted under this License from a particular 243 | Contributor are reinstated (a) provisionally, unless and until such 244 | Contributor explicitly and finally terminates Your grants, and (b) on an 245 | ongoing basis, if such Contributor fails to notify You of the 246 | non-compliance by some reasonable means prior to 60 days after You have 247 | come back into compliance. Moreover, Your grants from a particular 248 | Contributor are reinstated on an ongoing basis if such Contributor 249 | notifies You of the non-compliance by some reasonable means, this is the 250 | first time You have received notice of non-compliance with this License 251 | from such Contributor, and You become compliant prior to 30 days after 252 | Your receipt of the notice. 253 | 254 | 5.2. If You initiate litigation against any entity by asserting a patent 255 | infringement claim (excluding declaratory judgment actions, 256 | counter-claims, and cross-claims) alleging that a Contributor Version 257 | directly or indirectly infringes any patent, then the rights granted to 258 | You by any and all Contributors for the Covered Software under Section 259 | 2.1 of this License shall terminate. 260 | 261 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 262 | end user license agreements (excluding distributors and resellers) which 263 | have been validly granted by You or Your distributors under this License 264 | prior to termination shall survive termination. 265 | 266 | ************************************************************************ 267 | * * 268 | * 6. Disclaimer of Warranty * 269 | * ------------------------- * 270 | * * 271 | * Covered Software is provided under this License on an "as is" * 272 | * basis, without warranty of any kind, either expressed, implied, or * 273 | * statutory, including, without limitation, warranties that the * 274 | * Covered Software is free of defects, merchantable, fit for a * 275 | * particular purpose or non-infringing. The entire risk as to the * 276 | * quality and performance of the Covered Software is with You. * 277 | * Should any Covered Software prove defective in any respect, You * 278 | * (not any Contributor) assume the cost of any necessary servicing, * 279 | * repair, or correction. This disclaimer of warranty constitutes an * 280 | * essential part of this License. No use of any Covered Software is * 281 | * authorized under this License except under this disclaimer. * 282 | * * 283 | ************************************************************************ 284 | 285 | ************************************************************************ 286 | * * 287 | * 7. Limitation of Liability * 288 | * -------------------------- * 289 | * * 290 | * Under no circumstances and under no legal theory, whether tort * 291 | * (including negligence), contract, or otherwise, shall any * 292 | * Contributor, or anyone who distributes Covered Software as * 293 | * permitted above, be liable to You for any direct, indirect, * 294 | * special, incidental, or consequential damages of any character * 295 | * including, without limitation, damages for lost profits, loss of * 296 | * goodwill, work stoppage, computer failure or malfunction, or any * 297 | * and all other commercial damages or losses, even if such party * 298 | * shall have been informed of the possibility of such damages. This * 299 | * limitation of liability shall not apply to liability for death or * 300 | * personal injury resulting from such party's negligence to the * 301 | * extent applicable law prohibits such limitation. Some * 302 | * jurisdictions do not allow the exclusion or limitation of * 303 | * incidental or consequential damages, so this exclusion and * 304 | * limitation may not apply to You. * 305 | * * 306 | ************************************************************************ 307 | 308 | 8. Litigation 309 | ------------- 310 | 311 | Any litigation relating to this License may be brought only in the 312 | courts of a jurisdiction where the defendant maintains its principal 313 | place of business and such litigation shall be governed by laws of that 314 | jurisdiction, without reference to its conflict-of-law provisions. 315 | Nothing in this Section shall prevent a party's ability to bring 316 | cross-claims or counter-claims. 317 | 318 | 9. Miscellaneous 319 | ---------------- 320 | 321 | This License represents the complete agreement concerning the subject 322 | matter hereof. If any provision of this License is held to be 323 | unenforceable, such provision shall be reformed only to the extent 324 | necessary to make it enforceable. Any law or regulation which provides 325 | that the language of a contract shall be construed against the drafter 326 | shall not be used to construe this License against a Contributor. 327 | 328 | 10. Versions of the License 329 | --------------------------- 330 | 331 | 10.1. New Versions 332 | 333 | Mozilla Foundation is the license steward. Except as provided in Section 334 | 10.3, no one other than the license steward has the right to modify or 335 | publish new versions of this License. Each version will be given a 336 | distinguishing version number. 337 | 338 | 10.2. Effect of New Versions 339 | 340 | You may distribute the Covered Software under the terms of the version 341 | of the License under which You originally received the Covered Software, 342 | or under the terms of any subsequent version published by the license 343 | steward. 344 | 345 | 10.3. Modified Versions 346 | 347 | If you create software not governed by this License, and you want to 348 | create a new license for such software, you may create and use a 349 | modified version of this License if you rename the license and remove 350 | any references to the name of the license steward (except to note that 351 | such modified license differs from this License). 352 | 353 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 354 | Licenses 355 | 356 | If You choose to distribute Source Code Form that is Incompatible With 357 | Secondary Licenses under the terms of this version of the License, the 358 | notice described in Exhibit B of this License must be attached. 359 | 360 | Exhibit A - Source Code Form License Notice 361 | ------------------------------------------- 362 | 363 | This Source Code Form is subject to the terms of the Mozilla Public 364 | License, v. 2.0. If a copy of the MPL was not distributed with this 365 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 366 | 367 | If it is not possible or desirable to put the notice in a particular 368 | file, then You may include the notice in a location (such as a LICENSE 369 | file in a relevant directory) where a recipient would be likely to look 370 | for such a notice. 371 | 372 | You may add additional accurate notices of copyright ownership. 373 | 374 | Exhibit B - "Incompatible With Secondary Licenses" Notice 375 | --------------------------------------------------------- 376 | 377 | This Source Code Form is "Incompatible With Secondary Licenses", as 378 | defined by the Mozilla Public License, v. 2.0. 379 | 380 | -------------------------------------------------------------------------------- /src/content/icons/togglebutton/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denschub/firefox-tabgroups/b92a0224ac0cb6f0eddc170cca9c08b98894ea63/src/content/icons/togglebutton/icon-16.png -------------------------------------------------------------------------------- /src/content/icons/togglebutton/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denschub/firefox-tabgroups/b92a0224ac0cb6f0eddc170cca9c08b98894ea63/src/content/icons/togglebutton/icon-32.png -------------------------------------------------------------------------------- /src/content/icons/togglebutton/icon-inverted-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denschub/firefox-tabgroups/b92a0224ac0cb6f0eddc170cca9c08b98894ea63/src/content/icons/togglebutton/icon-inverted-16.png -------------------------------------------------------------------------------- /src/content/icons/togglebutton/icon-inverted-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denschub/firefox-tabgroups/b92a0224ac0cb6f0eddc170cca9c08b98894ea63/src/content/icons/togglebutton/icon-inverted-32.png -------------------------------------------------------------------------------- /src/data/action_creators.js: -------------------------------------------------------------------------------- 1 | const ActionCreators = { 2 | setTabgroups: function(tabgroups) { 3 | return { 4 | type: "TABGROUPS_RECEIVE", 5 | tabgroups: tabgroups 6 | }; 7 | }, 8 | 9 | setGroupCloseTimeout: function(timeout) { 10 | return { 11 | type: "GROUP_CLOSE_TIMEOUT_RECIEVE", 12 | closeTimeout: timeout 13 | }; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /src/data/assets/css/groupspanel.css: -------------------------------------------------------------------------------- 1 | ul, li { 2 | display: block; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | li { 8 | list-style-type: none; 9 | cursor: move; 10 | } 11 | 12 | .group-title { 13 | display: block; 14 | overflow: hidden; 15 | padding-right: 16px; 16 | text-overflow: ellipsis; 17 | white-space: nowrap; 18 | } 19 | 20 | .group.active .group-title, .tab.active { 21 | font-weight: bold; 22 | } 23 | 24 | .group:hover .group-title { 25 | padding-right: 15px; 26 | } 27 | 28 | .group-title input { 29 | background: transparent; 30 | border: 1px solid rgba(0, 0, 0, 0.12); 31 | margin: 0; 32 | padding: 1px; 33 | width: calc(100% - 25px); 34 | } 35 | 36 | .group, .tab { 37 | border-radius: 3px; 38 | border: 1px solid transparent; 39 | cursor: pointer; 40 | padding: 5px; 41 | position: relative; 42 | } 43 | 44 | .tab { 45 | padding: 0; 46 | } 47 | 48 | .group:hover, .tab:hover { 49 | background-color: rgba(0, 0, 0, 0.06); 50 | border-color: rgba(0, 0, 0, 0.12); 51 | } 52 | 53 | .group.dragSourceGroup { 54 | border: inherit; 55 | background-color : inherit; 56 | } 57 | 58 | .group.draggingOver, .draggingOver { 59 | border: 1px dashed #ccc; 60 | background-color: rgba(0, 0, 0, 0.04); 61 | } 62 | 63 | .tab-list { 64 | display: none; 65 | margin-top: 5px; 66 | } 67 | 68 | .expanded .tab-list { 69 | display: block; 70 | } 71 | 72 | .tab-title { 73 | display: inline-block; 74 | overflow: hidden; 75 | text-overflow: ellipsis; 76 | white-space: nowrap; 77 | max-width: calc(100% - 32px); 78 | padding: 5px 5px 1px; 79 | margin-bottom: 1px; 80 | } 81 | 82 | .tab-icon { 83 | height: 16px; 84 | width: 16px; 85 | margin-right: 1px; 86 | margin-bottom: 1px; 87 | margin-left: 4px; 88 | } 89 | 90 | .tab-icon:-moz-broken { 91 | background: url("chrome://mozapps/skin/places/defaultFavicon.png"); 92 | background-size: contain; 93 | display: inline-block; 94 | } 95 | 96 | @media (min-resolution: 1.1dppx) { 97 | .tab-icon:-moz-broken { 98 | background-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png"); 99 | } 100 | } 101 | 102 | .expanded .group-title { 103 | border-bottom: 1px solid rgba(0, 0, 0, 0.12); 104 | padding-bottom: 5px; 105 | } 106 | 107 | .group-controls { 108 | display: inline-block; 109 | font-size: 120%; 110 | opacity: 0.7; 111 | position: absolute; 112 | right: 5px; 113 | top: 4px; 114 | } 115 | 116 | .group.editing .group-controls { 117 | top: 6px; 118 | } 119 | 120 | .group.closing .group-controls .group-close, 121 | .group.closing:hover .group-controls .group-close, 122 | .group.closing .group-controls .fa-chevron-down, 123 | .group.closing .group-controls .group-edit { 124 | display: none; 125 | } 126 | 127 | .group.closing .group-title { 128 | color: #ccc; 129 | text-decoration: line-through; 130 | } 131 | 132 | .group:hover .group-title { 133 | padding-right: 55px; 134 | } 135 | 136 | .group .group-controls .group-close, 137 | .group .group-controls .group-edit { 138 | display: none; 139 | } 140 | 141 | .group:hover .group-controls .group-close, 142 | .group:hover .group-controls .group-edit { 143 | display: inline-block; 144 | } 145 | -------------------------------------------------------------------------------- /src/data/components/app.js: -------------------------------------------------------------------------------- 1 | const App = React.createClass({ 2 | propTypes: { 3 | onGroupAddClick: React.PropTypes.func, 4 | onGroupAddDrop: React.PropTypes.func, 5 | onGroupClick: React.PropTypes.func, 6 | onGroupDrop: React.PropTypes.func, 7 | onGroupCloseClick: React.PropTypes.func, 8 | onGroupTitleChange: React.PropTypes.func, 9 | onTabClick: React.PropTypes.func, 10 | onTabDrag: React.PropTypes.func, 11 | onTabDragStart: React.PropTypes.func, 12 | uiHeightChanged: React.PropTypes.func 13 | }, 14 | 15 | render: function() { 16 | return React.createElement(GroupList, this.props); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /src/data/components/group.js: -------------------------------------------------------------------------------- 1 | const Group = React.createClass({ 2 | propTypes: { 3 | closeTimeout: React.PropTypes.number, 4 | group: React.PropTypes.object.isRequired, 5 | onGroupClick: React.PropTypes.func, 6 | onGroupDrop: React.PropTypes.func, 7 | onGroupCloseClick: React.PropTypes.func, 8 | onGroupTitleChange: React.PropTypes.func, 9 | onTabClick: React.PropTypes.func, 10 | onTabDrag: React.PropTypes.func, 11 | onTabDragStart: React.PropTypes.func, 12 | uiHeightChanged: React.PropTypes.func 13 | }, 14 | 15 | getInitialState: function() { 16 | return { 17 | closing: false, 18 | editing: false, 19 | expanded: false, 20 | draggingOverCounter: 0, 21 | dragSourceGroup: false, 22 | newTitle: this.getTitle() 23 | }; 24 | }, 25 | 26 | componentDidUpdate: function() { 27 | this.props.uiHeightChanged && this.props.uiHeightChanged(); 28 | }, 29 | 30 | getTitle: function() { 31 | return this.props.group.title || ( 32 | addon.options.l10n.unnamed_group + " " + this.props.group.id 33 | ); 34 | }, 35 | 36 | render: function() { 37 | let titleElement; 38 | if (this.state.editing) { 39 | titleElement = React.DOM.input( 40 | { 41 | type: "text", 42 | defaultValue: this.getTitle(), 43 | onChange: (event) => { 44 | this.setState({newTitle: event.target.value}); 45 | }, 46 | onClick: (event) => { 47 | event.stopPropagation(); 48 | }, 49 | onKeyUp: this.handleGroupTitleInputKey 50 | } 51 | ); 52 | } else { 53 | titleElement = React.DOM.span({}, this.getTitle()); 54 | } 55 | 56 | let groupClasses = classNames({ 57 | active: this.props.group.active, 58 | editing: this.state.editing, 59 | closing: this.state.closing, 60 | draggingOver: this.state.draggingOverCounter !== 0, 61 | dragSourceGroup: this.state.dragSourceGroup, 62 | expanded: this.state.expanded, 63 | group: true 64 | }); 65 | 66 | return ( 67 | React.DOM.li( 68 | { 69 | className: groupClasses, 70 | onClick: this.handleGroupClick, 71 | onDragOver: this.handleGroupDragOver, 72 | onDragEnter: this.handleGroupDragEnter, 73 | onDragLeave: this.handleGroupDragLeave, 74 | onDrop: this.handleGroupDrop 75 | }, 76 | React.DOM.span( 77 | { 78 | className: "group-title" 79 | }, 80 | titleElement, 81 | React.createElement( 82 | GroupControls, 83 | { 84 | closing: this.state.closing, 85 | editing: this.state.editing, 86 | expanded: this.state.expanded, 87 | onClose: this.handleGroupCloseClick, 88 | onEdit: this.handleGroupEditClick, 89 | onEditAbort: this.handleGroupEditAbortClick, 90 | onEditSave: this.handleGroupEditSaveClick, 91 | onExpand: this.handleGroupExpandClick, 92 | onUndoCloseClick: this.handleGroupCloseAbortClick 93 | } 94 | ) 95 | ), 96 | this.state.expanded && React.createElement( 97 | TabList, 98 | { 99 | tabs: this.props.group.tabs, 100 | onTabClick: this.props.onTabClick, 101 | onTabDrag: this.props.onTabDrag, 102 | onTabDragStart: this.props.onTabDragStart, 103 | onTabDragEnd: this.props.onTabDragEnd 104 | } 105 | ) 106 | ) 107 | ); 108 | }, 109 | 110 | handleGroupCloseClick: function(event) { 111 | event.stopPropagation(); 112 | this.setState({editing: false}); 113 | this.setState({closing: true}); 114 | 115 | let group = this; 116 | 117 | if (this.props.closeTimeout == 0) { 118 | group.props.onGroupCloseClick(group.props.group.id); 119 | return; 120 | } 121 | 122 | setTimeout(function() { 123 | group.props.onGroupCloseClick(group.props.group.id); 124 | }, this.props.closeTimeout * 1000); 125 | }, 126 | 127 | handleGroupClick: function(event) { 128 | event.stopPropagation(); 129 | this.props.onGroupClick(this.props.group.id); 130 | }, 131 | 132 | handleGroupEditClick: function(event) { 133 | event.stopPropagation(); 134 | this.setState({editing: !this.state.editing}); 135 | }, 136 | 137 | handleGroupEditAbortClick: function(event) { 138 | event.stopPropagation(); 139 | this.setState({editing: false}); 140 | }, 141 | 142 | handleGroupEditSaveClick: function(event) { 143 | event.stopPropagation(); 144 | this.setState({editing: false}); 145 | this.props.onGroupTitleChange(this.props.group.id, this.state.newTitle); 146 | }, 147 | 148 | handleGroupExpandClick: function(event) { 149 | event.stopPropagation(); 150 | this.setState({expanded: !this.state.expanded}); 151 | }, 152 | 153 | handleGroupTitleInputKey: function(event) { 154 | if (event.keyCode == 13) { 155 | this.setState({editing: false}); 156 | this.props.onGroupTitleChange(this.props.group.id, this.state.newTitle); 157 | } 158 | }, 159 | 160 | handleGroupDrop: function(event) { 161 | event.stopPropagation(); 162 | 163 | this.setState({draggingOverCounter: 0}); 164 | 165 | let sourceGroup = event.dataTransfer.getData("tab/group"); 166 | let tabIndex = event.dataTransfer.getData("tab/index"); 167 | 168 | this.props.onGroupDrop( 169 | sourceGroup, 170 | tabIndex, 171 | this.props.group.id 172 | ); 173 | }, 174 | 175 | handleGroupDragOver: function(event) { 176 | event.stopPropagation(); 177 | event.preventDefault(); 178 | return false; 179 | }, 180 | 181 | handleGroupDragEnter: function(event) { 182 | event.stopPropagation(); 183 | event.preventDefault(); 184 | 185 | let sourceGroupId = event.dataTransfer.getData("tab/group"); 186 | let isSourceGroup = sourceGroupId == this.props.group.id; 187 | this.setState({dragSourceGroup: isSourceGroup}); 188 | 189 | let draggingCounterValue = (this.state.draggingOverCounter == 1) ? 2 : 1; 190 | this.setState({draggingOverCounter: draggingCounterValue}); 191 | }, 192 | 193 | handleGroupDragLeave: function(event) { 194 | event.stopPropagation(); 195 | event.preventDefault(); 196 | 197 | if (this.state.draggingOverCounter == 2) { 198 | this.setState({draggingOverCounter: 1}); 199 | } else if (this.state.draggingOverCounter == 1) { 200 | this.setState({draggingOverCounter: 0}); 201 | } 202 | 203 | return false; 204 | }, 205 | 206 | handleGroupCloseAbortClick: function(event) { 207 | event.stopPropagation(); 208 | 209 | this.setState({closing: false}); 210 | } 211 | }); 212 | -------------------------------------------------------------------------------- /src/data/components/groupaddbutton.js: -------------------------------------------------------------------------------- 1 | const GroupAddButton = React.createClass({ 2 | propTypes: { 3 | onClick: React.PropTypes.func, 4 | onDrop: React.PropTypes.func 5 | }, 6 | 7 | getInitialState: function() { 8 | return { 9 | draggingOverCounter: 0 10 | }; 11 | }, 12 | 13 | render: function() { 14 | let buttonClasses = classNames({ 15 | draggingOver: this.state.draggingOverCounter !== 0, 16 | group: true 17 | }); 18 | 19 | return ( 20 | React.DOM.li( 21 | { 22 | className: buttonClasses, 23 | onClick: this.handleClick, 24 | onDrop: this.handleDrop, 25 | onDragOver: this.handleGroupDragOver, 26 | onDragEnter: this.handleDragEnter, 27 | onDragLeave: this.handleDragLeave 28 | }, 29 | React.DOM.span( 30 | {className: "group-title"}, 31 | addon.options.l10n.add_group 32 | ) 33 | ) 34 | ); 35 | }, 36 | 37 | handleClick: function(event) { 38 | event.stopPropagation(); 39 | this.props.onClick(); 40 | }, 41 | 42 | handleGroupDragOver: function(event) { 43 | event.stopPropagation(); 44 | event.preventDefault(); 45 | }, 46 | 47 | handleDragEnter: function(event) { 48 | event.stopPropagation(); 49 | event.preventDefault(); 50 | 51 | let draggingCounterValue = (this.state.draggingOverCounter == 1) ? 2 : 1; 52 | this.setState({draggingOverCounter: draggingCounterValue}); 53 | }, 54 | 55 | handleDragLeave: function(event) { 56 | event.stopPropagation(); 57 | event.preventDefault(); 58 | 59 | if (this.state.draggingOverCounter == 2) { 60 | this.setState({draggingOverCounter: 1}); 61 | } else if (this.state.draggingOverCounter == 1) { 62 | this.setState({draggingOverCounter: 0}); 63 | } 64 | }, 65 | 66 | handleDrop: function(event) { 67 | event.stopPropagation(); 68 | 69 | this.setState({draggingOverCounter: 0}); 70 | 71 | let sourceGroup = event.dataTransfer.getData("tab/group"); 72 | let tabIndex = event.dataTransfer.getData("tab/index"); 73 | 74 | this.props.onDrop( 75 | sourceGroup, 76 | tabIndex 77 | ); 78 | } 79 | }); 80 | -------------------------------------------------------------------------------- /src/data/components/groupcontrols.js: -------------------------------------------------------------------------------- 1 | const GroupControls = React.createClass({ 2 | propTypes: { 3 | expanded: React.PropTypes.bool.isRequired, 4 | onClose: React.PropTypes.func, 5 | onEdit: React.PropTypes.func, 6 | onEditAbort: React.PropTypes.func, 7 | onEditSave: React.PropTypes.func, 8 | onExpand: React.PropTypes.func, 9 | onUndoCloseClick: React.PropTypes.func 10 | }, 11 | 12 | getEditControls: function() { 13 | let controls; 14 | if (this.props.editing) { 15 | controls = [ 16 | React.DOM.i({ 17 | className: "group-edit fa fa-fw fa-check", 18 | onClick: this.props.onEditSave 19 | }), 20 | React.DOM.i({ 21 | className: "group-edit fa fa-fw fa-ban", 22 | onClick: this.props.onEditAbort 23 | }) 24 | ]; 25 | } else { 26 | controls = React.DOM.i({ 27 | className: "group-edit fa fa-fw fa-pencil", 28 | onClick: this.props.onEdit 29 | }); 30 | } 31 | 32 | return controls; 33 | }, 34 | 35 | getClosingControls: function() { 36 | return [ 37 | React.DOM.i({ 38 | className: "group-close-undo fa fa-fw fa-undo", 39 | onClick: this.props.onUndoCloseClick 40 | }) 41 | ]; 42 | }, 43 | 44 | render: function() { 45 | let groupControls; 46 | if (this.props.closing) { 47 | groupControls = this.getClosingControls(); 48 | } else { 49 | groupControls = this.getEditControls(); 50 | } 51 | 52 | let expanderClasses = classNames({ 53 | "group-expand": true, 54 | "fa": true, 55 | "fa-fw": true, 56 | "fa-chevron-down": !this.props.expanded, 57 | "fa-chevron-up": this.props.expanded 58 | }); 59 | 60 | return React.DOM.span( 61 | { 62 | className: "group-controls" 63 | }, 64 | groupControls, 65 | React.DOM.i({ 66 | className: "group-close fa fa-fw fa-times", 67 | onClick: this.props.onClose 68 | }), 69 | React.DOM.i({ 70 | className: expanderClasses, 71 | onClick: this.props.onExpand 72 | }) 73 | ); 74 | } 75 | }); 76 | -------------------------------------------------------------------------------- /src/data/components/grouplist.js: -------------------------------------------------------------------------------- 1 | const GroupList = (() => { 2 | const GroupListStandalone = React.createClass({ 3 | propTypes: { 4 | groups: React.PropTypes.object.isRequired, 5 | closeTimeout: React.PropTypes.number, 6 | onGroupAddClick: React.PropTypes.func, 7 | onGroupAddDrop: React.PropTypes.func, 8 | onGroupClick: React.PropTypes.func, 9 | onGroupDrop: React.PropTypes.func, 10 | onGroupCloseClick: React.PropTypes.func, 11 | onGroupTitleChange: React.PropTypes.func, 12 | onTabClick: React.PropTypes.func, 13 | onTabDrag: React.PropTypes.func, 14 | onTabDragStart: React.PropTypes.func, 15 | uiHeightChanged: React.PropTypes.func 16 | }, 17 | 18 | componentDidUpdate: function() { 19 | this.props.uiHeightChanged && this.props.uiHeightChanged(); 20 | }, 21 | 22 | render: function() { 23 | return React.DOM.ul( 24 | {className: "group-list"}, 25 | this.props.groups.map((group) => { 26 | return React.createElement(Group, { 27 | key: group.id, 28 | group: group, 29 | closeTimeout: this.props.closeTimeout, 30 | onGroupClick: this.props.onGroupClick, 31 | onGroupDrop: this.props.onGroupDrop, 32 | onGroupCloseClick: this.props.onGroupCloseClick, 33 | onGroupTitleChange: this.props.onGroupTitleChange, 34 | onTabClick: this.props.onTabClick, 35 | onTabDrag: this.props.onTabDrag, 36 | onTabDragStart: this.props.onTabDragStart, 37 | uiHeightChanged: this.props.uiHeightChanged 38 | }); 39 | }), 40 | React.createElement( 41 | GroupAddButton, 42 | { 43 | onClick: this.props.onGroupAddClick, 44 | onDrop: this.props.onGroupAddDrop 45 | } 46 | ) 47 | ); 48 | } 49 | }); 50 | 51 | return ReactRedux.connect((state) => { 52 | return { 53 | groups: state.get("tabgroups"), 54 | closeTimeout: state.get("closeTimeout") 55 | }; 56 | }, ActionCreators)(GroupListStandalone); 57 | })(); 58 | -------------------------------------------------------------------------------- /src/data/components/tab.js: -------------------------------------------------------------------------------- 1 | const Tab = React.createClass({ 2 | propTypes: { 3 | onTabClick: React.PropTypes.func, 4 | onTabDrag: React.PropTypes.func, 5 | onTabDragStart: React.PropTypes.func, 6 | tab: React.PropTypes.object.isRequired 7 | }, 8 | 9 | render: function() { 10 | let favicon = React.DOM.img({ 11 | alt: "", 12 | className: "tab-icon", 13 | src: this.props.tab.icon 14 | }); 15 | 16 | let tabClasses = classNames({ 17 | active: this.props.tab.active, 18 | tab: true 19 | }); 20 | 21 | return ( 22 | React.DOM.li( 23 | { 24 | className: tabClasses, 25 | onClick: this.handleTabClick, 26 | onDrag: this.handleTabDrag, 27 | onDragStart: this.handleTabDragStart, 28 | draggable: true 29 | }, 30 | favicon, 31 | React.DOM.span({className: "tab-title"}, this.props.tab.title) 32 | ) 33 | ); 34 | }, 35 | 36 | handleTabClick: function(event) { 37 | event.stopPropagation(); 38 | 39 | let tab = this.props.tab; 40 | this.props.onTabClick( 41 | tab.group, 42 | tab.index 43 | ); 44 | }, 45 | 46 | handleTabDrag: function(event) { 47 | event.stopPropagation(); 48 | 49 | let tab = this.props.tab; 50 | event.dataTransfer.setData("tab/index", tab.index); 51 | event.dataTransfer.setData("tab/group", tab.group); 52 | 53 | this.props.onTabDrag( 54 | tab.group, 55 | tab.index 56 | ); 57 | }, 58 | 59 | handleTabDragStart: function(event) { 60 | event.stopPropagation(); 61 | 62 | let tab = this.props.tab; 63 | event.dataTransfer.setData("tab/index", tab.index); 64 | event.dataTransfer.setData("tab/group", tab.group); 65 | 66 | this.props.onTabDragStart( 67 | tab.group, 68 | tab.index 69 | ); 70 | } 71 | }); 72 | -------------------------------------------------------------------------------- /src/data/components/tablist.js: -------------------------------------------------------------------------------- 1 | const TabList = React.createClass({ 2 | propTypes: { 3 | onTabClick: React.PropTypes.func, 4 | onTabDrag: React.PropTypes.func, 5 | onTabDragStart: React.PropTypes.func, 6 | tabs: React.PropTypes.array.isRequired 7 | }, 8 | 9 | render: function() { 10 | return ( 11 | React.DOM.ul( 12 | {className: "tab-list"}, 13 | this.props.tabs.map((tab) => { 14 | return React.createElement(Tab, { 15 | key: tab.index, 16 | tab: tab, 17 | onTabClick: this.props.onTabClick, 18 | onTabDrag: this.props.onTabDrag, 19 | onTabDragStart: this.props.onTabDragStart, 20 | uiHeightChanged: this.props.uiHeightChanged 21 | }); 22 | }) 23 | ) 24 | ); 25 | } 26 | }); 27 | -------------------------------------------------------------------------------- /src/data/groupspanel.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 | -------------------------------------------------------------------------------- /src/data/groupspanel.js: -------------------------------------------------------------------------------- 1 | const store = Redux.createStore(Reducer); 2 | 3 | const Actions = { 4 | addGroup: function() { 5 | addon.port.emit("Group:Add"); 6 | }, 7 | 8 | addGroupWithTab: function(sourceGroupID, tabIndex) { 9 | addon.port.emit("Group:AddWithTab", {sourceGroupID, tabIndex}); 10 | }, 11 | 12 | closeGroup: function(groupID) { 13 | addon.port.emit("Group:Close", {groupID}); 14 | }, 15 | 16 | uiHeightChanged: function() { 17 | addon.port.emit("UI:Resize", { 18 | width: document.body.clientWidth, 19 | height: document.body.clientHeight 20 | }); 21 | }, 22 | 23 | renameGroup: function(groupID, title) { 24 | addon.port.emit("Group:Rename", {groupID, title}); 25 | }, 26 | 27 | selectGroup: function(groupID) { 28 | addon.port.emit("Group:Select", {groupID}); 29 | }, 30 | 31 | moveTabToGroup: function(sourceGroupID, tabIndex, targetGroupID) { 32 | addon.port.emit("Group:Drop", {sourceGroupID, tabIndex, targetGroupID}); 33 | }, 34 | 35 | selectTab: function(groupID, tabIndex) { 36 | addon.port.emit("Tab:Select", {groupID, tabIndex}); 37 | }, 38 | 39 | dragTab: function(groupID, tabIndex) { 40 | addon.port.emit("Tab:Drag", {groupID, tabIndex}); 41 | }, 42 | 43 | dragTabStart: function(groupID, tabIndex) { 44 | addon.port.emit("Tab:DragStart", {groupID, tabIndex}); 45 | } 46 | }; 47 | 48 | document.addEventListener("DOMContentLoaded", () => { 49 | ReactDOM.render( 50 | React.createElement( 51 | ReactRedux.Provider, 52 | {store: store}, 53 | React.createElement(App, { 54 | onGroupAddClick: Actions.addGroup, 55 | onGroupAddDrop: Actions.addGroupWithTab, 56 | onGroupClick: Actions.selectGroup, 57 | onGroupDrop: Actions.moveTabToGroup, 58 | onGroupCloseClick: Actions.closeGroup, 59 | onGroupTitleChange: Actions.renameGroup, 60 | onTabClick: Actions.selectTab, 61 | onTabDrag: Actions.dragTab, 62 | onTabDragStart: Actions.dragTabStart, 63 | uiHeightChanged: Actions.uiHeightChanged 64 | }) 65 | ), 66 | document.getElementById("content") 67 | ); 68 | }); 69 | 70 | addon.port.on("Groups:Changed", (tabgroups) => { 71 | store.dispatch(ActionCreators.setTabgroups(tabgroups)); 72 | }); 73 | 74 | addon.port.on("Groups:CloseTimeoutChanged", (timeout) => { 75 | store.dispatch(ActionCreators.setGroupCloseTimeout(timeout)); 76 | }); 77 | -------------------------------------------------------------------------------- /src/data/reducer.js: -------------------------------------------------------------------------------- 1 | const INITIAL_STATE = Immutable.fromJS({ 2 | tabgroups: [], 3 | closeTimeout: 0 4 | }); 5 | 6 | const Reducer = function(state = INITIAL_STATE, action) { 7 | switch (action.type) { 8 | case "TABGROUPS_RECEIVE": 9 | return state.set("tabgroups", Immutable.fromJS(action.tabgroups)); 10 | case "GROUP_CLOSE_TIMEOUT_RECIEVE": 11 | return state.set("closeTimeout", action.closeTimeout); 12 | } 13 | return state; 14 | }; 15 | -------------------------------------------------------------------------------- /src/data/vendor/css/font-awesome.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | /* FONT PATH 6 | * -------------------------- */ 7 | @font-face { 8 | font-family: 'FontAwesome'; 9 | src: url('../fonts/fontawesome.eot?v=4.4.0'); 10 | src: url('../fonts/fontawesome.eot?#iefix&v=4.4.0') format('embedded-opentype'), url('../fonts/fontawesome.woff?v=4.4.0') format('woff'), url('../fonts/fontawesome.ttf?v=4.4.0') format('truetype'), url('../fonts/fontawesome.svg?v=4.4.0#fontawesomeregular') format('svg'); 11 | font-weight: normal; 12 | font-style: normal; 13 | } 14 | .fa { 15 | display: inline-block; 16 | font: normal normal normal 14px/1 FontAwesome; 17 | font-size: inherit; 18 | text-rendering: auto; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; 21 | } 22 | /* makes the font 33% larger relative to the icon container */ 23 | .fa-lg { 24 | font-size: 1.33333333em; 25 | line-height: 0.75em; 26 | vertical-align: -15%; 27 | } 28 | .fa-2x { 29 | font-size: 2em; 30 | } 31 | .fa-3x { 32 | font-size: 3em; 33 | } 34 | .fa-4x { 35 | font-size: 4em; 36 | } 37 | .fa-5x { 38 | font-size: 5em; 39 | } 40 | .fa-fw { 41 | width: 1.28571429em; 42 | text-align: center; 43 | } 44 | 45 | .fa-check:before { content: "\f00c"; } 46 | .fa-remove:before, .fa-close:before, .fa-times:before { content: "\f00d"; } 47 | .fa-pencil:before { content: "\f040"; } 48 | .fa-ban:before { content: "\f05e"; } 49 | .fa-plus:before { content: "\f067"; } 50 | .fa-minus:before { content: "\f068"; } 51 | .fa-chevron-up:before { content: "\f077"; } 52 | .fa-chevron-down:before { content: "\f078"; } 53 | .fa-undo:before { content: "\f079"; } 54 | -------------------------------------------------------------------------------- /src/data/vendor/fonts/fontawesome.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denschub/firefox-tabgroups/b92a0224ac0cb6f0eddc170cca9c08b98894ea63/src/data/vendor/fonts/fontawesome.eot -------------------------------------------------------------------------------- /src/data/vendor/fonts/fontawesome.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Generated by IcoMoon 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/data/vendor/fonts/fontawesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denschub/firefox-tabgroups/b92a0224ac0cb6f0eddc170cca9c08b98894ea63/src/data/vendor/fonts/fontawesome.ttf -------------------------------------------------------------------------------- /src/data/vendor/fonts/fontawesome.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/denschub/firefox-tabgroups/b92a0224ac0cb6f0eddc170cca9c08b98894ea63/src/data/vendor/fonts/fontawesome.woff -------------------------------------------------------------------------------- /src/data/vendor/js/classnames.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Copyright (c) 2015 Jed Watson. 3 | Licensed under the MIT License (MIT), see 4 | http://jedwatson.github.io/classnames 5 | */ 6 | (function(){var a={}.hasOwnProperty;function b(){var g="";for(var f=0;fi;i++)n[i]=t[i+e];return n}function o(t){return void 0===t.size&&(t.size=t.__iterate(s)),t.size}function u(t,e){if("number"!=typeof e){var r=+e;if(""+r!==e)return NaN;e=r}return 0>e?o(t)+e:e}function s(){return!0}function a(t,e,r){return(0===t||void 0!==r&&-r>=t)&&(void 0===e||void 0!==r&&e>=r)}function h(t,e){return c(t,e,0)}function f(t,e){return c(t,e,e)}function c(t,e,r){return void 0===t?r:0>t?Math.max(0,e+t):void 0===e?t:Math.min(e,t)}function _(t){return y(t)?t:O(t)}function p(t){return d(t)?t:x(t)}function v(t){return m(t)?t:k(t)}function l(t){return y(t)&&!g(t)?t:A(t)}function y(t){return!(!t||!t[vr])}function d(t){return!(!t||!t[lr])}function m(t){return!(!t||!t[yr])}function g(t){return d(t)||m(t)}function w(t){return!(!t||!t[dr])}function S(t){this.next=t}function z(t,e,r,n){var i=0===t?e:1===t?r:[e,r];return n?n.value=i:n={value:i,done:!1},n}function I(){return{value:void 0,done:!0}}function b(t){return!!M(t)}function q(t){return t&&"function"==typeof t.next}function D(t){var e=M(t);return e&&e.call(t)}function M(t){var e=t&&(Sr&&t[Sr]||t[zr]);return"function"==typeof e?e:void 0}function E(t){return t&&"number"==typeof t.length}function O(t){return null===t||void 0===t?T():y(t)?t.toSeq():C(t)}function x(t){return null===t||void 0===t?T().toKeyedSeq():y(t)?d(t)?t.toSeq():t.fromEntrySeq():W(t)}function k(t){return null===t||void 0===t?T():y(t)?d(t)?t.entrySeq():t.toIndexedSeq():B(t)}function A(t){return(null===t||void 0===t?T():y(t)?d(t)?t.entrySeq():t:B(t)).toSetSeq()}function j(t){this._array=t,this.size=t.length}function R(t){var e=Object.keys(t);this._object=t,this._keys=e,this.size=e.length; 10 | }function U(t){this._iterable=t,this.size=t.length||t.size}function K(t){this._iterator=t,this._iteratorCache=[]}function L(t){return!(!t||!t[br])}function T(){return qr||(qr=new j([]))}function W(t){var e=Array.isArray(t)?new j(t).fromEntrySeq():q(t)?new K(t).fromEntrySeq():b(t)?new U(t).fromEntrySeq():"object"==typeof t?new R(t):void 0;if(!e)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+t);return e}function B(t){var e=J(t);if(!e)throw new TypeError("Expected Array or iterable object of values: "+t);return e}function C(t){var e=J(t)||"object"==typeof t&&new R(t);if(!e)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+t);return e}function J(t){return E(t)?new j(t):q(t)?new K(t):b(t)?new U(t):void 0}function N(t,e,r,n){var i=t._cache;if(i){for(var o=i.length-1,u=0;o>=u;u++){var s=i[r?o-u:u];if(e(s[1],n?s[0]:u,t)===!1)return u+1}return u}return t.__iterateUncached(e,r)}function P(t,e,r,n){var i=t._cache;if(i){var o=i.length-1,u=0;return new S(function(){var t=i[r?o-u:u];return u++>o?I():z(e,n?t[0]:u-1,t[1])})}return t.__iteratorUncached(e,r)}function H(){throw TypeError("Abstract")}function V(){}function Y(){}function Q(){}function X(t,e){if(t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1;if("function"==typeof t.valueOf&&"function"==typeof e.valueOf){if(t=t.valueOf(),e=e.valueOf(),t===e||t!==t&&e!==e)return!0;if(!t||!e)return!1}return"function"==typeof t.equals&&"function"==typeof e.equals&&t.equals(e)?!0:!1}function F(t,e){return e?G(e,t,"",{"":t}):Z(t)}function G(t,e,r,n){return Array.isArray(e)?t.call(n,r,k(e).map(function(r,n){return G(t,r,n,e)})):$(e)?t.call(n,r,x(e).map(function(r,n){return G(t,r,n,e)})):e}function Z(t){return Array.isArray(t)?k(t).map(Z).toList():$(t)?x(t).map(Z).toMap():t}function $(t){return t&&(t.constructor===Object||void 0===t.constructor)}function tt(t){return t>>>1&1073741824|3221225471&t}function et(t){if(t===!1||null===t||void 0===t)return 0;if("function"==typeof t.valueOf&&(t=t.valueOf(),t===!1||null===t||void 0===t))return 0; 11 | if(t===!0)return 1;var e=typeof t;if("number"===e){var r=0|t;for(r!==t&&(r^=4294967295*t);t>4294967295;)t/=4294967295,r^=t;return tt(r)}return"string"===e?t.length>jr?rt(t):nt(t):"function"==typeof t.hashCode?t.hashCode():it(t)}function rt(t){var e=Kr[t];return void 0===e&&(e=nt(t),Ur===Rr&&(Ur=0,Kr={}),Ur++,Kr[t]=e),e}function nt(t){for(var e=0,r=0;t.length>r;r++)e=31*e+t.charCodeAt(r)|0;return tt(e)}function it(t){var e;if(xr&&(e=Dr.get(t),void 0!==e))return e;if(e=t[Ar],void 0!==e)return e;if(!Or){if(e=t.propertyIsEnumerable&&t.propertyIsEnumerable[Ar],void 0!==e)return e;if(e=ot(t),void 0!==e)return e}if(e=++kr,1073741824&kr&&(kr=0),xr)Dr.set(t,e);else{if(void 0!==Er&&Er(t)===!1)throw Error("Non-extensible objects are not allowed as keys.");if(Or)Object.defineProperty(t,Ar,{enumerable:!1,configurable:!1,writable:!1,value:e});else if(void 0!==t.propertyIsEnumerable&&t.propertyIsEnumerable===t.constructor.prototype.propertyIsEnumerable)t.propertyIsEnumerable=function(){return this.constructor.prototype.propertyIsEnumerable.apply(this,arguments)},t.propertyIsEnumerable[Ar]=e;else{if(void 0===t.nodeType)throw Error("Unable to set a non-enumerable property on object.");t[Ar]=e}}return e}function ot(t){if(t&&t.nodeType>0)switch(t.nodeType){case 1:return t.uniqueID;case 9:return t.documentElement&&t.documentElement.uniqueID}}function ut(t,e){if(!t)throw Error(e)}function st(t){ut(t!==1/0,"Cannot perform this action with an infinite size.")}function at(t,e){this._iter=t,this._useKeys=e,this.size=t.size}function ht(t){this._iter=t,this.size=t.size}function ft(t){this._iter=t,this.size=t.size}function ct(t){this._iter=t,this.size=t.size}function _t(t){var e=jt(t);return e._iter=t,e.size=t.size,e.flip=function(){return t},e.reverse=function(){var e=t.reverse.apply(this);return e.flip=function(){return t.reverse()},e},e.has=function(e){return t.includes(e)},e.includes=function(e){return t.has(e)},e.cacheResult=Rt,e.__iterateUncached=function(e,r){var n=this;return t.__iterate(function(t,r){return e(r,t,n)!==!1},r)},e.__iteratorUncached=function(e,r){ 12 | if(e===wr){var n=t.__iterator(e,r);return new S(function(){var t=n.next();if(!t.done){var e=t.value[0];t.value[0]=t.value[1],t.value[1]=e}return t})}return t.__iterator(e===gr?mr:gr,r)},e}function pt(t,e,r){var n=jt(t);return n.size=t.size,n.has=function(e){return t.has(e)},n.get=function(n,i){var o=t.get(n,cr);return o===cr?i:e.call(r,o,n,t)},n.__iterateUncached=function(n,i){var o=this;return t.__iterate(function(t,i,u){return n(e.call(r,t,i,u),i,o)!==!1},i)},n.__iteratorUncached=function(n,i){var o=t.__iterator(wr,i);return new S(function(){var i=o.next();if(i.done)return i;var u=i.value,s=u[0];return z(n,s,e.call(r,u[1],s,t),i)})},n}function vt(t,e){var r=jt(t);return r._iter=t,r.size=t.size,r.reverse=function(){return t},t.flip&&(r.flip=function(){var e=_t(t);return e.reverse=function(){return t.flip()},e}),r.get=function(r,n){return t.get(e?r:-1-r,n)},r.has=function(r){return t.has(e?r:-1-r)},r.includes=function(e){return t.includes(e)},r.cacheResult=Rt,r.__iterate=function(e,r){var n=this;return t.__iterate(function(t,r){return e(t,r,n)},!r)},r.__iterator=function(e,r){return t.__iterator(e,!r)},r}function lt(t,e,r,n){var i=jt(t);return n&&(i.has=function(n){var i=t.get(n,cr);return i!==cr&&!!e.call(r,i,n,t)},i.get=function(n,i){var o=t.get(n,cr);return o!==cr&&e.call(r,o,n,t)?o:i}),i.__iterateUncached=function(i,o){var u=this,s=0;return t.__iterate(function(t,o,a){return e.call(r,t,o,a)?(s++,i(t,n?o:s-1,u)):void 0},o),s},i.__iteratorUncached=function(i,o){var u=t.__iterator(wr,o),s=0;return new S(function(){for(;;){var o=u.next();if(o.done)return o;var a=o.value,h=a[0],f=a[1];if(e.call(r,f,h,t))return z(i,n?h:s++,f,o)}})},i}function yt(t,e,r){var n=Lt().asMutable();return t.__iterate(function(i,o){n.update(e.call(r,i,o,t),0,function(t){return t+1})}),n.asImmutable()}function dt(t,e,r){var n=d(t),i=(w(t)?Ie():Lt()).asMutable();t.__iterate(function(o,u){i.update(e.call(r,o,u,t),function(t){return t=t||[],t.push(n?[u,o]:o),t})});var o=At(t);return i.map(function(e){return Ot(t,o(e))})}function mt(t,e,r,n){var i=t.size; 13 | if(void 0!==e&&(e=0|e),void 0!==r&&(r=0|r),a(e,r,i))return t;var o=h(e,i),s=f(r,i);if(o!==o||s!==s)return mt(t.toSeq().cacheResult(),e,r,n);var c,_=s-o;_===_&&(c=0>_?0:_);var p=jt(t);return p.size=0===c?c:t.size&&c||void 0,!n&&L(t)&&c>=0&&(p.get=function(e,r){return e=u(this,e),e>=0&&c>e?t.get(e+o,r):r}),p.__iterateUncached=function(e,r){var i=this;if(0===c)return 0;if(r)return this.cacheResult().__iterate(e,r);var u=0,s=!0,a=0;return t.__iterate(function(t,r){return s&&(s=u++c)return I();var t=i.next();return n||e===gr?t:e===mr?z(e,s-1,void 0,t):z(e,s-1,t.value[1],t)})},p}function gt(t,e,r){var n=jt(t);return n.__iterateUncached=function(n,i){var o=this;if(i)return this.cacheResult().__iterate(n,i);var u=0;return t.__iterate(function(t,i,s){return e.call(r,t,i,s)&&++u&&n(t,i,o)}),u},n.__iteratorUncached=function(n,i){var o=this;if(i)return this.cacheResult().__iterator(n,i);var u=t.__iterator(wr,i),s=!0;return new S(function(){if(!s)return I();var t=u.next();if(t.done)return t;var i=t.value,a=i[0],h=i[1];return e.call(r,h,a,o)?n===wr?t:z(n,a,h,t):(s=!1,I())})},n}function wt(t,e,r,n){var i=jt(t);return i.__iterateUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterate(i,o);var s=!0,a=0;return t.__iterate(function(t,o,h){return s&&(s=e.call(r,t,o,h))?void 0:(a++,i(t,n?o:a-1,u))}),a},i.__iteratorUncached=function(i,o){var u=this;if(o)return this.cacheResult().__iterator(i,o);var s=t.__iterator(wr,o),a=!0,h=0;return new S(function(){var t,o,f;do{if(t=s.next(),t.done)return n||i===gr?t:i===mr?z(i,h++,void 0,t):z(i,h++,t.value[1],t);var c=t.value;o=c[0],f=c[1],a&&(a=e.call(r,f,o,u))}while(a);return i===wr?t:z(i,o,f,t)})},i}function St(t,e){var r=d(t),n=[t].concat(e).map(function(t){return y(t)?r&&(t=p(t)):t=r?W(t):B(Array.isArray(t)?t:[t]),t}).filter(function(t){return 0!==t.size; 14 | });if(0===n.length)return t;if(1===n.length){var i=n[0];if(i===t||r&&d(i)||m(t)&&m(i))return i}var o=new j(n);return r?o=o.toKeyedSeq():m(t)||(o=o.toSetSeq()),o=o.flatten(!0),o.size=n.reduce(function(t,e){if(void 0!==t){var r=e.size;if(void 0!==r)return t+r}},0),o}function zt(t,e,r){var n=jt(t);return n.__iterateUncached=function(n,i){function o(t,a){var h=this;t.__iterate(function(t,i){return(!e||e>a)&&y(t)?o(t,a+1):n(t,r?i:u++,h)===!1&&(s=!0),!s},i)}var u=0,s=!1;return o(t,0),u},n.__iteratorUncached=function(n,i){var o=t.__iterator(n,i),u=[],s=0;return new S(function(){for(;o;){var t=o.next();if(t.done===!1){var a=t.value;if(n===wr&&(a=a[1]),e&&!(e>u.length)||!y(a))return r?t:z(n,s++,a,t);u.push(o),o=a.__iterator(n,i)}else o=u.pop()}return I()})},n}function It(t,e,r){var n=At(t);return t.toSeq().map(function(i,o){return n(e.call(r,i,o,t))}).flatten(!0)}function bt(t,e){var r=jt(t);return r.size=t.size&&2*t.size-1,r.__iterateUncached=function(r,n){var i=this,o=0;return t.__iterate(function(t){return(!o||r(e,o++,i)!==!1)&&r(t,o++,i)!==!1},n),o},r.__iteratorUncached=function(r,n){var i,o=t.__iterator(gr,n),u=0;return new S(function(){return(!i||u%2)&&(i=o.next(),i.done)?i:u%2?z(r,u++,e):z(r,u++,i.value,i)})},r}function qt(t,e,r){e||(e=Ut);var n=d(t),i=0,o=t.toSeq().map(function(e,n){return[n,e,i++,r?r(e,n,t):e]}).toArray();return o.sort(function(t,r){return e(t[3],r[3])||t[2]-r[2]}).forEach(n?function(t,e){o[e].length=2}:function(t,e){o[e]=t[1]}),n?x(o):m(t)?k(o):A(o)}function Dt(t,e,r){if(e||(e=Ut),r){var n=t.toSeq().map(function(e,n){return[e,r(e,n,t)]}).reduce(function(t,r){return Mt(e,t[1],r[1])?r:t});return n&&n[0]}return t.reduce(function(t,r){return Mt(e,t,r)?r:t})}function Mt(t,e,r){var n=t(r,e);return 0===n&&r!==e&&(void 0===r||null===r||r!==r)||n>0}function Et(t,e,r){var n=jt(t);return n.size=new j(r).map(function(t){return t.size}).min(),n.__iterate=function(t,e){for(var r,n=this.__iterator(gr,e),i=0;!(r=n.next()).done&&t(r.value,i++,this)!==!1;);return i},n.__iteratorUncached=function(t,n){var i=r.map(function(t){ 15 | return t=_(t),D(n?t.reverse():t)}),o=0,u=!1;return new S(function(){var r;return u||(r=i.map(function(t){return t.next()}),u=r.some(function(t){return t.done})),u?I():z(t,o++,e.apply(null,r.map(function(t){return t.value})))})},n}function Ot(t,e){return L(t)?e:t.constructor(e)}function xt(t){if(t!==Object(t))throw new TypeError("Expected [K, V] tuple: "+t)}function kt(t){return st(t.size),o(t)}function At(t){return d(t)?p:m(t)?v:l}function jt(t){return Object.create((d(t)?x:m(t)?k:A).prototype)}function Rt(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):O.prototype.cacheResult.call(this)}function Ut(t,e){return t>e?1:e>t?-1:0}function Kt(t){var e=D(t);if(!e){if(!E(t))throw new TypeError("Expected iterable or array-like: "+t);e=D(_(t))}return e}function Lt(t){return null===t||void 0===t?Qt():Tt(t)&&!w(t)?t:Qt().withMutations(function(e){var r=p(t);st(r.size),r.forEach(function(t,r){return e.set(r,t)})})}function Tt(t){return!(!t||!t[Lr])}function Wt(t,e){this.ownerID=t,this.entries=e}function Bt(t,e,r){this.ownerID=t,this.bitmap=e,this.nodes=r}function Ct(t,e,r){this.ownerID=t,this.count=e,this.nodes=r}function Jt(t,e,r){this.ownerID=t,this.keyHash=e,this.entries=r}function Nt(t,e,r){this.ownerID=t,this.keyHash=e,this.entry=r}function Pt(t,e,r){this._type=e,this._reverse=r,this._stack=t._root&&Vt(t._root)}function Ht(t,e){return z(t,e[0],e[1])}function Vt(t,e){return{node:t,index:0,__prev:e}}function Yt(t,e,r,n){var i=Object.create(Tr);return i.size=t,i._root=e,i.__ownerID=r,i.__hash=n,i.__altered=!1,i}function Qt(){return Wr||(Wr=Yt(0))}function Xt(t,r,n){var i,o;if(t._root){var u=e(_r),s=e(pr);if(i=Ft(t._root,t.__ownerID,0,void 0,r,n,u,s),!s.value)return t;o=t.size+(u.value?n===cr?-1:1:0)}else{if(n===cr)return t;o=1,i=new Wt(t.__ownerID,[[r,n]])}return t.__ownerID?(t.size=o,t._root=i,t.__hash=void 0,t.__altered=!0,t):i?Yt(o,i):Qt()}function Ft(t,e,n,i,o,u,s,a){return t?t.update(e,n,i,o,u,s,a):u===cr?t:(r(a),r(s),new Nt(e,i,[o,u]))}function Gt(t){return t.constructor===Nt||t.constructor===Jt; 16 | }function Zt(t,e,r,n,i){if(t.keyHash===n)return new Jt(e,n,[t.entry,i]);var o,u=(0===r?t.keyHash:t.keyHash>>>r)&fr,s=(0===r?n:n>>>r)&fr,a=u===s?[Zt(t,e,r+ar,n,i)]:(o=new Nt(e,n,i),s>u?[t,o]:[o,t]);return new Bt(e,1<u;u++){var s=e[u];o=o.update(t,0,void 0,s[0],s[1])}return o}function te(t,e,r,n){for(var i=0,o=0,u=Array(r),s=0,a=1,h=e.length;h>s;s++,a<<=1){var f=e[s];void 0!==f&&s!==n&&(i|=a,u[o++]=f)}return new Bt(t,i,u)}function ee(t,e,r,n,i){for(var o=0,u=Array(hr),s=0;0!==r;s++,r>>>=1)u[s]=1&r?e[o++]:void 0;return u[n]=i,new Ct(t,o+1,u)}function re(t,e,r){for(var n=[],i=0;r.length>i;i++){var o=r[i],u=p(o);y(o)||(u=u.map(function(t){return F(t)})),n.push(u)}return ie(t,e,n)}function ne(t){return function(e,r,n){return e&&e.mergeDeepWith&&y(r)?e.mergeDeepWith(t,r):t?t(e,r,n):r}}function ie(t,e,r){return r=r.filter(function(t){return 0!==t.size}),0===r.length?t:0!==t.size||t.__ownerID||1!==r.length?t.withMutations(function(t){for(var n=e?function(r,n){t.update(n,cr,function(t){return t===cr?r:e(t,r,n)})}:function(e,r){t.set(r,e)},i=0;r.length>i;i++)r[i].forEach(n)}):t.constructor(r[0])}function oe(t,e,r,n){var i=t===cr,o=e.next();if(o.done){var u=i?r:t,s=n(u);return s===u?t:s}ut(i||t&&t.set,"invalid keyPath");var a=o.value,h=i?cr:t.get(a,cr),f=oe(h,e,r,n);return f===h?t:f===cr?t.remove(a):(i?Qt():t).set(a,f)}function ue(t){return t-=t>>1&1431655765,t=(858993459&t)+(t>>2&858993459),t=t+(t>>4)&252645135,t+=t>>8,t+=t>>16,127&t}function se(t,e,r,n){var o=n?t:i(t);return o[e]=r,o}function ae(t,e,r,n){var i=t.length+1;if(n&&e+1===i)return t[e]=r,t;for(var o=Array(i),u=0,s=0;i>s;s++)s===e?(o[s]=r,u=-1):o[s]=t[s+u];return o}function he(t,e,r){var n=t.length-1;if(r&&e===n)return t.pop(),t;for(var i=Array(n),o=0,u=0;n>u;u++)u===e&&(o=1),i[u]=t[u+o];return i}function fe(t){var e=le();if(null===t||void 0===t)return e;if(ce(t))return t;var r=v(t),n=r.size;return 0===n?e:(st(n),n>0&&hr>n?ve(0,n,ar,null,new _e(r.toArray())):e.withMutations(function(t){ 17 | t.setSize(n),r.forEach(function(e,r){return t.set(r,e)})}))}function ce(t){return!(!t||!t[Nr])}function _e(t,e){this.array=t,this.ownerID=e}function pe(t,e){function r(t,e,r){return 0===e?n(t,r):i(t,e,r)}function n(t,r){var n=r===s?a&&a.array:t&&t.array,i=r>o?0:o-r,h=u-r;return h>hr&&(h=hr),function(){if(i===h)return Vr;var t=e?--h:i++;return n&&n[t]}}function i(t,n,i){var s,a=t&&t.array,h=i>o?0:o-i>>n,f=(u-i>>n)+1;return f>hr&&(f=hr),function(){for(;;){if(s){var t=s();if(t!==Vr)return t;s=null}if(h===f)return Vr;var o=e?--f:h++;s=r(a&&a[o],n-ar,i+(o<=t.size||0>r)return t.withMutations(function(t){0>r?we(t,r).set(0,n):we(t,0,r+1).set(r,n)});r+=t._origin;var i=t._tail,o=t._root,s=e(pr);return r>=ze(t._capacity)?i=de(i,t.__ownerID,0,r,n,s):o=de(o,t.__ownerID,t._level,r,n,s),s.value?t.__ownerID?(t._root=o,t._tail=i,t.__hash=void 0,t.__altered=!0,t):ve(t._origin,t._capacity,t._level,o,i):t}function de(t,e,n,i,o,u){var s=i>>>n&fr,a=t&&t.array.length>s;if(!a&&void 0===o)return t;var h;if(n>0){var f=t&&t.array[s],c=de(f,e,n-ar,i,o,u);return c===f?t:(h=me(t,e),h.array[s]=c,h)}return a&&t.array[s]===o?t:(r(u),h=me(t,e),void 0===o&&s===h.array.length-1?h.array.pop():h.array[s]=o,h)}function me(t,e){return e&&t&&e===t.ownerID?t:new _e(t?t.array.slice():[],e)}function ge(t,e){if(e>=ze(t._capacity))return t._tail;if(1<e){for(var r=t._root,n=t._level;r&&n>0;)r=r.array[e>>>n&fr],n-=ar;return r}}function we(t,e,r){void 0!==e&&(e=0|e),void 0!==r&&(r=0|r);var i=t.__ownerID||new n,o=t._origin,u=t._capacity,s=o+e,a=void 0===r?u:0>r?u+r:o+r;if(s===o&&a===u)return t;if(s>=a)return t.clear();for(var h=t._level,f=t._root,c=0;0>s+c;)f=new _e(f&&f.array.length?[void 0,f]:[],i),h+=ar,c+=1<=1<p?ge(t,a-1):p>_?new _e([],i):v;if(v&&p>_&&u>s&&v.array.length){f=me(f,i);for(var y=f,d=h;d>ar;d-=ar){var m=_>>>d&fr;y=y.array[m]=me(y.array[m],i)}y.array[_>>>ar&fr]=v}if(u>a&&(l=l&&l.removeAfter(i,0,a)),s>=p)s-=p,a-=p,h=ar,f=null,l=l&&l.removeBefore(i,0,s);else if(s>o||_>p){for(c=0;f;){var g=s>>>h&fr;if(g!==p>>>h&fr)break;g&&(c+=(1<o&&(f=f.removeBefore(i,h,s-c)),f&&_>p&&(f=f.removeAfter(i,h,p-c)),c&&(s-=c,a-=c)}return t.__ownerID?(t.size=a-s,t._origin=s,t._capacity=a,t._level=h,t._root=f,t._tail=l,t.__hash=void 0,t.__altered=!0,t):ve(s,a,h,f,l)}function Se(t,e,r){for(var n=[],i=0,o=0;r.length>o;o++){var u=r[o],s=v(u);s.size>i&&(i=s.size),y(u)||(s=s.map(function(t){return F(t)})),n.push(s)}return i>t.size&&(t=t.setSize(i)),ie(t,e,n)}function ze(t){return hr>t?0:t-1>>>ar<=hr&&u.size>=2*o.size?(i=u.filter(function(t,e){return void 0!==t&&s!==e}),n=i.toKeyedSeq().map(function(t){return t[0]}).flip().toMap(),t.__ownerID&&(n.__ownerID=i.__ownerID=t.__ownerID)):(n=o.remove(e),i=s===u.size-1?u.pop():u.set(s,void 0))}else if(a){if(r===u.get(s)[1])return t;n=o,i=u.set(s,[e,r])}else n=o.set(e,u.size),i=u.set(u.size,[e,r]);return t.__ownerID?(t.size=n.size,t._map=n,t._list=i,t.__hash=void 0,t):qe(n,i)}function Ee(t){return null===t||void 0===t?ke():Oe(t)?t:ke().unshiftAll(t)}function Oe(t){return!(!t||!t[Qr])}function xe(t,e,r,n){var i=Object.create(Xr);return i.size=t,i._head=e,i.__ownerID=r,i.__hash=n,i.__altered=!1, 19 | i}function ke(){return Fr||(Fr=xe(0))}function Ae(t){return null===t||void 0===t?Ke():je(t)&&!w(t)?t:Ke().withMutations(function(e){var r=l(t);st(r.size),r.forEach(function(t){return e.add(t)})})}function je(t){return!(!t||!t[Gr])}function Re(t,e){return t.__ownerID?(t.size=e.size,t._map=e,t):e===t._map?t:0===e.size?t.__empty():t.__make(e)}function Ue(t,e){var r=Object.create(Zr);return r.size=t?t.size:0,r._map=t,r.__ownerID=e,r}function Ke(){return $r||($r=Ue(Qt()))}function Le(t){return null===t||void 0===t?Be():Te(t)?t:Be().withMutations(function(e){var r=l(t);st(r.size),r.forEach(function(t){return e.add(t)})})}function Te(t){return je(t)&&w(t)}function We(t,e){var r=Object.create(tn);return r.size=t?t.size:0,r._map=t,r.__ownerID=e,r}function Be(){return en||(en=We(De()))}function Ce(t,e){var r,n=function(o){if(o instanceof n)return o;if(!(this instanceof n))return new n(o);if(!r){r=!0;var u=Object.keys(t);Pe(i,u),i.size=u.length,i._name=e,i._keys=u,i._defaultValues=t}this._map=Lt(o)},i=n.prototype=Object.create(rn);return i.constructor=n,n}function Je(t,e,r){var n=Object.create(Object.getPrototypeOf(t));return n._map=e,n.__ownerID=r,n}function Ne(t){return t._name||t.constructor.name||"Record"}function Pe(t,e){try{e.forEach(He.bind(void 0,t))}catch(r){}}function He(t,e){Object.defineProperty(t,e,{get:function(){return this.get(e)},set:function(t){ut(this.__ownerID,"Cannot set on an immutable record."),this.set(e,t)}})}function Ve(t,e){if(t===e)return!0;if(!y(e)||void 0!==t.size&&void 0!==e.size&&t.size!==e.size||void 0!==t.__hash&&void 0!==e.__hash&&t.__hash!==e.__hash||d(t)!==d(e)||m(t)!==m(e)||w(t)!==w(e))return!1;if(0===t.size&&0===e.size)return!0;var r=!g(t);if(w(t)){var n=t.entries();return e.every(function(t,e){var i=n.next().value;return i&&X(i[1],t)&&(r||X(i[0],e))})&&n.next().done}var i=!1;if(void 0===t.size)if(void 0===e.size)"function"==typeof t.cacheResult&&t.cacheResult();else{i=!0;var o=t;t=e,e=o}var u=!0,s=e.__iterate(function(e,n){return(r?t.has(e):i?X(e,t.get(n,cr)):X(t.get(n,cr),e))?void 0:(u=!1, 20 | !1)});return u&&t.size===s}function Ye(t,e,r){if(!(this instanceof Ye))return new Ye(t,e,r);if(ut(0!==r,"Cannot step a Range by 0"),t=t||0,void 0===e&&(e=1/0),r=void 0===r?1:Math.abs(r),t>e&&(r=-r),this._start=t,this._end=e,this._step=r,this.size=Math.max(0,Math.ceil((e-t)/r-1)+1),0===this.size){if(nn)return nn;nn=this}}function Qe(t,e){if(!(this instanceof Qe))return new Qe(t,e);if(this._value=t,this.size=void 0===e?1/0:Math.max(0,e),0===this.size){if(on)return on;on=this}}function Xe(t,e){var r=function(r){t.prototype[r]=e[r]};return Object.keys(e).forEach(r),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(e).forEach(r),t}function Fe(t,e){return e}function Ge(t,e){return[e,t]}function Ze(t){return function(){return!t.apply(this,arguments)}}function $e(t){return function(){return-t.apply(this,arguments)}}function tr(t){return"string"==typeof t?JSON.stringify(t):t}function er(){return i(arguments)}function rr(t,e){return e>t?1:t>e?-1:0}function nr(t){if(t.size===1/0)return 0;var e=w(t),r=d(t),n=e?1:0,i=t.__iterate(r?e?function(t,e){n=31*n+or(et(t),et(e))|0}:function(t,e){n=n+or(et(t),et(e))|0}:e?function(t){n=31*n+et(t)|0}:function(t){n=n+et(t)|0});return ir(i,n)}function ir(t,e){return e=Mr(e,3432918353),e=Mr(e<<15|e>>>-15,461845907),e=Mr(e<<13|e>>>-13,5),e=(e+3864292196|0)^t,e=Mr(e^e>>>16,2246822507),e=Mr(e^e>>>13,3266489909),e=tt(e^e>>>16)}function or(t,e){return t^e+2654435769+(t<<6)+(t>>2)|0}var ur=Array.prototype.slice,sr="delete",ar=5,hr=1<=i;i++)if(t(r[e?n-i:i],i,this)===!1)return i+1;return i},j.prototype.__iterator=function(t,e){var r=this._array,n=r.length-1,i=0;return new S(function(){return i>n?I():z(t,i,r[e?n-i++:i++])})},t(R,x),R.prototype.get=function(t,e){return void 0===e||this.has(t)?this._object[t]:e},R.prototype.has=function(t){return this._object.hasOwnProperty(t)},R.prototype.__iterate=function(t,e){for(var r=this._object,n=this._keys,i=n.length-1,o=0;i>=o;o++){var u=n[e?i-o:o];if(t(r[u],u,this)===!1)return o+1}return o},R.prototype.__iterator=function(t,e){var r=this._object,n=this._keys,i=n.length-1,o=0;return new S(function(){var u=n[e?i-o:o];return o++>i?I():z(t,u,r[u])})},R.prototype[dr]=!0,t(U,k),U.prototype.__iterateUncached=function(t,e){if(e)return this.cacheResult().__iterate(t,e);var r=this._iterable,n=D(r),i=0;if(q(n))for(var o;!(o=n.next()).done&&t(o.value,i++,this)!==!1;);return i},U.prototype.__iteratorUncached=function(t,e){if(e)return this.cacheResult().__iterator(t,e); 22 | var r=this._iterable,n=D(r);if(!q(n))return new S(I);var i=0;return new S(function(){var e=n.next();return e.done?e:z(t,i++,e.value)})},t(K,k),K.prototype.__iterateUncached=function(t,e){if(e)return this.cacheResult().__iterate(t,e);for(var r=this._iterator,n=this._iteratorCache,i=0;n.length>i;)if(t(n[i],i++,this)===!1)return i;for(var o;!(o=r.next()).done;){var u=o.value;if(n[i]=u,t(u,i++,this)===!1)break}return i},K.prototype.__iteratorUncached=function(t,e){if(e)return this.cacheResult().__iterator(t,e);var r=this._iterator,n=this._iteratorCache,i=0;return new S(function(){if(i>=n.length){var e=r.next();if(e.done)return e;n[i]=e.value}return z(t,i,n[i++])})};var qr;t(H,_),t(V,H),t(Y,H),t(Q,H),H.Keyed=V,H.Indexed=Y,H.Set=Q;var Dr,Mr="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function(t,e){t=0|t,e=0|e;var r=65535&t,n=65535&e;return r*n+((t>>>16)*n+r*(e>>>16)<<16>>>0)|0},Er=Object.isExtensible,Or=function(){try{return Object.defineProperty({},"@",{}),!0}catch(t){return!1}}(),xr="function"==typeof WeakMap;xr&&(Dr=new WeakMap);var kr=0,Ar="__immutablehash__";"function"==typeof Symbol&&(Ar=Symbol(Ar));var jr=16,Rr=255,Ur=0,Kr={};t(at,x),at.prototype.get=function(t,e){return this._iter.get(t,e)},at.prototype.has=function(t){return this._iter.has(t)},at.prototype.valueSeq=function(){return this._iter.valueSeq()},at.prototype.reverse=function(){var t=this,e=vt(this,!0);return this._useKeys||(e.valueSeq=function(){return t._iter.toSeq().reverse()}),e},at.prototype.map=function(t,e){var r=this,n=pt(this,t,e);return this._useKeys||(n.valueSeq=function(){return r._iter.toSeq().map(t,e)}),n},at.prototype.__iterate=function(t,e){var r,n=this;return this._iter.__iterate(this._useKeys?function(e,r){return t(e,r,n)}:(r=e?kt(this):0,function(i){return t(i,e?--r:r++,n)}),e)},at.prototype.__iterator=function(t,e){if(this._useKeys)return this._iter.__iterator(t,e);var r=this._iter.__iterator(gr,e),n=e?kt(this):0;return new S(function(){var i=r.next();return i.done?i:z(t,e?--n:n++,i.value,i)})},at.prototype[dr]=!0, 23 | t(ht,k),ht.prototype.includes=function(t){return this._iter.includes(t)},ht.prototype.__iterate=function(t,e){var r=this,n=0;return this._iter.__iterate(function(e){return t(e,n++,r)},e)},ht.prototype.__iterator=function(t,e){var r=this._iter.__iterator(gr,e),n=0;return new S(function(){var e=r.next();return e.done?e:z(t,n++,e.value,e)})},t(ft,A),ft.prototype.has=function(t){return this._iter.includes(t)},ft.prototype.__iterate=function(t,e){var r=this;return this._iter.__iterate(function(e){return t(e,e,r)},e)},ft.prototype.__iterator=function(t,e){var r=this._iter.__iterator(gr,e);return new S(function(){var e=r.next();return e.done?e:z(t,e.value,e.value,e)})},t(ct,x),ct.prototype.entrySeq=function(){return this._iter.toSeq()},ct.prototype.__iterate=function(t,e){var r=this;return this._iter.__iterate(function(e){if(e){xt(e);var n=y(e);return t(n?e.get(1):e[1],n?e.get(0):e[0],r)}},e)},ct.prototype.__iterator=function(t,e){var r=this._iter.__iterator(gr,e);return new S(function(){for(;;){var e=r.next();if(e.done)return e;var n=e.value;if(n){xt(n);var i=y(n);return z(t,i?n.get(0):n[0],i?n.get(1):n[1],e)}}})},ht.prototype.cacheResult=at.prototype.cacheResult=ft.prototype.cacheResult=ct.prototype.cacheResult=Rt,t(Lt,V),Lt.prototype.toString=function(){return this.__toString("Map {","}")},Lt.prototype.get=function(t,e){return this._root?this._root.get(0,void 0,t,e):e},Lt.prototype.set=function(t,e){return Xt(this,t,e)},Lt.prototype.setIn=function(t,e){return this.updateIn(t,cr,function(){return e})},Lt.prototype.remove=function(t){return Xt(this,t,cr)},Lt.prototype.deleteIn=function(t){return this.updateIn(t,function(){return cr})},Lt.prototype.update=function(t,e,r){return 1===arguments.length?t(this):this.updateIn([t],e,r)},Lt.prototype.updateIn=function(t,e,r){r||(r=e,e=void 0);var n=oe(this,Kt(t),e,r);return n===cr?void 0:n},Lt.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):Qt()},Lt.prototype.merge=function(){return re(this,void 0,arguments); 24 | },Lt.prototype.mergeWith=function(t){var e=ur.call(arguments,1);return re(this,t,e)},Lt.prototype.mergeIn=function(t){var e=ur.call(arguments,1);return this.updateIn(t,Qt(),function(t){return"function"==typeof t.merge?t.merge.apply(t,e):e[e.length-1]})},Lt.prototype.mergeDeep=function(){return re(this,ne(void 0),arguments)},Lt.prototype.mergeDeepWith=function(t){var e=ur.call(arguments,1);return re(this,ne(t),e)},Lt.prototype.mergeDeepIn=function(t){var e=ur.call(arguments,1);return this.updateIn(t,Qt(),function(t){return"function"==typeof t.mergeDeep?t.mergeDeep.apply(t,e):e[e.length-1]})},Lt.prototype.sort=function(t){return Ie(qt(this,t))},Lt.prototype.sortBy=function(t,e){return Ie(qt(this,e,t))},Lt.prototype.withMutations=function(t){var e=this.asMutable();return t(e),e.wasAltered()?e.__ensureOwner(this.__ownerID):this},Lt.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new n)},Lt.prototype.asImmutable=function(){return this.__ensureOwner()},Lt.prototype.wasAltered=function(){return this.__altered},Lt.prototype.__iterator=function(t,e){return new Pt(this,t,e)},Lt.prototype.__iterate=function(t,e){var r=this,n=0;return this._root&&this._root.iterate(function(e){return n++,t(e[1],e[0],r)},e),n},Lt.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?Yt(this.size,this._root,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},Lt.isMap=Tt;var Lr="@@__IMMUTABLE_MAP__@@",Tr=Lt.prototype;Tr[Lr]=!0,Tr[sr]=Tr.remove,Tr.removeIn=Tr.deleteIn,Wt.prototype.get=function(t,e,r,n){for(var i=this.entries,o=0,u=i.length;u>o;o++)if(X(r,i[o][0]))return i[o][1];return n},Wt.prototype.update=function(t,e,n,o,u,s,a){for(var h=u===cr,f=this.entries,c=0,_=f.length;_>c&&!X(o,f[c][0]);c++);var p=_>c;if(p?f[c][1]===u:h)return this;if(r(a),(h||!p)&&r(s),!h||1!==f.length){if(!p&&!h&&f.length>=Br)return $t(t,f,o,u);var v=t&&t===this.ownerID,l=v?f:i(f);return p?h?c===_-1?l.pop():l[c]=l.pop():l[c]=[o,u]:l.push([o,u]),v?(this.entries=l,this):new Wt(t,l)}},Bt.prototype.get=function(t,e,r,n){void 0===e&&(e=et(r)); 25 | var i=1<<((0===t?e:e>>>t)&fr),o=this.bitmap;return 0===(o&i)?n:this.nodes[ue(o&i-1)].get(t+ar,e,r,n)},Bt.prototype.update=function(t,e,r,n,i,o,u){void 0===r&&(r=et(n));var s=(0===e?r:r>>>e)&fr,a=1<=Cr)return ee(t,_,h,s,v);if(f&&!v&&2===_.length&&Gt(_[1^c]))return _[1^c];if(f&&v&&1===_.length&&Gt(v))return v;var l=t&&t===this.ownerID,y=f?v?h:h^a:h|a,d=f?v?se(_,c,v,l):he(_,c,l):ae(_,c,v,l);return l?(this.bitmap=y,this.nodes=d,this):new Bt(t,y,d)},Ct.prototype.get=function(t,e,r,n){void 0===e&&(e=et(r));var i=(0===t?e:e>>>t)&fr,o=this.nodes[i];return o?o.get(t+ar,e,r,n):n},Ct.prototype.update=function(t,e,r,n,i,o,u){void 0===r&&(r=et(n));var s=(0===e?r:r>>>e)&fr,a=i===cr,h=this.nodes,f=h[s];if(a&&!f)return this;var c=Ft(f,t,e+ar,r,n,i,o,u);if(c===f)return this;var _=this.count;if(f){if(!c&&(_--,Jr>_))return te(t,h,_,s)}else _++;var p=t&&t===this.ownerID,v=se(h,s,c,p);return p?(this.count=_,this.nodes=v,this):new Ct(t,_,v)},Jt.prototype.get=function(t,e,r,n){for(var i=this.entries,o=0,u=i.length;u>o;o++)if(X(r,i[o][0]))return i[o][1];return n},Jt.prototype.update=function(t,e,n,o,u,s,a){void 0===n&&(n=et(o));var h=u===cr;if(n!==this.keyHash)return h?this:(r(a),r(s),Zt(this,t,e,n,[o,u]));for(var f=this.entries,c=0,_=f.length;_>c&&!X(o,f[c][0]);c++);var p=_>c;if(p?f[c][1]===u:h)return this;if(r(a),(h||!p)&&r(s),h&&2===_)return new Nt(t,this.keyHash,f[1^c]);var v=t&&t===this.ownerID,l=v?f:i(f);return p?h?c===_-1?l.pop():l[c]=l.pop():l[c]=[o,u]:l.push([o,u]),v?(this.entries=l,this):new Jt(t,this.keyHash,l)},Nt.prototype.get=function(t,e,r,n){return X(r,this.entry[0])?this.entry[1]:n},Nt.prototype.update=function(t,e,n,i,o,u,s){var a=o===cr,h=X(i,this.entry[0]);return(h?o===this.entry[1]:a)?this:(r(s),a?void r(u):h?t&&t===this.ownerID?(this.entry[1]=o,this):new Nt(t,this.keyHash,[i,o]):(r(u),Zt(this,t,e,et(i),[i,o])))},Wt.prototype.iterate=Jt.prototype.iterate=function(t,e){ 26 | for(var r=this.entries,n=0,i=r.length-1;i>=n;n++)if(t(r[e?i-n:n])===!1)return!1},Bt.prototype.iterate=Ct.prototype.iterate=function(t,e){for(var r=this.nodes,n=0,i=r.length-1;i>=n;n++){var o=r[e?i-n:n];if(o&&o.iterate(t,e)===!1)return!1}},Nt.prototype.iterate=function(t){return t(this.entry)},t(Pt,S),Pt.prototype.next=function(){for(var t=this._type,e=this._stack;e;){var r,n=e.node,i=e.index++;if(n.entry){if(0===i)return Ht(t,n.entry)}else if(n.entries){if(r=n.entries.length-1,r>=i)return Ht(t,n.entries[this._reverse?r-i:i])}else if(r=n.nodes.length-1,r>=i){var o=n.nodes[this._reverse?r-i:i];if(o){if(o.entry)return Ht(t,o.entry);e=this._stack=Vt(o,e)}continue}e=this._stack=this._stack.__prev}return I()};var Wr,Br=hr/4,Cr=hr/2,Jr=hr/4;t(fe,Y),fe.of=function(){return this(arguments)},fe.prototype.toString=function(){return this.__toString("List [","]")},fe.prototype.get=function(t,e){if(t=u(this,t),t>=0&&this.size>t){t+=this._origin;var r=ge(this,t);return r&&r.array[t&fr]}return e},fe.prototype.set=function(t,e){return ye(this,t,e)},fe.prototype.remove=function(t){return this.has(t)?0===t?this.shift():t===this.size-1?this.pop():this.splice(t,1):this},fe.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=this._origin=this._capacity=0,this._level=ar,this._root=this._tail=null,this.__hash=void 0,this.__altered=!0,this):le()},fe.prototype.push=function(){var t=arguments,e=this.size;return this.withMutations(function(r){we(r,0,e+t.length);for(var n=0;t.length>n;n++)r.set(e+n,t[n])})},fe.prototype.pop=function(){return we(this,0,-1)},fe.prototype.unshift=function(){var t=arguments;return this.withMutations(function(e){we(e,-t.length);for(var r=0;t.length>r;r++)e.set(r,t[r])})},fe.prototype.shift=function(){return we(this,1)},fe.prototype.merge=function(){return Se(this,void 0,arguments)},fe.prototype.mergeWith=function(t){var e=ur.call(arguments,1);return Se(this,t,e)},fe.prototype.mergeDeep=function(){return Se(this,ne(void 0),arguments)},fe.prototype.mergeDeepWith=function(t){var e=ur.call(arguments,1); 27 | return Se(this,ne(t),e)},fe.prototype.setSize=function(t){return we(this,0,t)},fe.prototype.slice=function(t,e){var r=this.size;return a(t,e,r)?this:we(this,h(t,r),f(e,r))},fe.prototype.__iterator=function(t,e){var r=0,n=pe(this,e);return new S(function(){var e=n();return e===Vr?I():z(t,r++,e)})},fe.prototype.__iterate=function(t,e){for(var r,n=0,i=pe(this,e);(r=i())!==Vr&&t(r,n++,this)!==!1;);return n},fe.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?ve(this._origin,this._capacity,this._level,this._root,this._tail,t,this.__hash):(this.__ownerID=t,this)},fe.isList=ce;var Nr="@@__IMMUTABLE_LIST__@@",Pr=fe.prototype;Pr[Nr]=!0,Pr[sr]=Pr.remove,Pr.setIn=Tr.setIn,Pr.deleteIn=Pr.removeIn=Tr.removeIn,Pr.update=Tr.update,Pr.updateIn=Tr.updateIn,Pr.mergeIn=Tr.mergeIn,Pr.mergeDeepIn=Tr.mergeDeepIn,Pr.withMutations=Tr.withMutations,Pr.asMutable=Tr.asMutable,Pr.asImmutable=Tr.asImmutable,Pr.wasAltered=Tr.wasAltered,_e.prototype.removeBefore=function(t,e,r){if(r===e?1<>>e&fr;if(n>=this.array.length)return new _e([],t);var i,o=0===n;if(e>0){var u=this.array[n];if(i=u&&u.removeBefore(t,e-ar,r),i===u&&o)return this}if(o&&!i)return this;var s=me(this,t);if(!o)for(var a=0;n>a;a++)s.array[a]=void 0;return i&&(s.array[n]=i),s},_e.prototype.removeAfter=function(t,e,r){if(r===(e?1<>>e&fr;if(n>=this.array.length)return this;var i;if(e>0){var o=this.array[n];if(i=o&&o.removeAfter(t,e-ar,r),i===o&&n===this.array.length-1)return this}var u=me(this,t);return u.array.splice(n+1),i&&(u.array[n]=i),u};var Hr,Vr={};t(Ie,Lt),Ie.of=function(){return this(arguments)},Ie.prototype.toString=function(){return this.__toString("OrderedMap {","}")},Ie.prototype.get=function(t,e){var r=this._map.get(t);return void 0!==r?this._list.get(r)[1]:e},Ie.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._map.clear(),this._list.clear(),this):De()},Ie.prototype.set=function(t,e){return Me(this,t,e)},Ie.prototype.remove=function(t){ 28 | return Me(this,t,cr)},Ie.prototype.wasAltered=function(){return this._map.wasAltered()||this._list.wasAltered()},Ie.prototype.__iterate=function(t,e){var r=this;return this._list.__iterate(function(e){return e&&t(e[1],e[0],r)},e)},Ie.prototype.__iterator=function(t,e){return this._list.fromEntrySeq().__iterator(t,e)},Ie.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map.__ensureOwner(t),r=this._list.__ensureOwner(t);return t?qe(e,r,t,this.__hash):(this.__ownerID=t,this._map=e,this._list=r,this)},Ie.isOrderedMap=be,Ie.prototype[dr]=!0,Ie.prototype[sr]=Ie.prototype.remove;var Yr;t(Ee,Y),Ee.of=function(){return this(arguments)},Ee.prototype.toString=function(){return this.__toString("Stack [","]")},Ee.prototype.get=function(t,e){var r=this._head;for(t=u(this,t);r&&t--;)r=r.next;return r?r.value:e},Ee.prototype.peek=function(){return this._head&&this._head.value},Ee.prototype.push=function(){if(0===arguments.length)return this;for(var t=this.size+arguments.length,e=this._head,r=arguments.length-1;r>=0;r--)e={value:arguments[r],next:e};return this.__ownerID?(this.size=t,this._head=e,this.__hash=void 0,this.__altered=!0,this):xe(t,e)},Ee.prototype.pushAll=function(t){if(t=v(t),0===t.size)return this;st(t.size);var e=this.size,r=this._head;return t.reverse().forEach(function(t){e++,r={value:t,next:r}}),this.__ownerID?(this.size=e,this._head=r,this.__hash=void 0,this.__altered=!0,this):xe(e,r)},Ee.prototype.pop=function(){return this.slice(1)},Ee.prototype.unshift=function(){return this.push.apply(this,arguments)},Ee.prototype.unshiftAll=function(t){return this.pushAll(t)},Ee.prototype.shift=function(){return this.pop.apply(this,arguments)},Ee.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):ke()},Ee.prototype.slice=function(t,e){if(a(t,e,this.size))return this;var r=h(t,this.size),n=f(e,this.size);if(n!==this.size)return Y.prototype.slice.call(this,t,e);for(var i=this.size-r,o=this._head;r--;)o=o.next; 29 | return this.__ownerID?(this.size=i,this._head=o,this.__hash=void 0,this.__altered=!0,this):xe(i,o)},Ee.prototype.__ensureOwner=function(t){return t===this.__ownerID?this:t?xe(this.size,this._head,t,this.__hash):(this.__ownerID=t,this.__altered=!1,this)},Ee.prototype.__iterate=function(t,e){if(e)return this.reverse().__iterate(t);for(var r=0,n=this._head;n&&t(n.value,r++,this)!==!1;)n=n.next;return r},Ee.prototype.__iterator=function(t,e){if(e)return this.reverse().__iterator(t);var r=0,n=this._head;return new S(function(){if(n){var e=n.value;return n=n.next,z(t,r++,e)}return I()})},Ee.isStack=Oe;var Qr="@@__IMMUTABLE_STACK__@@",Xr=Ee.prototype;Xr[Qr]=!0,Xr.withMutations=Tr.withMutations,Xr.asMutable=Tr.asMutable,Xr.asImmutable=Tr.asImmutable,Xr.wasAltered=Tr.wasAltered;var Fr;t(Ae,Q),Ae.of=function(){return this(arguments)},Ae.fromKeys=function(t){return this(p(t).keySeq())},Ae.prototype.toString=function(){return this.__toString("Set {","}")},Ae.prototype.has=function(t){return this._map.has(t)},Ae.prototype.add=function(t){return Re(this,this._map.set(t,!0))},Ae.prototype.remove=function(t){return Re(this,this._map.remove(t))},Ae.prototype.clear=function(){return Re(this,this._map.clear())},Ae.prototype.union=function(){var t=ur.call(arguments,0);return t=t.filter(function(t){return 0!==t.size}),0===t.length?this:0!==this.size||this.__ownerID||1!==t.length?this.withMutations(function(e){for(var r=0;t.length>r;r++)l(t[r]).forEach(function(t){return e.add(t)})}):this.constructor(t[0])},Ae.prototype.intersect=function(){var t=ur.call(arguments,0);if(0===t.length)return this;t=t.map(function(t){return l(t)});var e=this;return this.withMutations(function(r){e.forEach(function(e){t.every(function(t){return t.includes(e)})||r.remove(e)})})},Ae.prototype.subtract=function(){var t=ur.call(arguments,0);if(0===t.length)return this;t=t.map(function(t){return l(t)});var e=this;return this.withMutations(function(r){e.forEach(function(e){t.some(function(t){return t.includes(e)})&&r.remove(e)})})},Ae.prototype.merge=function(){ 30 | return this.union.apply(this,arguments)},Ae.prototype.mergeWith=function(){var t=ur.call(arguments,1);return this.union.apply(this,t)},Ae.prototype.sort=function(t){return Le(qt(this,t))},Ae.prototype.sortBy=function(t,e){return Le(qt(this,e,t))},Ae.prototype.wasAltered=function(){return this._map.wasAltered()},Ae.prototype.__iterate=function(t,e){var r=this;return this._map.__iterate(function(e,n){return t(n,n,r)},e)},Ae.prototype.__iterator=function(t,e){return this._map.map(function(t,e){return e}).__iterator(t,e)},Ae.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map.__ensureOwner(t);return t?this.__make(e,t):(this.__ownerID=t,this._map=e,this)},Ae.isSet=je;var Gr="@@__IMMUTABLE_SET__@@",Zr=Ae.prototype;Zr[Gr]=!0,Zr[sr]=Zr.remove,Zr.mergeDeep=Zr.merge,Zr.mergeDeepWith=Zr.mergeWith,Zr.withMutations=Tr.withMutations,Zr.asMutable=Tr.asMutable,Zr.asImmutable=Tr.asImmutable,Zr.__empty=Ke,Zr.__make=Ue;var $r;t(Le,Ae),Le.of=function(){return this(arguments)},Le.fromKeys=function(t){return this(p(t).keySeq())},Le.prototype.toString=function(){return this.__toString("OrderedSet {","}")},Le.isOrderedSet=Te;var tn=Le.prototype;tn[dr]=!0,tn.__empty=Be,tn.__make=We;var en;t(Ce,V),Ce.prototype.toString=function(){return this.__toString(Ne(this)+" {","}")},Ce.prototype.has=function(t){return this._defaultValues.hasOwnProperty(t)},Ce.prototype.get=function(t,e){if(!this.has(t))return e;var r=this._defaultValues[t];return this._map?this._map.get(t,r):r},Ce.prototype.clear=function(){if(this.__ownerID)return this._map&&this._map.clear(),this;var t=this.constructor;return t._empty||(t._empty=Je(this,Qt()))},Ce.prototype.set=function(t,e){if(!this.has(t))throw Error('Cannot set unknown key "'+t+'" on '+Ne(this));var r=this._map&&this._map.set(t,e);return this.__ownerID||r===this._map?this:Je(this,r)},Ce.prototype.remove=function(t){if(!this.has(t))return this;var e=this._map&&this._map.remove(t);return this.__ownerID||e===this._map?this:Je(this,e)},Ce.prototype.wasAltered=function(){return this._map.wasAltered(); 31 | },Ce.prototype.__iterator=function(t,e){var r=this;return p(this._defaultValues).map(function(t,e){return r.get(e)}).__iterator(t,e)},Ce.prototype.__iterate=function(t,e){var r=this;return p(this._defaultValues).map(function(t,e){return r.get(e)}).__iterate(t,e)},Ce.prototype.__ensureOwner=function(t){if(t===this.__ownerID)return this;var e=this._map&&this._map.__ensureOwner(t);return t?Je(this,e,t):(this.__ownerID=t,this._map=e,this)};var rn=Ce.prototype;rn[sr]=rn.remove,rn.deleteIn=rn.removeIn=Tr.removeIn,rn.merge=Tr.merge,rn.mergeWith=Tr.mergeWith,rn.mergeIn=Tr.mergeIn,rn.mergeDeep=Tr.mergeDeep,rn.mergeDeepWith=Tr.mergeDeepWith,rn.mergeDeepIn=Tr.mergeDeepIn,rn.setIn=Tr.setIn,rn.update=Tr.update,rn.updateIn=Tr.updateIn,rn.withMutations=Tr.withMutations,rn.asMutable=Tr.asMutable,rn.asImmutable=Tr.asImmutable,t(Ye,k),Ye.prototype.toString=function(){return 0===this.size?"Range []":"Range [ "+this._start+"..."+this._end+(this._step>1?" by "+this._step:"")+" ]"},Ye.prototype.get=function(t,e){return this.has(t)?this._start+u(this,t)*this._step:e},Ye.prototype.includes=function(t){var e=(t-this._start)/this._step;return e>=0&&this.size>e&&e===Math.floor(e)},Ye.prototype.slice=function(t,e){return a(t,e,this.size)?this:(t=h(t,this.size),e=f(e,this.size),t>=e?new Ye(0,0):new Ye(this.get(t,this._end),this.get(e,this._end),this._step))},Ye.prototype.indexOf=function(t){var e=t-this._start;if(e%this._step===0){var r=e/this._step;if(r>=0&&this.size>r)return r}return-1},Ye.prototype.lastIndexOf=function(t){return this.indexOf(t)},Ye.prototype.__iterate=function(t,e){for(var r=this.size-1,n=this._step,i=e?this._start+r*n:this._start,o=0;r>=o;o++){if(t(i,o,this)===!1)return o+1;i+=e?-n:n}return o},Ye.prototype.__iterator=function(t,e){var r=this.size-1,n=this._step,i=e?this._start+r*n:this._start,o=0;return new S(function(){var u=i;return i+=e?-n:n,o>r?I():z(t,o++,u)})},Ye.prototype.equals=function(t){return t instanceof Ye?this._start===t._start&&this._end===t._end&&this._step===t._step:Ve(this,t)};var nn;t(Qe,k),Qe.prototype.toString=function(){ 32 | return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},Qe.prototype.get=function(t,e){return this.has(t)?this._value:e},Qe.prototype.includes=function(t){return X(this._value,t)},Qe.prototype.slice=function(t,e){var r=this.size;return a(t,e,r)?this:new Qe(this._value,f(e,r)-h(t,r))},Qe.prototype.reverse=function(){return this},Qe.prototype.indexOf=function(t){return X(this._value,t)?0:-1},Qe.prototype.lastIndexOf=function(t){return X(this._value,t)?this.size:-1},Qe.prototype.__iterate=function(t){for(var e=0;this.size>e;e++)if(t(this._value,e,this)===!1)return e+1;return e},Qe.prototype.__iterator=function(t){var e=this,r=0;return new S(function(){return e.size>r?z(t,r++,e._value):I()})},Qe.prototype.equals=function(t){return t instanceof Qe?X(this._value,t._value):Ve(t)};var on;_.Iterator=S,Xe(_,{toArray:function(){st(this.size);var t=Array(this.size||0);return this.valueSeq().__iterate(function(e,r){t[r]=e}),t},toIndexedSeq:function(){return new ht(this)},toJS:function(){return this.toSeq().map(function(t){return t&&"function"==typeof t.toJS?t.toJS():t}).__toJS()},toJSON:function(){return this.toSeq().map(function(t){return t&&"function"==typeof t.toJSON?t.toJSON():t}).__toJS()},toKeyedSeq:function(){return new at(this,!0)},toMap:function(){return Lt(this.toKeyedSeq())},toObject:function(){st(this.size);var t={};return this.__iterate(function(e,r){t[r]=e}),t},toOrderedMap:function(){return Ie(this.toKeyedSeq())},toOrderedSet:function(){return Le(d(this)?this.valueSeq():this)},toSet:function(){return Ae(d(this)?this.valueSeq():this)},toSetSeq:function(){return new ft(this)},toSeq:function(){return m(this)?this.toIndexedSeq():d(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return Ee(d(this)?this.valueSeq():this)},toList:function(){return fe(d(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(t,e){return 0===this.size?t+e:t+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+e},concat:function(){var t=ur.call(arguments,0);return Ot(this,St(this,t)); 33 | },includes:function(t){return this.some(function(e){return X(e,t)})},entries:function(){return this.__iterator(wr)},every:function(t,e){st(this.size);var r=!0;return this.__iterate(function(n,i,o){return t.call(e,n,i,o)?void 0:(r=!1,!1)}),r},filter:function(t,e){return Ot(this,lt(this,t,e,!0))},find:function(t,e,r){var n=this.findEntry(t,e);return n?n[1]:r},findEntry:function(t,e){var r;return this.__iterate(function(n,i,o){return t.call(e,n,i,o)?(r=[i,n],!1):void 0}),r},findLastEntry:function(t,e){return this.toSeq().reverse().findEntry(t,e)},forEach:function(t,e){return st(this.size),this.__iterate(e?t.bind(e):t)},join:function(t){st(this.size),t=void 0!==t?""+t:",";var e="",r=!0;return this.__iterate(function(n){r?r=!1:e+=t,e+=null!==n&&void 0!==n?""+n:""}),e},keys:function(){return this.__iterator(mr)},map:function(t,e){return Ot(this,pt(this,t,e))},reduce:function(t,e,r){st(this.size);var n,i;return arguments.length<2?i=!0:n=e,this.__iterate(function(e,o,u){i?(i=!1,n=e):n=t.call(r,n,e,o,u)}),n},reduceRight:function(){var t=this.toKeyedSeq().reverse();return t.reduce.apply(t,arguments)},reverse:function(){return Ot(this,vt(this,!0))},slice:function(t,e){return Ot(this,mt(this,t,e,!0))},some:function(t,e){return!this.every(Ze(t),e)},sort:function(t){return Ot(this,qt(this,t))},values:function(){return this.__iterator(gr)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some(function(){return!0})},count:function(t,e){return o(t?this.toSeq().filter(t,e):this)},countBy:function(t,e){return yt(this,t,e)},equals:function(t){return Ve(this,t)},entrySeq:function(){var t=this;if(t._cache)return new j(t._cache);var e=t.toSeq().map(Ge).toIndexedSeq();return e.fromEntrySeq=function(){return t.toSeq()},e},filterNot:function(t,e){return this.filter(Ze(t),e)},findLast:function(t,e,r){return this.toKeyedSeq().reverse().find(t,e,r)},first:function(){return this.find(s)},flatMap:function(t,e){return Ot(this,It(this,t,e))},flatten:function(t){return Ot(this,zt(this,t,!0)); 34 | },fromEntrySeq:function(){return new ct(this)},get:function(t,e){return this.find(function(e,r){return X(r,t)},void 0,e)},getIn:function(t,e){for(var r,n=this,i=Kt(t);!(r=i.next()).done;){var o=r.value;if(n=n&&n.get?n.get(o,cr):cr,n===cr)return e}return n},groupBy:function(t,e){return dt(this,t,e)},has:function(t){return this.get(t,cr)!==cr},hasIn:function(t){return this.getIn(t,cr)!==cr},isSubset:function(t){return t="function"==typeof t.includes?t:_(t),this.every(function(e){return t.includes(e)})},isSuperset:function(t){return t="function"==typeof t.isSubset?t:_(t),t.isSubset(this)},keySeq:function(){return this.toSeq().map(Fe).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},max:function(t){return Dt(this,t)},maxBy:function(t,e){return Dt(this,e,t)},min:function(t){return Dt(this,t?$e(t):rr)},minBy:function(t,e){return Dt(this,e?$e(e):rr,t)},rest:function(){return this.slice(1)},skip:function(t){return this.slice(Math.max(0,t))},skipLast:function(t){return Ot(this,this.toSeq().reverse().skip(t).reverse())},skipWhile:function(t,e){return Ot(this,wt(this,t,e,!0))},skipUntil:function(t,e){return this.skipWhile(Ze(t),e)},sortBy:function(t,e){return Ot(this,qt(this,e,t))},take:function(t){return this.slice(0,Math.max(0,t))},takeLast:function(t){return Ot(this,this.toSeq().reverse().take(t).reverse())},takeWhile:function(t,e){return Ot(this,gt(this,t,e))},takeUntil:function(t,e){return this.takeWhile(Ze(t),e)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=nr(this))}});var un=_.prototype;un[vr]=!0,un[Ir]=un.values,un.__toJS=un.toArray,un.__toStringMapper=tr,un.inspect=un.toSource=function(){return""+this},un.chain=un.flatMap,un.contains=un.includes,function(){try{Object.defineProperty(un,"length",{get:function(){if(!_.noLengthWarning){var t;try{throw Error()}catch(e){t=e.stack}if(-1===t.indexOf("_wrapObject"))return console&&console.warn&&console.warn("iterable.length has been deprecated, use iterable.size or iterable.count(). This warning will become a silent error in a future version. "+t), 35 | this.size}}})}catch(t){}}(),Xe(p,{flip:function(){return Ot(this,_t(this))},findKey:function(t,e){var r=this.findEntry(t,e);return r&&r[0]},findLastKey:function(t,e){return this.toSeq().reverse().findKey(t,e)},keyOf:function(t){return this.findKey(function(e){return X(e,t)})},lastKeyOf:function(t){return this.findLastKey(function(e){return X(e,t)})},mapEntries:function(t,e){var r=this,n=0;return Ot(this,this.toSeq().map(function(i,o){return t.call(e,[o,i],n++,r)}).fromEntrySeq())},mapKeys:function(t,e){var r=this;return Ot(this,this.toSeq().flip().map(function(n,i){return t.call(e,n,i,r)}).flip())}});var sn=p.prototype;sn[lr]=!0,sn[Ir]=un.entries,sn.__toJS=un.toObject,sn.__toStringMapper=function(t,e){return JSON.stringify(e)+": "+tr(t)},Xe(v,{toKeyedSeq:function(){return new at(this,!1)},filter:function(t,e){return Ot(this,lt(this,t,e,!1))},findIndex:function(t,e){var r=this.findEntry(t,e);return r?r[0]:-1},indexOf:function(t){var e=this.toKeyedSeq().keyOf(t);return void 0===e?-1:e},lastIndexOf:function(t){return this.toSeq().reverse().indexOf(t)},reverse:function(){return Ot(this,vt(this,!1))},slice:function(t,e){return Ot(this,mt(this,t,e,!1))},splice:function(t,e){var r=arguments.length;if(e=Math.max(0|e,0),0===r||2===r&&!e)return this;t=h(t,0>t?this.count():this.size);var n=this.slice(0,t);return Ot(this,1===r?n:n.concat(i(arguments,2),this.slice(t+e)))},findLastIndex:function(t,e){var r=this.toKeyedSeq().findLastKey(t,e);return void 0===r?-1:r},first:function(){return this.get(0)},flatten:function(t){return Ot(this,zt(this,t,!1))},get:function(t,e){return t=u(this,t),0>t||this.size===1/0||void 0!==this.size&&t>this.size?e:this.find(function(e,r){return r===t},void 0,e)},has:function(t){return t=u(this,t),t>=0&&(void 0!==this.size?this.size===1/0||this.size>t:-1!==this.indexOf(t))},interpose:function(t){return Ot(this,bt(this,t))},interleave:function(){var t=[this].concat(i(arguments)),e=Et(this.toSeq(),k.of,t),r=e.flatten(!0);return e.size&&(r.size=e.size*t.length),Ot(this,r)},last:function(){return this.get(-1); 36 | },skipWhile:function(t,e){return Ot(this,wt(this,t,e,!1))},zip:function(){var t=[this].concat(i(arguments));return Ot(this,Et(this,er,t))},zipWith:function(t){var e=i(arguments);return e[0]=this,Ot(this,Et(this,t,e))}}),v.prototype[yr]=!0,v.prototype[dr]=!0,Xe(l,{get:function(t,e){return this.has(t)?t:e},includes:function(t){return this.has(t)},keySeq:function(){return this.valueSeq()}}),l.prototype.has=un.includes,Xe(x,p.prototype),Xe(k,v.prototype),Xe(A,l.prototype),Xe(V,p.prototype),Xe(Y,v.prototype),Xe(Q,l.prototype);var an={Iterable:_,Seq:O,Collection:H,Map:Lt,OrderedMap:Ie,List:fe,Stack:Ee,Set:Ae,OrderedSet:Le,Record:Ce,Range:Ye,Repeat:Qe,is:X,fromJS:F};return an}); -------------------------------------------------------------------------------- /src/data/vendor/js/react-dom.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * ReactDOM v0.14.3 3 | * 4 | * Copyright 2013-2015, Facebook, Inc. 5 | * All rights reserved. 6 | * 7 | * This source code is licensed under the BSD-style license found in the 8 | * LICENSE file in the root directory of this source tree. An additional grant 9 | * of patent rights can be found in the PATENTS file in the same directory. 10 | * 11 | */ 12 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e(require("react"));else if("function"==typeof define&&define.amd)define(["react"],e);else{var f;f="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,f.ReactDOM=e(f.React)}}(function(e){return e.__SECRET_DOM_DO_NOT_USE_OR_YOU_WILL_BE_FIRED}); -------------------------------------------------------------------------------- /src/data/vendor/js/react-redux.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e(require("react"),require("redux")):"function"==typeof define&&define.amd?define(["react","redux"],e):"object"==typeof exports?exports.ReactRedux=e(require("react"),require("redux")):t.ReactRedux=e(t.React,t.Redux)}(this,function(t,e){return function(t){function e(o){if(r[o])return r[o].exports;var n=r[o]={exports:{},id:o,loaded:!1};return t[o].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var r={};return e.m=t,e.c=r,e.p="",e(0)}([function(t,e,r){"use strict";function o(t){return t&&t.__esModule?t.default:t}e.__esModule=!0;var n=r(3);e.Provider=o(n);var s=r(4);e.connect=o(s)},function(e,r){e.exports=t},function(t,e,r){"use strict";e.__esModule=!0;var o=r(1);e.default=o.PropTypes.shape({subscribe:o.PropTypes.func.isRequired,dispatch:o.PropTypes.func.isRequired,getState:o.PropTypes.func.isRequired}),t.exports=e.default},function(t,e,r){"use strict";function o(t){return t&&t.__esModule?t:{"default":t}}function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function s(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function u(){c||(c=!0,console.error(" does not support changing `store` on the fly. It is most likely that you see this error because you updated to Redux 2.x and React Redux 2.x which no longer hot reload reducers automatically. See https://github.com/rackt/react-redux/releases/tag/v2.0.0 for the migration instructions."))}e.__esModule=!0;var i=r(1),a=r(2),p=o(a),c=!1,f=function(t){function e(r,o){n(this,e),t.call(this,r,o),this.store=r.store}return s(e,t),e.prototype.getChildContext=function(){return{store:this.store}},e.prototype.componentWillReceiveProps=function(t){var e=this.store,r=t.store;e!==r&&u()},e.prototype.render=function(){var t=this.props.children;return i.Children.only(t)},e}(i.Component);e.default=f,f.propTypes={store:p.default.isRequired,children:i.PropTypes.element.isRequired},f.childContextTypes={store:p.default.isRequired},t.exports=e.default},function(t,e,r){"use strict";function o(t){return t&&t.__esModule?t:{"default":t}}function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function s(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function, not "+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}function u(t){return t.displayName||t.name||"Component"}function i(t,e,r){function o(t,e){var r=t.getState(),o=T?b(r,e):b(r);return _.default(m.default(o),"`mapStateToProps` must return an object. Instead received %s.",o),o}function i(t,e){var r=t.dispatch,o=C?v(r,e):v(r);return _.default(m.default(o),"`mapDispatchToProps` must return an object. Instead received %s.",o),o}function f(t,e,r){var o=P(t,e,r);return _.default(m.default(o),"`mergeProps` must return an object. Instead received %s.",o),o}var d=arguments.length<=3||void 0===arguments[3]?{}:arguments[3],y=Boolean(t),b=t||S,v=m.default(e)?g.default(e):e||w,P=r||O,T=b.length>1,C=v.length>1,R=d.pure,M=void 0===R?!0:R,q=d.withRef,E=void 0===q?!1:q,I=j++;return function(t){var e=function(e){function r(t,s){n(this,r),e.call(this,t,s),this.version=I,this.store=t.store||s.store,_.default(this.store,'Could not find "store" in either the context or '+('props of "'+this.constructor.displayName+'". ')+"Either wrap the root component in a , "+('or explicitly pass "store" as a prop to "'+this.constructor.displayName+'".')),this.stateProps=o(this.store,t),this.dispatchProps=i(this.store,t),this.state={storeState:null},this.updateState()}return s(r,e),r.prototype.shouldComponentUpdate=function(t,e){if(!M)return this.updateStateProps(t),this.updateDispatchProps(t),this.updateState(t),!0;var r=e.storeState!==this.state.storeState,o=!h.default(t,this.props),n=!1,s=!1;return(r||o&&T)&&(n=this.updateStateProps(t)),o&&C&&(s=this.updateDispatchProps(t)),o||n||s?(this.updateState(t),!0):!1},r.prototype.computeNextState=function(){var t=arguments.length<=0||void 0===arguments[0]?this.props:arguments[0];return f(this.stateProps,this.dispatchProps,t)},r.prototype.updateStateProps=function(){var t=arguments.length<=0||void 0===arguments[0]?this.props:arguments[0],e=o(this.store,t);return h.default(e,this.stateProps)?!1:(this.stateProps=e,!0)},r.prototype.updateDispatchProps=function(){var t=arguments.length<=0||void 0===arguments[0]?this.props:arguments[0],e=i(this.store,t);return h.default(e,this.dispatchProps)?!1:(this.dispatchProps=e,!0)},r.prototype.updateState=function(){var t=arguments.length<=0||void 0===arguments[0]?this.props:arguments[0];this.nextState=this.computeNextState(t)},r.prototype.isSubscribed=function(){return"function"==typeof this.unsubscribe},r.prototype.trySubscribe=function(){y&&!this.unsubscribe&&(this.unsubscribe=this.store.subscribe(this.handleChange.bind(this)),this.handleChange())},r.prototype.tryUnsubscribe=function(){this.unsubscribe&&(this.unsubscribe(),this.unsubscribe=null)},r.prototype.componentDidMount=function(){this.trySubscribe()},r.prototype.componentWillUnmount=function(){this.tryUnsubscribe()},r.prototype.handleChange=function(){this.unsubscribe&&this.setState({storeState:this.store.getState()})},r.prototype.getWrappedInstance=function(){return _.default(E,"To access the wrapped instance, you need to specify { withRef: true } as the fourth argument of the connect() call."),this.refs.wrappedInstance},r.prototype.render=function(){var e=E?"wrappedInstance":null;return c.default.createElement(t,a({},this.nextState,{ref:e}))},r}(p.Component);return e.displayName="Connect("+u(t)+")",e.WrappedComponent=t,e.contextTypes={store:l.default},e.propTypes={store:l.default},x.default(e,t)}}e.__esModule=!0;var a=Object.assign||function(t){for(var e=1;en;n++)e[n]=arguments[n];return function(t){return e.reduceRight(function(t,e){return e(t)},t)}}e.__esModule=!0,e.default=n,t.exports=e.default},function(t,e){"use strict";function n(t){if(!t||"object"!=typeof t)return!1;var e="function"==typeof t.constructor?Object.getPrototypeOf(t):Object.prototype;if(null===e)return!0;var n=e.constructor;return"function"==typeof n&&n instanceof n&&r(n)===r(Object)}e.__esModule=!0,e.default=n;var r=function(t){return Function.prototype.toString.call(t)};t.exports=e.default},function(t,e){"use strict";function n(t,e){return Object.keys(t).reduce(function(n,r){return n[r]=e(t[r],r),n},{})}e.__esModule=!0,e.default=n,t.exports=e.default},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(){for(var t=arguments.length,e=Array(t),n=0;t>n;n++)e[n]=arguments[n];return function(t){return function(n,r){var o=t(n,r),i=o.dispatch,f=[],c={getState:o.getState,dispatch:function(t){return i(t)}};return f=e.map(function(t){return t(c)}),i=a.default.apply(void 0,f)(o.dispatch),u({},o,{dispatch:i})}}}e.__esModule=!0;var u=Object.assign||function(t){for(var e=1;e { 91 | if (this._groupsPanel.isShowing) { 92 | this._groupsPanel.hide(); 93 | } else { 94 | this._groupsPanel.show({position: this._panelButton}); 95 | this._panelButton.state("window", {checked: true}); 96 | } 97 | } 98 | }); 99 | }, 100 | 101 | createNavigationHotkey: function() { 102 | if (!Prefs.prefs.bindNavigationShortcut) { 103 | return; 104 | } 105 | 106 | this._hotkeyNextGroup = Hotkey({ 107 | combo: "control-`", 108 | onPress: () => { 109 | this._tabs.selectNextPrevGroup( 110 | this._getWindow(), 111 | this._getTabBrowser(), 112 | 1 113 | ); 114 | } 115 | }); 116 | this._hotkeyPrevGroup = Hotkey({ 117 | combo: "control-shift-`", 118 | onPress: () => { 119 | this._tabs.selectNextPrevGroup( 120 | this._getWindow(), 121 | this._getTabBrowser(), 122 | -1 123 | ); 124 | } 125 | }); 126 | }, 127 | 128 | bindHotkeyPreference: function() { 129 | if (Prefs.prefs.bindPanoramaShortcut) { 130 | this.createOpenHotkey(); 131 | } 132 | 133 | if (Prefs.prefs.bindNavigationShortcut) { 134 | this.createNavigationHotkey(); 135 | } 136 | 137 | Prefs.on("bindPanoramaShortcut", () => { 138 | if (Prefs.prefs.bindPanoramaShortcut) { 139 | if (!this._hotkeyOpen) { 140 | this.createOpenHotkey(); 141 | } 142 | } else if (this._hotkeyOpen) { 143 | this._hotkeyOpen.destroy(); 144 | this._hotkeyOpen = null; 145 | } 146 | }); 147 | 148 | Prefs.on("bindNavigationShortcut", () => { 149 | if (Prefs.prefs.bindNavigationShortcut) { 150 | if (!this._hotkeyNextGroup) { 151 | this.createNavigationHotkey(); 152 | } 153 | } else { 154 | if (this._hotkeyNextGroup) { 155 | this._hotkeyNextGroup.destroy(); 156 | this._hotkeyNextGroup = null; 157 | } 158 | if (this._hotkeyPrevGroup) { 159 | this._hotkeyPrevGroup.destroy(); 160 | this._hotkeyPrevGroup = null; 161 | } 162 | } 163 | }); 164 | }, 165 | 166 | bindGroupPreference: function() { 167 | let emitCloseTimeoutChange = () => { 168 | this._groupsPanel.port.emit("Groups:CloseTimeoutChanged", Prefs.prefs.groupCloseTimeout); 169 | }; 170 | 171 | Prefs.on("groupCloseTimeout", emitCloseTimeoutChange); 172 | 173 | emitCloseTimeoutChange(); 174 | }, 175 | 176 | bindPanelButtonEvents: function() { 177 | this._panelButton.on("change", (state) => { 178 | if (!state.checked) { 179 | this._groupsPanel.hide(); 180 | } else { 181 | if (this._groupsPanel.isShowing) { 182 | this._groupsPanel.hide(); 183 | } 184 | 185 | this._groupsPanel.show({position: this._panelButton}); 186 | } 187 | }); 188 | }, 189 | 190 | bindPanelEvents: function() { 191 | this._groupsPanel.on("hide", () => { 192 | this._panelButton.state("window", {checked: false}); 193 | }); 194 | 195 | this._groupsPanel.on("show", this.refreshUi.bind(this)); 196 | 197 | this._groupsPanel.port.on("Group:Add", this.onGroupAdd.bind(this)); 198 | this._groupsPanel.port.on("Group:AddWithTab", this.onGroupAddWithTab.bind(this)); 199 | this._groupsPanel.port.on("Group:Close", this.onGroupClose.bind(this)); 200 | this._groupsPanel.port.on("Group:Rename", this.onGroupRename.bind(this)); 201 | this._groupsPanel.port.on("Group:Select", this.onGroupSelect.bind(this)); 202 | this._groupsPanel.port.on("Group:Drop", this.onGroupDrop.bind(this)); 203 | this._groupsPanel.port.on("Tab:Select", this.onTabSelect.bind(this)); 204 | this._groupsPanel.port.on("UI:Resize", this.resizePanel.bind(this)); 205 | }, 206 | 207 | bindTabEvents: function() { 208 | Tabs.on("activate", () => { 209 | let window = this._getWindow(); 210 | this._tabs.updateCurrentSelectedTab(window); 211 | this._tabs.updateCurrentSelectedGroup(window); 212 | }); 213 | Tabs.on("open", () => { 214 | this._tabs.updateCurrentSelectedTab(this._getWindow()); 215 | }); 216 | }, 217 | 218 | refreshUi: function() { 219 | let groups = this._tabs.getGroupsWithTabs(this._getWindow(), Prefs.prefs.enableAlphabeticSort); 220 | 221 | this._groupsPanel.port.emit("Groups:Changed", groups); 222 | }, 223 | 224 | resizePanel: function(dimensions) { 225 | this._groupsPanel.resize( 226 | this._groupsPanel.width, 227 | dimensions.height + 18 228 | ); 229 | }, 230 | 231 | onGroupAdd: function() { 232 | this._tabs.addGroup( 233 | this._getWindow() 234 | ); 235 | this.refreshUi(); 236 | }, 237 | 238 | onGroupAddWithTab: function(event) { 239 | this._tabs.addGroupWithTab( 240 | this._getWindow(), 241 | this._getTabBrowser(), 242 | event.tabIndex 243 | ); 244 | this.refreshUi(); 245 | }, 246 | 247 | onGroupClose: function(event) { 248 | this._tabs.closeGroup( 249 | this._getWindow(), 250 | this._getTabBrowser(), 251 | event.groupID 252 | ); 253 | this.refreshUi(); 254 | }, 255 | 256 | onGroupRename: function(event) { 257 | this._tabs.renameGroup( 258 | this._getWindow(), 259 | event.groupID, 260 | event.title 261 | ); 262 | this.refreshUi(); 263 | }, 264 | 265 | onGroupSelect: function(event) { 266 | this._tabs.selectGroup( 267 | this._getWindow(), 268 | this._getTabBrowser(), 269 | event.groupID 270 | ); 271 | this.refreshUi(); 272 | }, 273 | 274 | onTabSelect: function(event) { 275 | this._tabs.selectTab( 276 | this._getWindow(), 277 | this._getTabBrowser(), 278 | event.tabIndex, 279 | event.groupID 280 | ); 281 | this.refreshUi(); 282 | }, 283 | 284 | onGroupDrop: function(event) { 285 | this._tabs.moveTabToGroup( 286 | this._getWindow(), 287 | this._getTabBrowser(), 288 | event.tabIndex, 289 | event.targetGroupID 290 | ); 291 | this.refreshUi(); 292 | }, 293 | 294 | _getWindow: function() { 295 | return WindowUtils.getMostRecentBrowserWindow(); 296 | }, 297 | 298 | _getTabBrowser: function() { 299 | return TabsUtils.getTabBrowser(this._getWindow()); 300 | } 301 | }; 302 | 303 | new TabGroups(); 304 | -------------------------------------------------------------------------------- /src/install.rdf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tabgroups@schub.io 5 | 2 6 | true 7 | 0.6.0-dev 8 | data:text/xml,<placeholder/> 9 | 2 10 | 11 | 12 | 13 | 14 | {ec8030f7-c20a-464f-9b0e-13a3a9e97384} 15 | 44.0a1 16 | * 17 | 18 | 19 | 20 | Simplified Tab Groups 21 | Provides tab grouping features similar to the removed TabView/TabGroups/Panorama features. 22 | Dennis Schubert <mozilla@dennis-schubert.de> 23 | https://github.com/denschub/firefox-tabgroups 24 | chrome://simplified-tabgroups/content/icons/extension-32.png 25 | chrome://simplified-tabgroups/content/icons/extension-32.png 26 | true 27 | 28 | 29 | 30 | de-DE 31 | Einfache Tab-Gruppen 32 | Ermöglicht die Gruppierung von Tabs ähnlich den entfernten Panorama-Funktionen 33 | Dennis Schubert <mozilla@dennis-schubert.de> 34 | https://github.com/denschub/firefox-tabgroups 35 | 36 | 37 | 38 | 39 | fr-FR 40 | Simplified Tab Groups 41 | Permet de remplacer la gestion des groupes d'onglets supprimée suite à l'abandon de Panorama. 42 | Dennis Schubert <mozilla@dennis-schubert.de> 43 | https://github.com/denschub/firefox-tabgroups 44 | 45 | 46 | 47 | 48 | es-AR 49 | Grupos de pestañas simplificados 50 | Provee la funcionalidad de gestionar grupos de pestañas similar a la funcionalidad removida de TabView/TabGroups/Panorama. 51 | Dennis Schubert <mozilla@dennis-schubert.de> 52 | https://github.com/denschub/firefox-tabgroups 53 | 54 | 55 | 56 | 57 | es-ES 58 | Grupos de pestañas simplificados 59 | Provee la funcionalidad de gestionar grupos de pestañas similar a la funcionalidad removida de TabView/TabGroups/Panorama. 60 | Dennis Schubert <mozilla@dennis-schubert.de> 61 | https://github.com/denschub/firefox-tabgroups 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/lib/storage/session.js: -------------------------------------------------------------------------------- 1 | const {Cc, Ci} = require("chrome"); 2 | const TabsUtils = require("sdk/tabs/utils"); 3 | 4 | function SessionStorage() { 5 | this._store = Cc["@mozilla.org/browser/sessionstore;1"] 6 | .getService(Ci.nsISessionStore); 7 | } 8 | 9 | /** 10 | * Note: This is an implementation of the existing Panorama storage using 11 | * SessionStore so we are able to reuse the existing groups. 12 | * 13 | * This will eventually get replaced by the SDKs simple-storage or something 14 | * similar. 15 | */ 16 | SessionStorage.prototype = { 17 | /** 18 | * Returns an array of available groups. 19 | * 20 | * @param {ChromeWindow} chromeWindow 21 | * @returns {Array} 22 | */ 23 | getGroups: function(chromeWindow) { 24 | let groupsData = this._getGroupsData(chromeWindow); 25 | let currentGroup = this._getCurrentGroupData(chromeWindow); 26 | 27 | if (Object.keys(groupsData).length == 0) { 28 | this.addGroup(chromeWindow); 29 | groupsData = this._getGroupsData(chromeWindow); 30 | } 31 | 32 | let groups = []; 33 | for (let groupIndex in groupsData) { 34 | let group = groupsData[groupIndex]; 35 | 36 | groups.push({ 37 | active: group.id == currentGroup.activeGroupId, 38 | id: group.id, 39 | title: group.title, 40 | selectedIndex: group.selectedIndex 41 | }); 42 | } 43 | 44 | return groups; 45 | }, 46 | 47 | /** 48 | * Returns all tabs. 49 | * 50 | * @param {ChromeWindow} chromeWindow 51 | * @returns {Array} 52 | */ 53 | getTabs: function(chromeWindow) { 54 | let browser = TabsUtils.getTabBrowser(chromeWindow); 55 | let tabs = []; 56 | let currentGroup = this.getCurrentGroup(chromeWindow); 57 | 58 | for (let tabIndex = 0; tabIndex < browser.tabs.length; tabIndex++) { 59 | let tab = browser.tabs[tabIndex]; 60 | let tabData = this._getTabData(tab); 61 | let tabState = this._getTabState(tab); 62 | 63 | if (tabState.pinned) { 64 | continue; 65 | } 66 | 67 | let group = currentGroup; 68 | if (tabData && tabData.groupID) { 69 | group = tabData.groupID; 70 | } else { 71 | this.setTabGroup(tab, group); 72 | } 73 | 74 | tabs.push({ 75 | active: tab.selected, 76 | group: group, 77 | icon: browser.getIcon(tab), 78 | index: tabIndex, 79 | title: tab.label 80 | }); 81 | } 82 | 83 | return tabs; 84 | }, 85 | 86 | /** 87 | * Returns all tab indexes in the specified group. 88 | * 89 | * @param {TabBrowser} tabBrowser 90 | * @param {Number} groupID 91 | * @returns {Array} 92 | */ 93 | getTabIndexesByGroup: function(tabBrowser, targetGroupId) { 94 | let tabs = []; 95 | 96 | for (let tabIndex = 0; tabIndex < tabBrowser.tabs.length; tabIndex++) { 97 | let tab = tabBrowser.tabs[tabIndex]; 98 | let tabData = this._getTabData(tab); 99 | let tabState = this._getTabState(tab); 100 | 101 | let group = 0; 102 | if (tabData && tabData.groupID) { 103 | group = tabData.groupID; 104 | } 105 | 106 | if (tabState.pinned || group != targetGroupId) { 107 | continue; 108 | } 109 | 110 | tabs.push(tabIndex); 111 | } 112 | 113 | return tabs; 114 | }, 115 | 116 | /** 117 | * Returns the ID of the current group. 118 | * 119 | * @param {ChromeWindow} chromeWindow 120 | * @returns {Number} 121 | */ 122 | getCurrentGroup: function(chromeWindow) { 123 | let groupData = this._getCurrentGroupData(chromeWindow); 124 | return groupData.activeGroupId || 0; 125 | }, 126 | 127 | /** 128 | * Returns the ID of the current group. 129 | * 130 | * @param {ChromeWindow} chromeWindow 131 | * @param {Number} groupID 132 | */ 133 | setCurrentGroup: function(chromeWindow, groupID) { 134 | let groupData = this._getCurrentGroupData(chromeWindow); 135 | groupData.activeGroupId = groupID; 136 | this._setCurrentGroupData(chromeWindow, groupData); 137 | }, 138 | 139 | /** 140 | * Assigns a tab to a group. 141 | * 142 | * @param {XULElement} tab 143 | * @param {Number} groupID 144 | */ 145 | setTabGroup: function(tab, groupID) { 146 | this._setTabData( 147 | tab, 148 | Object.assign({}, this._getTabData(tab), {groupID}) 149 | ); 150 | }, 151 | 152 | /** 153 | * Returns the next possible GroupID. 154 | * 155 | * @param {ChromeWindow} chromeWindow 156 | * @returns {Number} 157 | */ 158 | getNextGroupID: function(chromeWindow) { 159 | let groupData = this._getCurrentGroupData(chromeWindow); 160 | let id = groupData.nextID; 161 | groupData.nextID++; 162 | this._setCurrentGroupData(chromeWindow, groupData); 163 | return id; 164 | }, 165 | 166 | /** 167 | * Creates a new tab group. 168 | * 169 | * @param {ChromeWindow} chromeWindow 170 | * @param {String} title - defaults to an empty string 171 | */ 172 | addGroup: function(chromeWindow, title = "") { 173 | let groups = this._getGroupsData(chromeWindow); 174 | let groupID = this.getNextGroupID(chromeWindow); 175 | groups[groupID] = { 176 | id: groupID, 177 | title: title, 178 | selectedIndex: 0 179 | }; 180 | 181 | let currentGroups = this._getCurrentGroupData(chromeWindow); 182 | currentGroups.totalNumber++; 183 | 184 | this._setGroupsData(chromeWindow, groups); 185 | this._setCurrentGroupData(chromeWindow, currentGroups); 186 | }, 187 | 188 | /** 189 | * Removes tabs from a specified group. 190 | * 191 | * @param {TabBrowser} tabBrowser 192 | * @param {Number} groupID 193 | */ 194 | removeGroupTabs: function(tabBrowser, groupID) { 195 | let tabsToRemove = []; 196 | for (let tabIndex = 0; tabIndex < tabBrowser.tabs.length; tabIndex++) { 197 | let tab = tabBrowser.tabs[tabIndex]; 198 | let tabData = this._getTabData(tab); 199 | 200 | if (tabData && tabData.groupID && tabData.groupID == groupID) { 201 | tabsToRemove.push(tab); 202 | } 203 | } 204 | 205 | tabsToRemove.forEach((tab) => { 206 | tabBrowser.removeTab(tab); 207 | }); 208 | }, 209 | 210 | /** 211 | * Removes a tab group from the storage. 212 | * 213 | * @param {ChromeWindow} chromeWindow 214 | * @param {Number} groupID 215 | */ 216 | removeGroup: function(chromeWindow, groupID) { 217 | let groups = this._getGroupsData(chromeWindow); 218 | delete groups[groupID]; 219 | 220 | let currentGroups = this._getCurrentGroupData(chromeWindow); 221 | currentGroups.totalNumber -= 1; 222 | 223 | this._setGroupsData(chromeWindow, groups); 224 | this._setCurrentGroupData(chromeWindow, currentGroups); 225 | }, 226 | 227 | /** 228 | * Renames a group. 229 | * 230 | * @param {ChromeWindow} chromeWindow 231 | * @param {Number} groupID - the groupID 232 | * @param {String} title - the new title 233 | */ 234 | renameGroup: function(chromeWindow, groupID, title) { 235 | let groupsData = this._getGroupsData(chromeWindow); 236 | groupsData[groupID].title = title; 237 | this._setGroupsData(chromeWindow, groupsData); 238 | }, 239 | 240 | /** 241 | * Get the selected tab index for a group. 242 | * 243 | * @param {ChromeWindow} chromeWindow 244 | * @param {Number} groupID - the groupID 245 | */ 246 | getGroupSelectedIndex: function(chromeWindow, groupID) { 247 | let groupsData = this._getGroupsData(chromeWindow); 248 | let currentGroup = groupsData[groupID]; 249 | return currentGroup == null ? 0 : currentGroup.selectedIndex; 250 | }, 251 | 252 | /** 253 | * Set the selected tab index for a group. 254 | * 255 | * @param {ChromeWindow} chromeWindow 256 | * @param {Number} groupID - the groupID 257 | * @param {Number} index - the new selected index 258 | */ 259 | setGroupSelectedIndex: function(chromeWindow, groupID, index) { 260 | let groupsData = this._getGroupsData(chromeWindow); 261 | let currentGroup = groupsData[groupID]; 262 | if (currentGroup != null) { 263 | currentGroup.selectedIndex = index; 264 | this._setGroupsData(chromeWindow, groupsData); 265 | } 266 | }, 267 | 268 | /** 269 | * Returns the data for a tab. 270 | * 271 | * @param {XULElement} tab 272 | * @returns {Object} 273 | */ 274 | _getTabData: function(tab) { 275 | return this._parseOptionalJson( 276 | this._store.getTabValue(tab, "tabview-tab") 277 | ); 278 | }, 279 | 280 | /** 281 | * Stores the data for a tab. 282 | * 283 | * @param {XULElement} tab 284 | * @param {Object} data 285 | * @returns {Object} 286 | */ 287 | _setTabData: function(tab, data) { 288 | this._stopTabView(tab.ownerDocument.defaultView); 289 | this._store.setTabValue( 290 | tab, 291 | "tabview-tab", 292 | JSON.stringify(data) 293 | ); 294 | }, 295 | 296 | /** 297 | * Returns the data for the current tab state. 298 | * 299 | * @param {XULElement} tab 300 | * @returns {Object} 301 | */ 302 | _getTabState: function(tab) { 303 | return this._parseOptionalJson( 304 | this._store.getTabState(tab) 305 | ); 306 | }, 307 | 308 | /** 309 | * Returns all tab groups with additional information. 310 | * 311 | * @param {ChromeWindow} chromeWindow 312 | * @returns {Object} 313 | */ 314 | _getGroupsData: function(chromeWindow) { 315 | return this._parseOptionalJson( 316 | this._store.getWindowValue(chromeWindow, "tabview-group") 317 | ); 318 | }, 319 | 320 | /** 321 | * Set group information for the given window. 322 | * 323 | * @param {ChromeWindow} chromeWindow 324 | * @param {Object} data 325 | * @returns {Object} 326 | */ 327 | _setGroupsData: function(chromeWindow, data) { 328 | this._stopTabView(chromeWindow); 329 | this._store.setWindowValue( 330 | chromeWindow, 331 | "tabview-group", 332 | JSON.stringify(data) 333 | ); 334 | }, 335 | 336 | /** 337 | * Returns the current group as well as the next group ID and the total 338 | * number of groups. 339 | * 340 | * @param {ChromeWindow} chromeWindow 341 | * @returns {Object} 342 | */ 343 | _getCurrentGroupData: function(chromeWindow) { 344 | let data = this._parseOptionalJson( 345 | this._store.getWindowValue(chromeWindow, "tabview-groups") 346 | ); 347 | 348 | if (Object.keys(data).length == 0) { 349 | data = { 350 | activeGroupId: 1, 351 | nextID: 1, 352 | totalNumber: 0 353 | }; 354 | } 355 | 356 | return data; 357 | }, 358 | 359 | /** 360 | * Stores information about the current session. 361 | * 362 | * @param {ChromeWindow} chromeWindow 363 | * @param {Object} data 364 | * @returns {Object} 365 | */ 366 | _setCurrentGroupData: function(chromeWindow, data) { 367 | this._stopTabView(chromeWindow); 368 | this._store.setWindowValue( 369 | chromeWindow, 370 | "tabview-groups", 371 | JSON.stringify(data) 372 | ); 373 | }, 374 | 375 | /** 376 | * Deinitializes the TabView frame from the Tab Groups add-on, so that it 377 | * reconstructs any changed data by us properly when it is accessed again. 378 | * The _deinitFrame method only exists with that add-on enabled, and it 379 | * will no-op if the frame isn't already initialized. 380 | * 381 | * @param {ChromeWindow} chromeWindow 382 | */ 383 | _stopTabView: function(chromeWindow) { 384 | if (chromeWindow.TabView && chromeWindow.TabView._deinitFrame) { 385 | chromeWindow.TabView._deinitFrame(); 386 | } 387 | }, 388 | 389 | /** 390 | * Safely parses a JSON string. 391 | * 392 | * @param {String} jsonString - JSON encoded data 393 | * @returns {Object} decoded JSON data or an empty object if something failed 394 | */ 395 | _parseOptionalJson: function(jsonString) { 396 | if (jsonString) { 397 | try { 398 | return JSON.parse(jsonString); 399 | } catch (e) { 400 | return {}; 401 | } 402 | } 403 | return {}; 404 | } 405 | }; 406 | 407 | exports.SessionStorage = SessionStorage; 408 | -------------------------------------------------------------------------------- /src/lib/tabmanager.js: -------------------------------------------------------------------------------- 1 | const TabsUtils = require("sdk/tabs/utils"); 2 | 3 | function TabManager(storage) { 4 | this._storage = storage; 5 | } 6 | 7 | TabManager.prototype = { 8 | /** 9 | * Returns all groups with their tabs. 10 | * 11 | * @param {ChromeWindow} chromeWindow 12 | * @returns {Object} 13 | */ 14 | getGroupsWithTabs: function(chromeWindow, sort) { 15 | let groups = this._storage.getGroups(chromeWindow); 16 | let tabs = this._storage.getTabs(chromeWindow); 17 | 18 | let retGroups = groups.map((group) => { 19 | return Object.assign({}, group, { 20 | tabs: tabs.filter((tab) => { 21 | return tab.group == group.id; 22 | }) 23 | }); 24 | }); 25 | 26 | if (sort) { 27 | retGroups.sort((a, b) => { 28 | if (a.title.toLowerCase() == b.title.toLowerCase()) { 29 | return 0; 30 | } 31 | 32 | return a.title.toLowerCase() < b.title.toLowerCase() ? -1 : 1; 33 | }); 34 | } 35 | 36 | return retGroups; 37 | }, 38 | 39 | /** 40 | * Selects a given tab. 41 | * 42 | * @param {ChromeWindow} chromeWindow 43 | * @param {TabBrowser} tabBrowser 44 | * @param {Number} index - the tabs index 45 | * @param {Number} groupID - the tabs groupID 46 | */ 47 | selectTab: function(chromeWindow, tabBrowser, index, groupID) { 48 | let currentGroup = this._storage.getCurrentGroup(chromeWindow); 49 | 50 | if (currentGroup == groupID) { 51 | tabBrowser.selectedTab = tabBrowser.tabs[index]; 52 | } else { 53 | this.selectGroup(chromeWindow, tabBrowser, groupID, index); 54 | } 55 | }, 56 | 57 | /** 58 | * Move tab beetwen groups 59 | * 60 | * @param {ChromeWindow} chromeWindow 61 | * @param {TabBrowser} tabBrowser 62 | * @param {Number} tabIndex - the tabs index 63 | * @param {Number} targetGroupID - target groupID (where to move tab) 64 | */ 65 | moveTabToGroup: function(chromeWindow, tabBrowser, tabIndex, targetGroupID) { 66 | let tab = tabBrowser.tabs[tabIndex]; 67 | if (tab.groupID === targetGroupID) { 68 | return; 69 | } 70 | this._storage.setTabGroup(tab, targetGroupID); 71 | if (tab.selected) { 72 | this.selectGroup(chromeWindow, tabBrowser, targetGroupID); 73 | } 74 | }, 75 | 76 | /** 77 | * Selects a given group. 78 | * 79 | * @param {ChromeWindow} chromeWindow 80 | * @param {TabBrowser} tabBrowser 81 | * @param {Number} groupID - the groupID 82 | * @param {Number} tabIndex - the tab to activate 83 | */ 84 | selectGroup: function(chromeWindow, tabBrowser, groupID, tabIndex = 0) { 85 | let currentGroup = this._storage.getCurrentGroup(chromeWindow); 86 | if (currentGroup == groupID) { 87 | return; 88 | } 89 | 90 | this.updateCurrentSelectedTab(chromeWindow); 91 | 92 | let lastSelected = this._storage.getGroupSelectedIndex(chromeWindow, groupID); 93 | let tabs = this._storage.getTabIndexesByGroup(tabBrowser, groupID); 94 | 95 | let selectedTab; 96 | if (tabs.length == 0) { 97 | selectedTab = tabBrowser.addTab("about:newtab"); 98 | this._storage.setTabGroup(selectedTab, groupID); 99 | tabs.push(selectedTab); 100 | } else if (tabIndex) { 101 | selectedTab = tabBrowser.tabs[tabIndex]; 102 | } else { 103 | selectedTab = tabBrowser.tabs[lastSelected < tabs.length ? tabs[lastSelected] : tabs[0]]; 104 | } 105 | 106 | this._storage.setCurrentGroup(chromeWindow, groupID); 107 | tabBrowser.selectedTab = selectedTab; 108 | 109 | tabBrowser.showOnlyTheseTabs(tabs.map((tab) => { 110 | return tabBrowser.tabs[tab]; 111 | })); 112 | }, 113 | 114 | /** 115 | * Selects the next or previous group in the list 116 | * 117 | * @param {ChromeWindow} chromeWindow 118 | * @param {Number} direction 119 | */ 120 | selectNextPrevGroup: function(chromeWindow, tabBrowser, direction) { 121 | let currentGroup = this._storage.getCurrentGroup(chromeWindow); 122 | let groups = this._storage.getGroups(chromeWindow); 123 | if (groups.length == 0) { 124 | return; 125 | } 126 | 127 | let index = groups.findIndex((group) => { 128 | return group.id == currentGroup; 129 | }); 130 | 131 | if (index == -1) { 132 | return; 133 | } 134 | 135 | index = (index + direction + groups.length) % groups.length; 136 | this.selectGroup(chromeWindow, tabBrowser, groups[index].id); 137 | }, 138 | 139 | /** 140 | * Updates the currently selected index for the given window 141 | * 142 | * @param {ChromeWindow} chromeWindow 143 | */ 144 | updateCurrentSelectedTab: function(chromeWindow) { 145 | let tabs = this._storage.getTabs(chromeWindow); 146 | let curtab = tabs.find((tab) => { 147 | return tab.active; 148 | }); 149 | 150 | if (curtab) { 151 | let curindex = tabs.filter((tab) => { 152 | return tab.group == curtab.group; 153 | }).indexOf(curtab); 154 | 155 | this._storage.setGroupSelectedIndex(chromeWindow, curtab.group, curindex); 156 | } 157 | }, 158 | 159 | /** 160 | * Updates the currently selected group based on the active tab 161 | * 162 | * @param {ChromwWindow} chromeWindow 163 | */ 164 | updateCurrentSelectedGroup: function(chromeWindow) { 165 | let tabs = this._storage.getTabs(chromeWindow); 166 | let curtab = tabs.find((tab) => { 167 | return tab.active; 168 | }); 169 | 170 | if (curtab) { 171 | let currentGroupID = this._storage.getCurrentGroup(chromeWindow); 172 | if (currentGroupID && curtab.group !== currentGroupID) { 173 | this.selectGroup(chromeWindow, TabsUtils.getTabBrowser(chromeWindow), curtab.group, tabs.indexOf(curtab)); 174 | } 175 | } 176 | }, 177 | 178 | /** 179 | * Renames a given group. 180 | * 181 | * @param {ChromeWindow} chromeWindow 182 | * @param {Number} groupID - the groupID 183 | * @param {String} title - the new title 184 | */ 185 | renameGroup: function(chromeWindow, groupID, title) { 186 | this._storage.renameGroup(chromeWindow, groupID, title); 187 | }, 188 | 189 | /** 190 | * Adds a blank group 191 | * 192 | * @param {ChromeWindow} chromeWindow 193 | */ 194 | addGroup: function(chromeWindow) { 195 | this._storage.addGroup(chromeWindow); 196 | }, 197 | 198 | /** 199 | * Adds a group with associated tab 200 | * 201 | * @param {ChromeWindow} chromeWindow 202 | * @param {TabBrowser} tabBrowser 203 | * @param {Number} tabIndex - the tab to place into group 204 | */ 205 | addGroupWithTab: function(chromeWindow, tabBrowser, tabIndex) { 206 | this._storage.addGroup(chromeWindow); 207 | let group = this.getRecentlyAddedGroup(chromeWindow); 208 | this.moveTabToGroup( 209 | chromeWindow, 210 | tabBrowser, 211 | tabIndex, 212 | group.id 213 | ); 214 | }, 215 | 216 | /** 217 | * Return recently added group 218 | * 219 | * @param {ChromeWindow} chromeWindow 220 | */ 221 | getRecentlyAddedGroup: function(chromeWindow) { 222 | let currentGoups = this._storage.getGroups(chromeWindow); 223 | let recentlyAddedGroup = null; 224 | if (currentGoups.length > 0) { 225 | recentlyAddedGroup = currentGoups[currentGoups.length - 1]; 226 | } 227 | return recentlyAddedGroup; 228 | }, 229 | 230 | /** 231 | * Closes a group and all attached tabs 232 | * 233 | * @param {Number} groupID - the groupID 234 | */ 235 | closeGroup: function(chromeWindow, tabBrowser, groupID) { 236 | this._storage.removeGroup(chromeWindow, groupID); 237 | 238 | let currentGroup = this._storage.getCurrentGroup(chromeWindow); 239 | if (currentGroup == groupID) { 240 | let remainingGroups = this._storage.getGroups(chromeWindow); 241 | this.selectGroup(chromeWindow, tabBrowser, remainingGroups[0].id); 242 | } 243 | 244 | this._storage.removeGroupTabs(tabBrowser, groupID); 245 | } 246 | }; 247 | 248 | exports.TabManager = TabManager; 249 | -------------------------------------------------------------------------------- /src/lib/utils.js: -------------------------------------------------------------------------------- 1 | const _ = require("sdk/l10n").get; 2 | const PrefService = require("sdk/preferences/service"); 3 | 4 | /** 5 | * Returns an object of translated strings for the use in the frontend. 6 | * 7 | * @param {Array} keys - l10n keys 8 | * @returns {Object} 9 | */ 10 | exports.getL10nStrings = function(keys) { 11 | let returnStrings = {}; 12 | 13 | for (let key of keys) { 14 | returnStrings[key] = _(key); 15 | } 16 | 17 | return returnStrings; 18 | }; 19 | 20 | function isDarkTheme() { 21 | let currentTheme = PrefService.get("lightweightThemes.selectedThemeID"); 22 | switch (currentTheme) { 23 | case "firefox-compact-dark@mozilla.org": 24 | return true; 25 | case "firefox-devedition@mozilla.org": 26 | let devtoolsTheme = PrefService.get("devtools.theme"); 27 | return devtoolsTheme == "dark"; 28 | default: 29 | return false; 30 | } 31 | } 32 | 33 | /** 34 | * Used to switch stuff by the current design. 35 | * 36 | * @param {Object} object - object with .light and .dark 37 | * @returns {Object} input.dark if a dark theme is used, .light otherwise 38 | */ 39 | exports.themeSwitch = function(object) { 40 | return isDarkTheme() ? object.dark : object.light; 41 | }; 42 | -------------------------------------------------------------------------------- /src/locale/de-DE.properties: -------------------------------------------------------------------------------- 1 | bindPanoramaShortcut_title = Strg/Cmd-Umschalt-E verwenden 2 | enableAlphabeticSort_title = Alphabetische Sortierung 3 | 4 | add_group = Neue Gruppe anlegen 5 | panelButton_label = Tabs gruppieren 6 | unnamed_group = Unbenannte Gruppe 7 | -------------------------------------------------------------------------------- /src/locale/en-US.properties: -------------------------------------------------------------------------------- 1 | bindPanoramaShortcut_title = Listen to Ctrl/Cmd-Shift-E 2 | enableAlphabeticSort_title = Enable alphabetic sorting 3 | groupUndoCloseTimeout_title = Closing group delay 4 | groupUndoCloseTimeout_description = Delay in seconds to wait before the group gets actually closed. 5 | 6 | add_group = Create new group 7 | panelButton_label = Group Tabs 8 | unnamed_group = Unnamed Group 9 | close_group_prompt_title = Close group 10 | close_group_prompt_message = Do you want to close this group? 11 | -------------------------------------------------------------------------------- /src/locale/es-AR.properties: -------------------------------------------------------------------------------- 1 | bindPanoramaShortcut_title = Escuchar el atajo de teclado Ctrl/Cmd-Shift-E 2 | enableAlphabeticSort_title = Habilitar orden alfabético 3 | groupUndoCloseTimeout_title = Retardo al cerrar un grupo 4 | groupUndoCloseTimeout_description = Tiempo de espera en segundos antes de cerrar el grupo. 5 | 6 | add_group = Crear nuevo grupo 7 | panelButton_label = Grupos de Pestañas 8 | unnamed_group = Grupo sin nombre 9 | close_group_prompt_title = Cerrar grupo 10 | close_group_prompt_message = ¿Está seguro de querer cerrar este grupo? 11 | -------------------------------------------------------------------------------- /src/locale/es-ES.properties: -------------------------------------------------------------------------------- 1 | bindPanoramaShortcut_title = Escuchar el atajo de teclado Ctrl/Cmd-Shift-E 2 | enableAlphabeticSort_title = Habilitar orden alfabético 3 | groupUndoCloseTimeout_title = Retardo al cerrar un grupo 4 | groupUndoCloseTimeout_description = Tiempo de espera en segundos antes de cerrar el grupo. 5 | 6 | add_group = Crear nuevo grupo 7 | panelButton_label = Grupos de Pestañas 8 | unnamed_group = Grupo sin nombre 9 | close_group_prompt_title = Cerrar grupo 10 | close_group_prompt_message = ¿Quieres cerrar este grupo? 11 | -------------------------------------------------------------------------------- /src/locale/fr-FR.properties: -------------------------------------------------------------------------------- 1 | bindPanoramaShortcut_title = Utiliser le raccourci Ctrl/Cmd-Maj-E 2 | enableAlphabeticSort_title = Activer le tri alphabétique 3 | groupUndoCloseTimeout_title = Délai de fermeture du groupe 4 | groupUndoCloseTimeout_description = Délai d'attente (en secondes) avant la fermeture effective du groupe. 5 | 6 | add_group = Ajouter un groupe 7 | panelButton_label = Groupes d'onglets 8 | unnamed_group = Groupe sans nom 9 | close_group_prompt_title = Fermer le groupe 10 | close_group_prompt_message = Voulez-vous fermer ce groupe ? 11 | -------------------------------------------------------------------------------- /src/locale/nl-NL.properties: -------------------------------------------------------------------------------- 1 | bindPanoramaShortcut_title = Sneltoets Ctrl/Cmd-Shift-E activeren 2 | enableAlphabeticSort_title = Alfabetische sortering activeren 3 | groupUndoCloseTimeout_title = Vertraging op sluiten van groepen 4 | groupUndoCloseTimeout_description = Vertraging in seconden waarna de groep gesloten zal worden 5 | 6 | add_group = Nieuwe groep toevoegen 7 | panelButton_label = Tabbladen groeperen 8 | unnamed_group = Niet-benoemde groep 9 | close_group_prompt_title = Groep sluiten 10 | close_group_prompt_message = Wilt u deze groep sluiten? 11 | -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "tabgroups@schub.io", 3 | "name": "tabgroups", 4 | "version": "0.6.0-dev", 5 | "title": "Simplified Tab Groups", 6 | "description": "An add-on to replace the removed TabView/TabGroups/Panorama.", 7 | "author": "Dennis Schubert ", 8 | "license": "MIT", 9 | "main": "index.js", 10 | "icon": "chrome://simplified-tabgroups/content/icons/extension-32.png", 11 | "permissions": { 12 | "private-browsing": true 13 | }, 14 | "preferences": [ 15 | { 16 | "name": "bindPanoramaShortcut", 17 | "title": "Listen to Ctrl/Cmd-Shift-E", 18 | "type": "bool", 19 | "value": true 20 | }, 21 | { 22 | "name": "bindNavigationShortcut", 23 | "title": "Listen to Ctrl-~ and Ctrl-Shift-~", 24 | "type": "bool", 25 | "value": true 26 | }, 27 | { 28 | "name": "enableAlphabeticSort", 29 | "title": "Enable alphabetic sorting", 30 | "type": "bool", 31 | "value": false 32 | }, 33 | { 34 | "name": "groupCloseTimeout", 35 | "title": "Closing group delay", 36 | "description": "Delay in seconds to wait before the group gets actually closed.", 37 | "type": "integer", 38 | "value": 3 39 | } 40 | ] 41 | } 42 | --------------------------------------------------------------------------------