Merge pull request #564 from FoseFx/fosefx-eslint

ESLint
This commit is contained in:
Ajay Ramachandran 2020-12-14 13:11:56 -05:00 committed by GitHub
commit 59826aae6d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1819 additions and 642 deletions

33
.eslintrc.js Normal file
View file

@ -0,0 +1,33 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
],
parser: "@typescript-eslint/parser",
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 12,
sourceType: "module",
},
plugins: ["react", "@typescript-eslint"],
rules: {
// TODO: Remove warn rules when not needed anymore
"@typescript-eslint/no-this-alias": "warn",
"no-self-assign": "warn",
"@typescript-eslint/no-empty-interface": "warn",
"@typescript-eslint/ban-types": "warn",
},
settings: {
react: {
version: "detect",
},
},
};

File diff suppressed because one or more lines are too long

1085
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -19,7 +19,11 @@
"@types/firefox-webext-browser": "70.0.1",
"@types/jest": "^24.0.23",
"@types/jquery": "^3.3.31",
"@typescript-eslint/eslint-plugin": "^4.9.1",
"@typescript-eslint/parser": "^4.9.1",
"copy-webpack-plugin": "^6.0.3",
"eslint": "^7.15.0",
"eslint-plugin-react": "^7.21.5",
"jest": "^26.4.0",
"rimraf": "^3.0.0",
"ts-jest": "^26.2.0",
@ -47,7 +51,9 @@
"dev": "npm run build:dev && concurrently \"npm run web-run\" \"npm run build:watch\"",
"dev:firefox": "npm run build:dev:firefox && concurrently \"npm run web-run:firefox\" \"npm run build:watch:firefox\"",
"clean": "rimraf dist",
"test": "npx jest"
"test": "npx jest",
"lint": "eslint src",
"lint:fix": "eslint src --fix"
},
"repository": {
"type": "git",

View file

@ -529,81 +529,81 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@types/prop-types
15.7.3 <https://github.com/DefinitelyTyped/DefinitelyTyped>
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
******************************
@types/react
16.9.22 <https://github.com/DefinitelyTyped/DefinitelyTyped>
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
******************************
@types/react-dom
16.9.5 <https://github.com/DefinitelyTyped/DefinitelyTyped>
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
******************************
@ -637,26 +637,26 @@ SOFTWARE.
@webassemblyjs/floating-point-hex-parser
1.8.5 <https://github.com/xtuc/webassemblyjs>
MIT License
Copyright (c) 2017 Mauro Bringolf
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
MIT License
Copyright (c) 2017 Mauro Bringolf
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************
@ -1855,7 +1855,7 @@ THE SOFTWARE.
async-each
1.0.3 <https://github.com/paulmillr/async-each>
license: MIT
authors: Paul Miller <https://paulmillr.com/>
authors: Paul Miller (https://paulmillr.com/)
******************************
@ -2640,6 +2640,34 @@ The above copyright notice and this permission notice shall be included in all c
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************
bindings
1.5.0 <https://github.com/TooTallNate/node-bindings>
(The MIT License)
Copyright (c) 2012 Nathan Rajlich &lt;nathan@tootallnate.net&gt;
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************
bluebird
@ -3760,13 +3788,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
color-name
1.1.3 <https://github.com/dfcreative/color-name>
The MIT License (MIT)
Copyright (c) 2015 Dmitry Ivanov
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
The MIT License (MIT)
Copyright (c) 2015 Dmitry Ivanov
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************
@ -3830,30 +3858,30 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
component-emitter
1.3.0 <https://github.com/component/emitter>
(The MIT License)
Copyright (c) 2014 Component contributors <dev@component.io>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
(The MIT License)
Copyright (c) 2014 Component contributors <dev@component.io>
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
******************************
@ -4918,6 +4946,32 @@ OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
USE OR PERFORMANCE OF THIS SOFTWARE.
******************************
file-uri-to-path
1.0.0 <https://github.com/TooTallNate/file-uri-to-path>
Copyright (c) 2014 Nathan Rajlich <nathan@tootallnate.net>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************
fill-range
@ -5153,6 +5207,34 @@ the licensed code:
DEALINGS IN THE SOFTWARE.
******************************
fsevents
2.1.3 <https://github.com/fsevents/fsevents>
MIT License
-----------
Copyright (C) 2010-2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
******************************
gensync
@ -6709,6 +6791,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
******************************
nan
2.14.1 <https://github.com/nodejs/nan>
The MIT License (MIT)
=====================
Copyright (c) 2018 NAN contributors
-----------------------------------
*NAN contributors listed at <https://github.com/nodejs/nan#contributors>*
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************
nanomatch
@ -8034,26 +8135,26 @@ authors: Mathias Bynens <https://mathiasbynens.be/>
regjsgen
0.2.0 <https://github.com/d10/regjsgen>
Copyright 2014 Benjamin Tan <demoneaux@gmail.com> (https://d10.github.io/)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Copyright 2014 Benjamin Tan <demoneaux@gmail.com> (https://d10.github.io/)
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************
@ -8350,7 +8451,7 @@ SOFTWARE.
run-queue
1.0.3 <https://github.com/iarna/run-queue>
license: ISC
authors: Rebecca Turner <me@re-becca.org>
authors: Rebecca Turner <me@re-becca.org> (http://re-becca.org/)
******************************
@ -9336,7 +9437,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
spdx-license-ids
3.0.5 <https://github.com/shinnn/spdx-license-ids>
license: CC0-1.0
authors: Shinnosuke Watanabe <https://github.com/shinnn>
authors: Shinnosuke Watanabe (https://github.com/shinnn)
******************************
@ -9961,61 +10062,61 @@ THE SOFTWARE.
tslib
1.10.0 <https://github.com/Microsoft/tslib>
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
******************************

View file

@ -1,17 +1,18 @@
import * as CompileConfig from "../config.json";
import Config from "./config";
import { Registration } from "./types";
// Make the config public for debugging purposes
(<any> window).SB = Config;
import Utils from "./utils";
var utils = new Utils({
const utils = new Utils({
registerFirefoxContentScript,
unregisterFirefoxContentScript
});
// Used only on Firefox, which does not support non persistent background pages.
var contentScriptRegistrations = {};
const contentScriptRegistrations = {};
// Register content script if needed
if (utils.isFirefox()) {
@ -58,6 +59,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, callback) {
iconUrl: "./icons/LogoSponsorBlocker256px.png"
});
}
break;
case "registerContentScript":
registerFirefoxContentScript(request);
return false;
@ -93,8 +95,8 @@ chrome.runtime.onInstalled.addListener(function (object) {
*
* @param {JSON} options
*/
function registerFirefoxContentScript(options) {
let oldRegistration = contentScriptRegistrations[options.id];
function registerFirefoxContentScript(options: Registration) {
const oldRegistration = contentScriptRegistrations[options.id];
if (oldRegistration) oldRegistration.unregister();
browser.contentScripts.register({
@ -124,10 +126,10 @@ async function submitVote(type: number, UUID: string, category: string) {
Config.config.userID = userID;
}
let typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
const typeSection = (type !== undefined) ? "&type=" + type : "&category=" + category;
//publish this vote
let response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection);
const response = await asyncRequestToServer("POST", "/api/voteOnSponsorTime?UUID=" + UUID + "&userID=" + userID + typeSection);
if (response.ok) {
return {
@ -149,7 +151,7 @@ async function submitVote(type: number, UUID: string, category: string) {
}
async function asyncRequestToServer(type: string, address: string, data = {}) {
let serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
return await (sendRequestToCustomServer(type, serverAddress + address, data));
}
@ -165,8 +167,8 @@ async function sendRequestToCustomServer(type: string, url: string, data = {}) {
// If GET, convert JSON to parameters
if (type.toLowerCase() === "get") {
for (const key in data) {
let seperator = url.includes("?") ? "&" : "?";
let value = (typeof(data[key]) === "string") ? data[key]: JSON.stringify(data[key]);
const seperator = url.includes("?") ? "&" : "?";
const value = (typeof(data[key]) === "string") ? data[key]: JSON.stringify(data[key]);
url += seperator + key + "=" + value;
}

View file

@ -23,7 +23,7 @@ class CategoryChooserComponent extends React.Component<CategoryChooserProps, Cat
}
}
render() {
render(): React.ReactElement {
return (
<table id="categoryChooserTable"
className="categoryChooserTable">
@ -55,7 +55,7 @@ class CategoryChooserComponent extends React.Component<CategoryChooserProps, Cat
}
getCategorySkipOptions(): JSX.Element[] {
let elements: JSX.Element[] = [];
const elements: JSX.Element[] = [];
for (const category of CompileConfig.categoryList) {
elements.push(

View file

@ -29,7 +29,7 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
}
}
render() {
render(): React.ReactElement {
let defaultOption = "disable";
// Set the default opton properly
for (const categorySelection of Config.config.categorySelections) {
@ -145,9 +145,9 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
}
getCategorySkipOptions(): JSX.Element[] {
let elements: JSX.Element[] = [];
const elements: JSX.Element[] = [];
let optionNames = ["disable", "showOverlay", "manualSkip", "autoSkip"];
const optionNames = ["disable", "showOverlay", "manualSkip", "autoSkip"];
for (const optionName of optionNames) {
elements.push(
@ -160,7 +160,7 @@ class CategorySkipOptionsComponent extends React.Component<CategorySkipOptionsPr
return elements;
}
setColorState(event: React.FormEvent<HTMLInputElement>, preview: boolean) {
setColorState(event: React.FormEvent<HTMLInputElement>, preview: boolean): void {
if (preview) {
this.setState({
previewColor: event.currentTarget.value

View file

@ -35,7 +35,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
constructor(props: NoticeProps) {
super(props);
let maxCountdownTime = () => {
const maxCountdownTime = () => {
if (this.props.maxCountdownTime) return this.props.maxCountdownTime();
else return 4;
};
@ -60,12 +60,12 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
}
}
componentDidMount() {
componentDidMount(): void {
this.startCountdown();
}
render() {
let noticeStyle: React.CSSProperties = {
render(): React.ReactElement {
const noticeStyle: React.CSSProperties = {
zIndex: this.props.zIndex || (50 + this.amountOfPreviousNotices)
}
@ -124,19 +124,19 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
);
}
timerMouseEnter() {
timerMouseEnter(): void {
if (this.state.countdownManuallyPaused) return;
this.pauseCountdown();
}
timerMouseLeave() {
timerMouseLeave(): void {
if (this.state.countdownManuallyPaused) return;
this.startCountdown();
}
toggleManualPause() {
toggleManualPause(): void {
this.setState({
countdownManuallyPaused: !this.state.countdownManuallyPaused
}, () => {
@ -149,10 +149,10 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
}
//called every second to lower the countdown before hiding the notice
countdown() {
countdown(): void {
if (!this.props.timed) return;
let countdownTime = this.state.countdownTime - 1;
const countdownTime = this.state.countdownTime - 1;
if (countdownTime <= 0) {
//remove this from setInterval
@ -166,7 +166,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
if (countdownTime == 3) {
//start fade out animation
let notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
const notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
notice.style.removeProperty("animation");
notice.classList.add("sponsorSkipNoticeFadeOut");
}
@ -176,7 +176,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
})
}
pauseCountdown() {
pauseCountdown(): void {
if (!this.props.timed) return;
//remove setInterval
@ -190,12 +190,12 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
});
//remove the fade out class if it exists
let notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
const notice = document.getElementById("sponsorSkipNotice" + this.idSuffix);
notice.classList.remove("sponsorSkipNoticeFadeOut");
notice.style.animation = "none";
}
startCountdown() {
startCountdown(): void {
if (!this.props.timed) return;
//if it has already started, don't start it again
@ -209,7 +209,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
this.countdownInterval = setInterval(this.countdown.bind(this), 1000);
}
resetCountdown() {
resetCountdown(): void {
if (!this.props.timed) return;
this.setState({
@ -221,36 +221,36 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
/**
* @param silent If true, the close listener will not be called
*/
close(silent?: boolean) {
close(silent?: boolean): void {
//remove setInterval
if (this.countdownInterval !== null) clearInterval(this.countdownInterval);
if (!silent) this.props.closeListener();
}
changeNoticeTitle(title) {
changeNoticeTitle(title: string): void {
this.setState({
noticeTitle: title
});
}
addNoticeInfoMessage(message: string, message2: string = "") {
addNoticeInfoMessage(message: string, message2 = ""): void {
//TODO: Replace
let previousInfoMessage = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix);
const previousInfoMessage = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix);
if (previousInfoMessage != null) {
//remove it
document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage);
}
let previousInfoMessage2 = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix + "2");
const previousInfoMessage2 = document.getElementById("sponsorTimesInfoMessage" + this.idSuffix + "2");
if (previousInfoMessage2 != null) {
//remove it
document.getElementById("sponsorSkipNotice" + this.idSuffix).removeChild(previousInfoMessage2);
}
//add info
let thanksForVotingText = document.createElement("p");
const thanksForVotingText = document.createElement("p");
thanksForVotingText.id = "sponsorTimesInfoMessage" + this.idSuffix;
thanksForVotingText.className = "sponsorTimesInfoMessage";
thanksForVotingText.innerText = message;
@ -259,7 +259,7 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
document.querySelector("#sponsorSkipNotice" + this.idSuffix + " > tbody").insertBefore(thanksForVotingText, document.getElementById("sponsorSkipNoticeSpacer" + this.idSuffix));
if (message2 !== undefined) {
let thanksForVotingText2 = document.createElement("p");
const thanksForVotingText2 = document.createElement("p");
thanksForVotingText2.id = "sponsorTimesInfoMessage" + this.idSuffix + "2";
thanksForVotingText2.className = "sponsorTimesInfoMessage";
thanksForVotingText2.innerText = message2;
@ -270,4 +270,4 @@ class NoticeComponent extends React.Component<NoticeProps, NoticeState> {
}
}
export default NoticeComponent;
export default NoticeComponent;

View file

@ -16,8 +16,8 @@ class NoticeTextSelectionComponent extends React.Component<NoticeTextSelectionPr
super(props);
}
render() {
let style: React.CSSProperties = {};
render(): React.ReactElement {
const style: React.CSSProperties = {};
if (this.props.onClick) {
style.cursor = "pointer";
style.textDecoration = "underline"

View file

@ -4,7 +4,7 @@ import Config from "../config"
import { ContentContainer, SponsorHideType, SponsorTime } from "../types";
import Utils from "../utils";
var utils = new Utils();
const utils = new Utils();
import NoticeComponent from "./NoticeComponent";
import NoticeTextSelectionComponent from "./NoticeTextSectionComponent";
@ -31,7 +31,7 @@ export interface SkipNoticeState {
noticeTitle: string;
messages: string[];
messageOnClick: (event: React.MouseEvent) => any;
messageOnClick: (event: React.MouseEvent) => unknown;
countdownTime: number;
maxCountdownTime: () => number;
@ -56,7 +56,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
amountOfPreviousNotices: number;
audio: HTMLAudioElement;
idSuffix: any;
idSuffix: string;
noticeRef: React.MutableRefObject<NoticeComponent>;
categoryOptionRef: React.RefObject<HTMLSelectElement>;
@ -74,7 +74,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
this.contentContainer = props.contentContainer;
this.audio = null;
let categoryName = chrome.i18n.getMessage(this.segments.length > 1 ? "multipleSegments"
const categoryName = chrome.i18n.getMessage(this.segments.length > 1 ? "multipleSegments"
: "category_" + this.segments[0].category + "_short") || chrome.i18n.getMessage("category_" + this.segments[0].category);
let noticeTitle = categoryName + " " + chrome.i18n.getMessage("skipped");
if (!this.autoSkip) {
@ -98,7 +98,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
if (this.amountOfPreviousNotices > 0) {
//another notice exists
let previousNotice = document.getElementsByClassName("sponsorSkipNotice")[0];
const previousNotice = document.getElementsByClassName("sponsorSkipNotice")[0];
previousNotice.classList.add("secondSkipNotice")
}
@ -129,15 +129,15 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
}
componentDidMount() {
componentDidMount(): void {
if (Config.config.audioNotificationOnSkip && this.audio) {
this.audio.volume = this.contentContainer().v.volume * 0.1;
if (this.autoSkip) this.audio.play();
}
}
render() {
let noticeStyle: React.CSSProperties = {
render(): React.ReactElement {
const noticeStyle: React.CSSProperties = {
zIndex: 50 + this.amountOfPreviousNotices
}
if (this.contentContainer().onMobileYouTube) {
@ -286,7 +286,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
getSubmissionChooser(): JSX.Element[] {
let elements: JSX.Element[] = [];
const elements: JSX.Element[] = [];
for (let i = 0; i < this.segments.length; i++) {
elements.push(
@ -301,7 +301,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
return elements;
}
prepAction(action: SkipNoticeAction) {
prepAction(action: SkipNoticeAction): void {
if (this.segments.length === 1) {
this.performAction(0, action);
} else {
@ -321,7 +321,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
);
}
let elements: JSX.Element[] = [];
const elements: JSX.Element[] = [];
for (let i = 0; i < this.state.messages.length; i++) {
elements.push(
@ -341,7 +341,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
*
* @param index
*/
performAction(index: number, action?: SkipNoticeAction) {
performAction(index: number, action?: SkipNoticeAction): void {
switch (action ?? this.state.actionState) {
case SkipNoticeAction.None:
break;
@ -364,7 +364,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
});
}
adjustDownvotingState(value: boolean) {
adjustDownvotingState(value: boolean): void {
if (!value) this.clearConfigListener();
this.setState({
@ -373,14 +373,14 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
});
}
clearConfigListener() {
clearConfigListener(): void {
if (this.configListener) {
Config.configListeners.splice(Config.configListeners.indexOf(this.configListener), 1);
this.configListener = null;
}
}
openCategoryChooser() {
openCategoryChooser(): void {
// Add as a config listener
this.configListener = () => this.forceUpdate();
Config.configListeners.push(this.configListener);
@ -396,8 +396,8 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
});
}
getCategoryOptions() {
let elements = [];
getCategoryOptions(): React.ReactElement[] {
const elements = [];
for (const category of Config.config.categorySelections) {
elements.push(
@ -421,7 +421,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
return elements;
}
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>) {
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>): void {
// See if show more categories was pressed
if (event.target.value === "moreCategories") {
// Open options page
@ -433,14 +433,14 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
}
unskip(index: number) {
unskip(index: number): void {
this.contentContainer().unskipSponsorTime(this.segments[index]);
this.unskippedMode(index, chrome.i18n.getMessage("reskip"));
}
/** Sets up notice to be not skipped yet */
unskippedMode(index: number, buttonText: string) {
unskippedMode(index: number, buttonText: string): void {
//setup new callback and reset countdown
this.setState(this.getUnskippedModeInfo(index, buttonText), () => {
this.noticeRef.current.resetCountdown();
@ -448,10 +448,10 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
getUnskippedModeInfo(index: number, buttonText: string) {
let self = this;
let maxCountdownTime = function() {
let sponsorTime = self.segments[index];
let duration = Math.round((sponsorTime.segment[1] - self.contentContainer().v.currentTime) * (1 / self.contentContainer().v.playbackRate));
const self = this;
const maxCountdownTime = function() {
const sponsorTime = self.segments[index];
const duration = Math.round((sponsorTime.segment[1] - self.contentContainer().v.currentTime) * (1 / self.contentContainer().v.playbackRate));
return Math.max(duration, 4);
};
@ -468,7 +468,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
}
reskip(index: number) {
reskip(index: number): void {
this.contentContainer().reskipSponsorTime(this.segments[index]);
//reset countdown
@ -488,7 +488,7 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
}
afterVote(segment: SponsorTime, type: number, category: string) {
afterVote(segment: SponsorTime, type: number, category: string): void {
this.addVoteButtonInfo(chrome.i18n.getMessage("voted"));
if (type === 0) {
@ -508,32 +508,32 @@ class SkipNoticeComponent extends React.Component<SkipNoticeProps, SkipNoticeSta
}
}
setNoticeInfoMessageWithOnClick(onClick: (event: React.MouseEvent) => any, ...messages: string[]) {
setNoticeInfoMessageWithOnClick(onClick: (event: React.MouseEvent) => any, ...messages: string[]): void {
this.setState({
messages,
messageOnClick: (event) => onClick(event)
});
}
setNoticeInfoMessage(...messages: string[]) {
setNoticeInfoMessage(...messages: string[]): void {
this.setState({
messages
});
}
addVoteButtonInfo(message) {
addVoteButtonInfo(message): void {
this.setState({
thanksForVotingText: message
});
}
resetVoteButtonInfo() {
resetVoteButtonInfo(): void {
this.setState({
thanksForVotingText: null
});
}
closeListener() {
closeListener(): void {
this.clearConfigListener();
this.props.closeListener();

View file

@ -6,7 +6,7 @@ import * as CompileConfig from "../../config.json";
import Utils from "../utils";
import { ContentContainer, SponsorTime } from "../types";
import SubmissionNoticeComponent from "./SubmissionNoticeComponent";
var utils = new Utils();
const utils = new Utils();
export interface SponsorTimeEditProps {
index: number,
@ -44,7 +44,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
};
}
componentDidMount() {
componentDidMount(): void {
// Prevent inputs from triggering key events
document.getElementById("sponsorTimesContainer" + this.idSuffix).addEventListener('keydown', function (event) {
event.stopPropagation();
@ -57,14 +57,14 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
}
}
componentWillUnmount() {
componentWillUnmount(): void {
if (this.configUpdateListener) {
Config.configListeners.splice(Config.configListeners.indexOf(this.configUpdate.bind(this)), 1);
}
}
render() {
let style: React.CSSProperties = {
render(): React.ReactElement {
const style: React.CSSProperties = {
textAlign: "center"
};
@ -74,7 +74,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
// This method is required to get !important
// https://stackoverflow.com/a/45669262/1985387
let oldYouTubeDarkStyles = (node) => {
const oldYouTubeDarkStyles = (node) => {
if (node) {
node.style.setProperty("color", "black", "important");
node.style.setProperty("text-shadow", "none", "important");
@ -83,8 +83,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
// Create time display
let timeDisplay: JSX.Element;
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
let segment = sponsorTime.segment;
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
const segment = sponsorTime.segment;
if (this.state.editing) {
timeDisplay = (
<div id={"sponsorTimesContainer" + this.idSuffix}
@ -102,7 +102,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
type="text"
value={this.state.sponsorTimeEdits[0]}
onChange={(e) => {
let sponsorTimeEdits = this.state.sponsorTimeEdits;
const sponsorTimeEdits = this.state.sponsorTimeEdits;
sponsorTimeEdits[0] = e.target.value;
this.setState({sponsorTimeEdits});
@ -121,7 +121,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
type="text"
value={this.state.sponsorTimeEdits[1]}
onChange={(e) => {
let sponsorTimeEdits = this.state.sponsorTimeEdits;
const sponsorTimeEdits = this.state.sponsorTimeEdits;
sponsorTimeEdits[1] = e.target.value;
this.setState({sponsorTimeEdits});
@ -215,8 +215,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
);
}
getCategoryOptions() {
let elements = [(
getCategoryOptions(): React.ReactElement[] {
const elements = [(
<option value={"chooseACategory"}
key={"chooseACategory"}>
{chrome.i18n.getMessage("chooseACategory")}
@ -245,7 +245,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
return elements;
}
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>) {
categorySelectionChange(event: React.ChangeEvent<HTMLSelectElement>): void {
// See if show more categories was pressed
if (event.target.value === "moreCategories") {
// Open options page
@ -259,16 +259,16 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
this.saveEditTimes();
}
setTimeToNow(index: number) {
setTimeToNow(index: number): void {
this.setTimeTo(index, this.props.contentContainer().getRealCurrentTime());
}
setTimeToEnd() {
setTimeToEnd(): void {
this.setTimeTo(1, this.props.contentContainer().v.duration);
}
setTimeTo(index: number, time: number) {
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
setTimeTo(index: number, time: number): void {
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
sponsorTime.segment[index] =
time;
@ -287,7 +287,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
this.saveEditTimes();
} else {
let sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
const sponsorTime = this.props.contentContainer().sponsorTimesSubmitting[this.props.index];
this.setState({
editing: true,
@ -302,8 +302,8 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
utils.getFormattedTime(sponsorTime.segment[1], true)];
}
saveEditTimes() {
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
saveEditTimes(): void {
const sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
if (this.state.editing) {
const startTime = utils.getFormattedTimeToSeconds(this.state.sponsorTimeEdits[0]);
@ -323,26 +323,26 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
}
previewTime(): void {
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
let index = this.props.index;
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
const index = this.props.index;
let skipTime = sponsorTimes[index].segment[0];
const skipTime = sponsorTimes[index].segment[0];
this.props.contentContainer().previewTime(skipTime - 2);
}
inspectTime(): void {
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
let index = this.props.index;
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
const index = this.props.index;
let skipTime = sponsorTimes[index].segment[0];
const skipTime = sponsorTimes[index].segment[0];
this.props.contentContainer().previewTime(skipTime + 0.000001, false);
}
deleteTime(): void {
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
let index = this.props.index;
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
const index = this.props.index;
//if it is not a complete sponsor time
if (sponsorTimes[index].segment.length < 2) {
@ -369,7 +369,7 @@ class SponsorTimeEditComponent extends React.Component<SponsorTimeEditProps, Spo
}
}
configUpdate() {
configUpdate(): void {
this.forceUpdate();
}
}

View file

@ -10,7 +10,7 @@ export interface SubmissionNoticeProps {
// Contains functions and variables from the content script needed by the skip notice
contentContainer: ContentContainer;
callback: () => any;
callback: () => unknown;
closeListener: () => void
}
@ -25,7 +25,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
// Contains functions and variables from the content script needed by the skip notice
contentContainer: ContentContainer;
callback: () => any;
callback: () => unknown;
noticeRef: React.MutableRefObject<NoticeComponent>;
timeEditRefs: React.RefObject<SponsorTimeEditComponent>[];
@ -39,7 +39,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
this.contentContainer = props.contentContainer;
this.callback = props.callback;
let noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle");
const noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle");
// Setup state
this.state = {
@ -49,7 +49,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
}
}
componentDidMount() {
componentDidMount(): void {
// Catch and rerender when the video size changes
//TODO: Use ResizeObserver when it is supported in TypeScript
this.videoObserver = new MutationObserver(() => {
@ -61,13 +61,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
});
}
componentWillUnmount() {
componentWillUnmount(): void {
if (this.videoObserver) {
this.videoObserver.disconnect();
}
}
render() {
render(): React.ReactElement {
return (
<NoticeComponent noticeTitle={this.state.noticeTitle}
idSuffix={this.state.idSuffix}
@ -114,13 +114,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
}
getSponsorTimeMessages(): JSX.Element[] | JSX.Element {
let elements: JSX.Element[] = [];
const elements: JSX.Element[] = [];
this.timeEditRefs = [];
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
for (let i = 0; i < sponsorTimes.length; i++) {
let timeRef = React.createRef<SponsorTimeEditComponent>();
const timeRef = React.createRef<SponsorTimeEditComponent>();
elements.push(
<SponsorTimeEditComponent key={i}
@ -139,7 +139,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
}
getMessageBoxes(): JSX.Element[] | JSX.Element {
let elements: JSX.Element[] = [];
const elements: JSX.Element[] = [];
for (let i = 0; i < this.state.messages.length; i++) {
elements.push(
@ -153,7 +153,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
return elements;
}
cancel() {
cancel(): void {
this.noticeRef.current.close(true);
this.contentContainer().resetSponsorSubmissionNotice();
@ -161,13 +161,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
this.props.closeListener();
}
submit() {
submit(): void {
// save all items
for (const ref of this.timeEditRefs) {
ref.current.saveEditTimes();
}
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
const sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
for (const sponsorTime of sponsorTimesSubmitting) {
if (sponsorTime.category === "chooseACategory") {
alert(chrome.i18n.getMessage("youMustSelectACategory"));

View file

@ -132,7 +132,7 @@ class SBMap<T, U> extends Map {
}
}
var Config: SBObject = {
const Config: SBObject = {
/**
* Callback function when an option is updated
*/
@ -149,7 +149,7 @@ var Config: SBObject = {
skipCount: 0,
sponsorTimesContributed: 0,
submissionCountSinceCategories: 0,
showTimeWithSkips: true,
showTimeWithSkips: true,
unsubmittedWarning: true,
disableSkipping: false,
trackViewCount: true,
@ -286,7 +286,7 @@ function configProxy(): any {
}
});
var handler: ProxyHandler<any> = {
const handler: ProxyHandler<any> = {
set(obj, prop, value) {
Config.localConfig[prop] = value;
@ -298,7 +298,7 @@ function configProxy(): any {
},
get(obj, prop): any {
let data = Config.localConfig[prop];
const data = Config.localConfig[prop];
return obj[prop] || data;
},
@ -314,7 +314,7 @@ function configProxy(): any {
return new Proxy({handler}, handler);
}
function fetchConfig() {
function fetchConfig(): Promise<void> {
return new Promise((resolve, reject) => {
chrome.storage.sync.get(null, function(items) {
Config.localConfig = <SBConfig> <unknown> items; // Data is ready
@ -351,7 +351,7 @@ function migrateOldFormats(config: SBConfig) {
if (config.whitelistedChannels.length > 0 &&
(config.whitelistedChannels[0] == null || config.whitelistedChannels[0].includes("/"))) {
const channelURLFixer = async() => {
let newChannelList: string[] = [];
const newChannelList: string[] = [];
for (const item of config.whitelistedChannels) {
if (item != null) {
if (item.includes("/channel/")) {
@ -360,7 +360,7 @@ function migrateOldFormats(config: SBConfig) {
// Replace channel URL with channelID
let response = await utils.asyncRequestToCustomServer("GET", "https://sponsor.ajay.app/invidious/api/v1/channels/" + item.split("/")[2] + "?fields=authorId");
const response = await utils.asyncRequestToCustomServer("GET", "https://sponsor.ajay.app/invidious/api/v1/channels/" + item.split("/")[2] + "?fields=authorId");
if (response.ok) {
newChannelList.push((JSON.parse(response.responseText)).authorId);
@ -408,9 +408,9 @@ function migrateOldFormats(config: SBConfig) {
// Otherwise junk data
if (Array.isArray(jsonData)) {
let oldMap = new Map(jsonData);
const oldMap = new Map(jsonData);
oldMap.forEach((sponsorTimes: number[][], key) => {
let segmentTimes: SponsorTime[] = [];
const segmentTimes: SponsorTime[] = [];
for (const segment of sponsorTimes) {
segmentTimes.push({
segment: segment,
@ -442,7 +442,7 @@ async function setupConfig() {
// Reset config
function resetConfig() {
Config.config = Config.defaults;
};
}
function convertJSON(): void {
Object.keys(Config.localConfig).forEach(key => {
@ -453,17 +453,17 @@ function convertJSON(): void {
// Add defaults
function addDefaults() {
for (const key in Config.defaults) {
if(!Config.localConfig.hasOwnProperty(key)) {
Config.localConfig[key] = Config.defaults[key];
if(!Object.prototype.hasOwnProperty.call(Config.localConfig, key)) {
Config.localConfig[key] = Config.defaults[key];
} else if (key === "barTypes") {
for (const key2 in Config.defaults[key]) {
if(!Config.localConfig[key].hasOwnProperty(key2)) {
if(!Object.prototype.hasOwnProperty.call(Config.localConfig[key], key2)) {
Config.localConfig[key][key2] = Config.defaults[key][key2];
}
}
}
}
};
}
// Sync config
setupConfig();

View file

@ -4,7 +4,7 @@ import { SponsorTime, CategorySkipOption, CategorySelection, VideoID, SponsorHid
import { ContentContainer } from "./types";
import Utils from "./utils";
var utils = new Utils();
const utils = new Utils();
import runThePopup from "./popup";
@ -17,80 +17,80 @@ import SubmissionNotice from "./render/SubmissionNotice";
utils.wait(() => Config.config !== null, 5000, 10).then(addCSS);
//was sponsor data found when doing SponsorsLookup
var sponsorDataFound = false;
var previousVideoID: VideoID = null;
let sponsorDataFound = false;
let previousVideoID: VideoID = null;
//the actual sponsorTimes if loaded and UUIDs associated with them
var sponsorTimes: SponsorTime[] = null;
let sponsorTimes: SponsorTime[] = null;
//what video id are these sponsors for
var sponsorVideoID: VideoID = null;
let sponsorVideoID: VideoID = null;
// JSON video info
var videoInfo: any = null;
let videoInfo: any = null;
//the channel this video is about
var channelID;
let channelID;
// Skips are scheduled to ensure precision.
// Skips are rescheduled every seeking event.
// Skips are canceled every seeking event
var currentSkipSchedule: NodeJS.Timeout = null;
var seekListenerSetUp = false
let currentSkipSchedule: NodeJS.Timeout = null;
let seekListenerSetUp = false
/** @type {Array[boolean]} Has the sponsor been skipped */
var sponsorSkipped: boolean[] = [];
let sponsorSkipped: boolean[] = [];
//the video
var video: HTMLVideoElement;
let video: HTMLVideoElement;
var onInvidious;
var onMobileYouTube;
let onInvidious;
let onMobileYouTube;
//the video id of the last preview bar update
var lastPreviewBarUpdate;
let lastPreviewBarUpdate;
//whether the duration listener listening for the duration changes of the video has been setup yet
var durationListenerSetUp = false;
let durationListenerSetUp = false;
// Is the video currently being switched
var switchingVideos = null;
let switchingVideos = null;
// Used by the play and playing listeners to make sure two aren't
// called at the same time
var lastCheckTime = 0;
var lastCheckVideoTime = -1;
let lastCheckTime = 0;
let lastCheckVideoTime = -1;
//is this channel whitelised from getting sponsors skipped
var channelWhitelisted = false;
let channelWhitelisted = false;
// create preview bar
var previewBar: PreviewBar = null;
let previewBar: PreviewBar = null;
//the player controls on the YouTube player
var controls = null;
let controls = null;
// Direct Links after the config is loaded
utils.wait(() => Config.config !== null, 1000, 1).then(() => videoIDChange(getYouTubeVideoID(document.URL)));
//the amount of times the sponsor lookup has retried
//this only happens if there is an error
var sponsorLookupRetries = 0;
let sponsorLookupRetries = 0;
//if showing the start sponsor button or the end sponsor button on the player
var showingStartSponsor = true;
let showingStartSponsor = true;
//the sponsor times being prepared to be submitted
var sponsorTimesSubmitting: SponsorTime[] = [];
let sponsorTimesSubmitting: SponsorTime[] = [];
//becomes true when isInfoFound is called
//this is used to close the popup on YouTube when the other popup opens
var popupInitialised = false;
let popupInitialised = false;
var submissionNotice: SubmissionNotice = null;
let submissionNotice: SubmissionNotice = null;
// If there is an advert playing (or about to be played), this is true
var isAdPlaying = false;
let isAdPlaying = false;
// Contains all of the functions and variables needed by the skip notice
var skipNoticeContentContainer: ContentContainer = () => ({
const skipNoticeContentContainer: ContentContainer = () => ({
vote,
dontShowNoticeAgain,
unskipSponsorTime,
@ -218,13 +218,13 @@ if (!Config.configListeners.includes(contentConfigUpdateListener)) {
//check for hotkey pressed
document.onkeydown = function(e: KeyboardEvent){
var key = e.key;
const key = e.key;
let video = document.getElementById("movie_player");
const video = document.getElementById("movie_player");
let startSponsorKey = Config.config.startSponsorKeybind;
const startSponsorKey = Config.config.startSponsorKeybind;
let submitKey = Config.config.submitKeybind;
const submitKey = Config.config.submitKeybind;
//is the video in focus, otherwise they could be typing a comment
if (document.activeElement === video) {
@ -296,7 +296,7 @@ async function videoIDChange(id) {
}
if (isUnlisted()) {
let shouldContinue = confirm(chrome.i18n.getMessage("confirmPrivacy"));
const shouldContinue = confirm(chrome.i18n.getMessage("confirmPrivacy"));
if(!shouldContinue) return;
}
}
@ -323,7 +323,7 @@ async function videoIDChange(id) {
//warn them if they had unsubmitted times
if (previousVideoID != null) {
//get the sponsor times from storage
let sponsorTimes = Config.config.segmentTimes.get(previousVideoID);
const sponsorTimes = Config.config.segmentTimes.get(previousVideoID);
if (sponsorTimes != undefined && sponsorTimes.length > 0 && new URL(document.URL).host !== "music.youtube.com") {
//warn them that they have unsubmitted sponsor times
chrome.runtime.sendMessage({
@ -347,7 +347,7 @@ async function videoIDChange(id) {
//make sure everything is properly added
updateVisibilityOfPlayerControlsButton().then(() => {
//see if the onvideo control image needs to be changed
let segments = Config.config.segmentTimes.get(sponsorVideoID);
const segments = Config.config.segmentTimes.get(sponsorVideoID);
if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length >= 2) {
changeStartSponsorButton(true, true);
} else if (segments != null && segments.length > 0 && segments[segments.length - 1].segment.length < 2) {
@ -368,7 +368,7 @@ async function videoIDChange(id) {
}
function handleMobileControlsMutations(): void {
let mobileYouTubeSelector = ".progress-bar-background";
const mobileYouTubeSelector = ".progress-bar-background";
updateVisibilityOfPlayerControlsButton().then((createdButtons) => {
if (createdButtons) {
@ -470,14 +470,14 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
if (currentTime === undefined || currentTime === null) currentTime = video.currentTime;
let skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
const skipInfo = getNextSkipIndex(currentTime, includeIntersectingSegments, includeNonIntersectingSegments);
if (skipInfo.index === -1) return;
let currentSkip = skipInfo.array[skipInfo.index];
let skipTime: number[] = [currentSkip.segment[0], skipInfo.array[skipInfo.endIndex].segment[1]];
let timeUntilSponsor = skipTime[0] - currentTime;
let videoID = sponsorVideoID;
const currentSkip = skipInfo.array[skipInfo.index];
const skipTime: number[] = [currentSkip.segment[0], skipInfo.array[skipInfo.endIndex].segment[1]];
const timeUntilSponsor = skipTime[0] - currentTime;
const videoID = sponsorVideoID;
// Find all indexes in between the start and end
let skippingSegments = [skipInfo.array[skipInfo.index]];
@ -496,7 +496,7 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
if (utils.getCategorySelection(currentSkip.category)?.option === CategorySkipOption.ShowOverlay
&& skipInfo.array !== sponsorTimesSubmitting) return;
let skippingFunction = () => {
const skippingFunction = () => {
let forcedSkipTime: number = null;
let forcedIncludeIntersectingSegments = false;
let forcedIncludeNonIntersectingSegments = true;
@ -529,7 +529,7 @@ function startSponsorSchedule(includeIntersectingSegments = false, currentTime?:
* This makes sure the videoID is still correct and if the sponsorTime is included
*/
function incorrectVideoCheck(videoID?: string, sponsorTime?: SponsorTime): boolean {
let currentVideoID = getYouTubeVideoID(document.URL);
const currentVideoID = getYouTubeVideoID(document.URL);
if (currentVideoID !== (videoID || sponsorVideoID) || (sponsorTime && (!sponsorTimes || !sponsorTimes.includes(sponsorTime)) && !sponsorTimesSubmitting.includes(sponsorTime))) {
// Something has really gone wrong
console.error("[SponsorBlock] The videoID recorded when trying to skip is different than what it should be.");
@ -614,7 +614,7 @@ async function sponsorsLookup(id: string) {
//made true once a setTimeout has been created to try again after a server error
let recheckStarted = false;
// Create categories list
let categories: string[] = [];
const categories: string[] = [];
for (const categorySelection of Config.config.categorySelections) {
categories.push(categorySelection.name);
}
@ -649,7 +649,7 @@ async function sponsorsLookup(id: string) {
}
}
let recievedSegments: SponsorTime[] = result;
const recievedSegments: SponsorTime[] = result;
if (!recievedSegments.length) {
console.error("[SponsorBlock] Server returned malformed response: " + JSON.stringify(recievedSegments));
return;
@ -713,7 +713,7 @@ function retryFetch(id: string): void {
//check if this video was uploaded recently
utils.wait(() => !!videoInfo).then(() => {
let dateUploaded = videoInfo?.microformat?.playerMicroformatRenderer?.uploadDate;
const dateUploaded = videoInfo?.microformat?.playerMicroformatRenderer?.uploadDate;
//if less than 3 days old
if (Date.now() - new Date(dateUploaded).getTime() < 259200000) {
@ -733,7 +733,7 @@ function retryFetch(id: string): void {
function startSkipScheduleCheckingForStartSponsors() {
if (!switchingVideos) {
// See if there are any starting sponsors
let startingSponsor: number = -1;
let startingSponsor = -1;
for (const time of sponsorTimes) {
if (time.segment[0] <= video.currentTime && time.segment[0] > startingSponsor && time.segment[1] > video.currentTime) {
startingSponsor = time.segment[0];
@ -763,7 +763,7 @@ function startSkipScheduleCheckingForStartSponsors() {
function getVideoInfo() {
sendRequestToCustomServer('GET', "https://www.youtube.com/get_video_info?video_id=" + sponsorVideoID, function(xmlhttp, error) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
let decodedData = decodeURIComponent(xmlhttp.responseText).match(/player_response=([^&]*)/)[1];
const decodedData = decodeURIComponent(xmlhttp.responseText).match(/player_response=([^&]*)/)[1];
if (!decodedData) {
console.error("[SB] Failed at getting video info from YouTube.");
return;
@ -803,7 +803,7 @@ function getYouTubeVideoID(url: string) {
//Get ID from searchParam
if (urlObject.searchParams.has("v") && ["/watch", "/watch/"].includes(urlObject.pathname) || urlObject.pathname.startsWith("/tv/watch")) {
let id = urlObject.searchParams.get("v");
const id = urlObject.searchParams.get("v");
return id.length == 11 ? id : false;
} else if (urlObject.pathname.startsWith("/embed/")) {
try {
@ -836,10 +836,10 @@ function updatePreviewBar() {
let localSponsorTimes = sponsorTimes;
if (localSponsorTimes == null) localSponsorTimes = [];
let allSponsorTimes = localSponsorTimes.concat(sponsorTimesSubmitting);
const allSponsorTimes = localSponsorTimes.concat(sponsorTimesSubmitting);
//create an array of the sponsor types
let types = [];
const types = [];
for (let i = 0; i < localSponsorTimes.length; i++) {
if (localSponsorTimes[i].hidden === SponsorHideType.Visible) {
types.push(localSponsorTimes[i].category);
@ -872,7 +872,7 @@ function whitelistCheck() {
}
//see if this is a whitelisted channel
let whitelistedChannels = Config.config.whitelistedChannels;
const whitelistedChannels = Config.config.whitelistedChannels;
if (whitelistedChannels != undefined && whitelistedChannels.includes(channelID)) {
channelWhitelisted = true;
@ -888,17 +888,17 @@ function whitelistCheck() {
function getNextSkipIndex(currentTime: number, includeIntersectingSegments: boolean, includeNonIntersectingSegments: boolean):
{array: SponsorTime[], index: number, endIndex: number, openNotice: boolean} {
let sponsorStartTimes = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments);
let sponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, true, true);
const sponsorStartTimes = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments);
const sponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimes, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, true, true);
let minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime));
let endTimeIndex = getLatestEndTimeIndex(sponsorTimes, minSponsorTimeIndex);
const minSponsorTimeIndex = sponsorStartTimes.indexOf(Math.min(...sponsorStartTimesAfterCurrentTime));
const endTimeIndex = getLatestEndTimeIndex(sponsorTimes, minSponsorTimeIndex);
let previewSponsorStartTimes = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments);
let previewSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, false, false);
const previewSponsorStartTimes = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments);
const previewSponsorStartTimesAfterCurrentTime = getStartTimes(sponsorTimesSubmitting, includeIntersectingSegments, includeNonIntersectingSegments, currentTime, false, false);
let minPreviewSponsorTimeIndex = previewSponsorStartTimes.indexOf(Math.min(...previewSponsorStartTimesAfterCurrentTime));
let previewEndTimeIndex = getLatestEndTimeIndex(sponsorTimesSubmitting, minPreviewSponsorTimeIndex);
const minPreviewSponsorTimeIndex = previewSponsorStartTimes.indexOf(Math.min(...previewSponsorStartTimesAfterCurrentTime));
const previewEndTimeIndex = getLatestEndTimeIndex(sponsorTimesSubmitting, minPreviewSponsorTimeIndex);
if ((minPreviewSponsorTimeIndex === -1 && minSponsorTimeIndex !== -1) ||
sponsorStartTimes[minSponsorTimeIndex] < previewSponsorStartTimes[minPreviewSponsorTimeIndex]) {
@ -931,7 +931,7 @@ function getNextSkipIndex(currentTime: number, includeIntersectingSegments: bool
* @param index Index of the given sponsor
* @param hideHiddenSponsors
*/
function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideHiddenSponsors: boolean = true): number {
function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideHiddenSponsors = true): number {
// Only combine segments for AutoSkip
if (index == -1 ||
utils.getCategorySelection(sponsorTimes[index].category)?.option !== CategorySkipOption.AutoSkip) return index;
@ -940,8 +940,8 @@ function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideH
let latestEndTimeIndex = index;
for (let i = 0; i < sponsorTimes?.length; i++) {
let currentSegment = sponsorTimes[i].segment;
let latestEndTime = sponsorTimes[latestEndTimeIndex].segment[1];
const currentSegment = sponsorTimes[i].segment;
const latestEndTime = sponsorTimes[latestEndTimeIndex].segment[1];
if (currentSegment[0] <= latestEndTime && currentSegment[1] > latestEndTime
&& (!hideHiddenSponsors || sponsorTimes[i].hidden === SponsorHideType.Visible)
@ -970,10 +970,10 @@ function getLatestEndTimeIndex(sponsorTimes: SponsorTime[], index: number, hideH
* the current time, but end after
*/
function getStartTimes(sponsorTimes: SponsorTime[], includeIntersectingSegments: boolean, includeNonIntersectingSegments: boolean,
minimum?: number, onlySkippableSponsors: boolean = false, hideHiddenSponsors: boolean = false): number[] {
minimum?: number, onlySkippableSponsors = false, hideHiddenSponsors = false): number[] {
if (sponsorTimes === null) return [];
let startTimes: number[] = [];
const startTimes: number[] = [];
for (let i = 0; i < sponsorTimes?.length; i++) {
if ((minimum === undefined
@ -1006,7 +1006,7 @@ function previewTime(time: number, unpause = true) {
//skip from the start time to the end time for a certain index sponsor time
function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: SponsorTime[], openNotice: boolean) {
// There will only be one submission if it is manual skip
let autoSkip: boolean = utils.getCategorySelection(skippingSegments[0].category)?.option === CategorySkipOption.AutoSkip;
const autoSkip: boolean = utils.getCategorySelection(skippingSegments[0].category)?.option === CategorySkipOption.AutoSkip;
if ((autoSkip || sponsorTimesSubmitting.includes(skippingSegments[0])) && v.currentTime !== skipTime[1]) {
// Fix for looped videos not working when skipping to the end #426
@ -1031,7 +1031,7 @@ function skipToTime(v: HTMLVideoElement, skipTime: number[], skippingSegments: S
let isPreviewSegment = false;
for (const segment of skippingSegments) {
let index = sponsorTimes.indexOf(segment);
const index = sponsorTimes.indexOf(segment);
if (index !== -1 && !sponsorSkipped[index]) {
utils.asyncRequestToServer("POST", "/api/viewedVideoSponsorTime?UUID=" + segment.UUID);
@ -1066,7 +1066,7 @@ function createButton(baseID, title, callback, imageName, isDraggable=false): bo
if (document.getElementById(baseID + "Button") != null) return false;
// Button HTML
let newButton = document.createElement("button");
const newButton = document.createElement("button");
newButton.draggable = isDraggable;
newButton.id = baseID + "Button";
newButton.classList.add("playerButton");
@ -1077,7 +1077,7 @@ function createButton(baseID, title, callback, imageName, isDraggable=false): bo
});
// Image HTML
let newButtonImage = document.createElement("img");
const newButtonImage = document.createElement("img");
newButton.draggable = isDraggable;
newButtonImage.id = baseID + "Image";
newButtonImage.className = "playerButtonImage";
@ -1092,8 +1092,8 @@ function createButton(baseID, title, callback, imageName, isDraggable=false): bo
return true;
}
function getControls(): HTMLElement | boolean {
let controlsSelectors = [
function getControls(): HTMLElement | false {
const controlsSelectors = [
// YouTube
".ytp-right-controls",
// Mobile YouTube
@ -1103,7 +1103,7 @@ function getControls(): HTMLElement | boolean {
]
for (const controlsSelector of controlsSelectors) {
let controls = document.querySelectorAll(controlsSelector);
const controls = document.querySelectorAll(controlsSelector);
if (controls && controls.length > 0) {
return <HTMLElement> controls[controls.length - 1];
@ -1111,13 +1111,13 @@ function getControls(): HTMLElement | boolean {
}
return false;
};
}
//adds all the player controls buttons
async function createButtons(): Promise<boolean> {
if (onMobileYouTube) return;
let result = await utils.wait(getControls).catch();
const result = await utils.wait(getControls).catch();
//set global controls variable
controls = result;
@ -1138,7 +1138,7 @@ async function updateVisibilityOfPlayerControlsButton(): Promise<boolean> {
//not on a proper video yet
if (!sponsorVideoID) return false;
let createdButtons = await createButtons();
const createdButtons = await createButtons();
if (Config.config.hideVideoPlayerControls || onInvidious) {
document.getElementById("startSponsorButton").style.display = "none";
@ -1168,8 +1168,8 @@ async function updateVisibilityOfPlayerControlsButton(): Promise<boolean> {
*/
function getRealCurrentTime(): number {
// Used to check if replay button
let playButtonSVGData = document.querySelector(".ytp-play-button")?.querySelector(".ytp-svg-fill")?.getAttribute("d");
let replaceSVGData = "M 18,11 V 7 l -5,5 5,5 v -4 c 3.3,0 6,2.7 6,6 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 h -2 c 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 0,-4.4 -3.6,-8 -8,-8 z";
const playButtonSVGData = document.querySelector(".ytp-play-button")?.querySelector(".ytp-svg-fill")?.getAttribute("d");
const replaceSVGData = "M 18,11 V 7 l -5,5 5,5 v -4 c 3.3,0 6,2.7 6,6 0,3.3 -2.7,6 -6,6 -3.3,0 -6,-2.7 -6,-6 h -2 c 0,4.4 3.6,8 8,8 4.4,0 8,-3.6 8,-8 0,-4.4 -3.6,-8 -8,-8 z";
if (playButtonSVGData === replaceSVGData) {
// At the end of the video
@ -1204,8 +1204,8 @@ function startSponsorClicked() {
updateSponsorTimesSubmitting(false)
}
function updateSponsorTimesSubmitting(getFromConfig: boolean = true) {
let segmentTimes = Config.config.segmentTimes.get(sponsorVideoID);
function updateSponsorTimesSubmitting(getFromConfig = true) {
const segmentTimes = Config.config.segmentTimes.get(sponsorVideoID);
//see if this data should be saved in the sponsorTimesSubmitting variable
if (getFromConfig && segmentTimes != undefined) {
@ -1234,7 +1234,7 @@ async function changeStartSponsorButton(showStartSponsor, uploadButtonVisible) {
if(!sponsorVideoID) return false;
//if it isn't visible, there is no data
let shouldHide = (uploadButtonVisible && !(Config.config.hideDeleteButtonPlayerControls || onInvidious)) ? "unset" : "none"
const shouldHide = (uploadButtonVisible && !(Config.config.hideDeleteButtonPlayerControls || onInvidious)) ? "unset" : "none"
document.getElementById("deleteButton").style.display = shouldHide;
if (showStartSponsor) {
@ -1275,7 +1275,7 @@ function openInfoMenu() {
sendRequestToCustomServer('GET', chrome.extension.getURL("popup.html"), function(xmlhttp) {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
let popup = document.createElement("div");
const popup = document.createElement("div");
popup.id = "sponsorBlockPopupContainer";
let htmlData = xmlhttp.responseText;
@ -1288,7 +1288,7 @@ function openInfoMenu() {
popup.innerHTML = htmlData;
//close button
let closeButton = document.createElement("div");
const closeButton = document.createElement("div");
closeButton.innerText = chrome.i18n.getMessage("closePopup");
closeButton.classList.add("smallLink");
closeButton.setAttribute("align", "center");
@ -1299,7 +1299,7 @@ function openInfoMenu() {
//add the close button
popup.prepend(closeButton);
let parentNodes = document.querySelectorAll("#secondary");
const parentNodes = document.querySelectorAll("#secondary");
let parentNode = null;
for (let i = 0; i < parentNodes.length; i++) {
if (parentNodes[i].firstElementChild !== null) {
@ -1313,10 +1313,10 @@ function openInfoMenu() {
//make the logo source not 404
//query selector must be used since getElementByID doesn't work on a node and this isn't added to the document yet
let logo = <HTMLImageElement> popup.querySelector("#sponsorBlockPopupLogo");
let settings = <HTMLImageElement> popup.querySelector("#sbPopupIconSettings");
let edit = <HTMLImageElement> popup.querySelector("#sbPopupIconEdit");
let check = <HTMLImageElement> popup.querySelector("#sbPopupIconCheck");
const logo = <HTMLImageElement> popup.querySelector("#sponsorBlockPopupLogo");
const settings = <HTMLImageElement> popup.querySelector("#sbPopupIconSettings");
const edit = <HTMLImageElement> popup.querySelector("#sbPopupIconEdit");
const check = <HTMLImageElement> popup.querySelector("#sbPopupIconCheck");
logo.src = chrome.extension.getURL("icons/LogoSponsorBlocker256px.png");
settings.src = chrome.extension.getURL("icons/settings.svg");
edit.src = chrome.extension.getURL("icons/pencil.svg");
@ -1332,7 +1332,7 @@ function openInfoMenu() {
}
function closeInfoMenu() {
let popup = document.getElementById("sponsorBlockPopupContainer");
const popup = document.getElementById("sponsorBlockPopupContainer");
if (popup != null) {
popup.remove();
@ -1347,12 +1347,12 @@ function clearSponsorTimes() {
//it can't update to this info yet
closeInfoMenu();
let currentVideoID = sponsorVideoID;
const currentVideoID = sponsorVideoID;
let sponsorTimes = Config.config.segmentTimes.get(currentVideoID);
const sponsorTimes = Config.config.segmentTimes.get(currentVideoID);
if (sponsorTimes != undefined && sponsorTimes.length > 0) {
let confirmMessage = chrome.i18n.getMessage("clearThis") + getSegmentsMessage(sponsorTimes)
const confirmMessage = chrome.i18n.getMessage("clearThis") + getSegmentsMessage(sponsorTimes)
+ "\n" + chrome.i18n.getMessage("confirmMSG")
if(!confirm(confirmMessage)) return;
@ -1377,7 +1377,7 @@ function vote(type: number, UUID: string, category?: string, skipNotice?: SkipNo
skipNotice.setNoticeInfoMessage.bind(skipNotice)();
}
let sponsorIndex = utils.getSponsorIndexFromUUID(sponsorTimes, UUID);
const sponsorIndex = utils.getSponsorIndexFromUUID(sponsorTimes, UUID);
// Don't vote for preview sponsors
if (sponsorIndex == -1 || sponsorTimes[sponsorIndex].UUID === null) return;
@ -1420,7 +1420,7 @@ function vote(type: number, UUID: string, category?: string, skipNotice?: SkipNo
//Closes all notices that tell the user that a sponsor was just skipped
function closeAllSkipNotices(){
let notices = document.getElementsByClassName("sponsorSkipNotice");
const notices = document.getElementsByClassName("sponsorSkipNotice");
for (let i = 0; i < notices.length; i++) {
notices[i].remove();
}
@ -1456,7 +1456,7 @@ function submitSponsorTimes() {
//it can't update to this info yet
closeInfoMenu();
let currentVideoID = sponsorVideoID;
const currentVideoID = sponsorVideoID;
if (sponsorTimesSubmitting !== undefined && sponsorTimesSubmitting.length > 0) {
submissionNotice = new SubmissionNotice(skipNoticeContentContainer, sendSubmitMessage);
@ -1466,7 +1466,7 @@ function submitSponsorTimes() {
//send the message to the background js
//called after all the checks have been made that it's okay to do so
async function sendSubmitMessage(){
async function sendSubmitMessage(): Promise<void> {
//add loading animation
(<HTMLImageElement> document.getElementById("submitImage")).src = chrome.extension.getURL("icons/PlayerUploadIconSponsorBlocker256px.png");
document.getElementById("submitButton").style.animation = "rotate 1s 0s infinite";
@ -1485,7 +1485,7 @@ async function sendSubmitMessage(){
if (Config.config.minDuration > 0) {
for (let i = 0; i < sponsorTimesSubmitting.length; i++) {
if (sponsorTimesSubmitting[i].segment[1] - sponsorTimesSubmitting[i].segment[0] < Config.config.minDuration) {
let confirmShort = chrome.i18n.getMessage("shortCheck") + "\n\n" +
const confirmShort = chrome.i18n.getMessage("shortCheck") + "\n\n" +
getSegmentsMessage(sponsorTimesSubmitting);
if(!confirm(confirmShort)) return;
@ -1493,7 +1493,7 @@ async function sendSubmitMessage(){
}
}
let response = await utils.asyncRequestToServer("POST", "/api/skipSegments", {
const response = await utils.asyncRequestToServer("POST", "/api/skipSegments", {
videoID: sponsorVideoID,
userID: Config.config.userID,
segments: sponsorTimesSubmitting
@ -1501,11 +1501,11 @@ async function sendSubmitMessage(){
if (response.status === 200) {
//hide loading message
let submitButton = document.getElementById("submitButton");
const submitButton = document.getElementById("submitButton");
submitButton.style.animation = "rotate 1s";
//finish this animation
//when the animation is over, hide the button
let animationEndListener = function() {
const animationEndListener = function() {
changeStartSponsorButton(true, false);
submitButton.style.animation = "none";
@ -1579,10 +1579,10 @@ function isUnlisted(): boolean {
function addCSS() {
if (!utils.isFirefox() && Config.config.invidiousInstances.includes(new URL(document.URL).host)) {
window.addEventListener("DOMContentLoaded", () => {
let head = document.getElementsByTagName("head")[0];
const head = document.getElementsByTagName("head")[0];
for (const file of utils.css) {
let fileref = document.createElement("link");
const fileref = document.createElement("link");
fileref.rel = "stylesheet";
fileref.type = "text/css";
@ -1595,7 +1595,7 @@ function addCSS() {
}
function sendRequestToCustomServer(type, fullAddress, callback) {
let xmlhttp = new XMLHttpRequest();
const xmlhttp = new XMLHttpRequest();
xmlhttp.open(type, fullAddress, true);
@ -1617,7 +1617,7 @@ function sendRequestToCustomServer(type, fullAddress, callback) {
* Update the isAdPlaying flag and hide preview bar/controls if ad is playing
*/
function updateAdFlag() {
let wasAdPlaying = isAdPlaying;
const wasAdPlaying = isAdPlaying;
isAdPlaying = document.getElementsByClassName('ad-showing').length > 0;
if(wasAdPlaying != isAdPlaying) {
@ -1641,10 +1641,10 @@ function showTimeWithoutSkips(allSponsorTimes): void {
}
// YouTube player time display
let display = document.getElementsByClassName("ytp-time-display notranslate")[0];
const display = document.getElementsByClassName("ytp-time-display notranslate")[0];
if (!display) return;
let formatedTime = utils.getFormattedTime(video.duration - skipDuration);
const formatedTime = utils.getFormattedTime(video.duration - skipDuration);
const durationID = "sponsorBlockDurationAfterSkips";
let duration = document.getElementById(durationID);

View file

@ -7,7 +7,7 @@
import Config from "../config";
import Utils from "../utils";
let utils = new Utils();
const utils = new Utils();
class PreviewBar {
container: HTMLUListElement;
@ -16,9 +16,9 @@ class PreviewBar {
onInvidious: boolean;
timestamps: number[][];
types: string;
types: string[];
constructor(parent, onMobileYouTube, onInvidious) {
constructor(parent: any, onMobileYouTube: boolean, onInvidious: boolean) {
this.container = document.createElement('ul');
this.container.id = 'previewbar';
this.parent = parent;
@ -31,15 +31,15 @@ class PreviewBar {
this.setupHoverText();
}
setupHoverText() {
setupHoverText(): void {
if (this.onMobileYouTube || this.onInvidious) return;
let seekBar = document.querySelector(".ytp-progress-bar-container");
const seekBar = document.querySelector(".ytp-progress-bar-container");
// Create label placeholder
let tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper");
let titleTooltip = document.querySelector(".ytp-tooltip-title");
let categoryTooltip = document.createElement("div");
const tooltipTextWrapper = document.querySelector(".ytp-tooltip-text-wrapper");
const titleTooltip = document.querySelector(".ytp-tooltip-title");
const categoryTooltip = document.createElement("div");
categoryTooltip.className = "sbHidden ytp-tooltip-title";
categoryTooltip.id = "sponsor-block-category-tooltip"
@ -64,12 +64,12 @@ class PreviewBar {
return;
}
let tooltips = document.querySelectorAll(".ytp-tooltip-text");
const tooltips = document.querySelectorAll(".ytp-tooltip-text");
for (const tooltip of tooltips) {
let splitData = tooltip.textContent.split(":");
const splitData = tooltip.textContent.split(":");
if (splitData.length === 2 && !isNaN(parseInt(splitData[0])) && !isNaN(parseInt(splitData[1]))) {
// Add label
let timeInSeconds = parseInt(splitData[0]) * 60 + parseInt(splitData[1]);
const timeInSeconds = parseInt(splitData[0]) * 60 + parseInt(splitData[1]);
// Find category at that location
let category = null;
@ -112,7 +112,7 @@ class PreviewBar {
});
}
updatePosition(parent) {
updatePosition(parent: any): void {
//below the seek bar
// this.parent.insertAdjacentElement("afterEnd", this.container);
@ -129,15 +129,15 @@ class PreviewBar {
this.parent.insertAdjacentElement("afterBegin", this.container);
}
updateColor(segment, color, opacity) {
let bars = <NodeListOf<HTMLElement>> document.querySelectorAll('[data-vs-segment-type=' + segment + ']');
for (let bar of bars) {
updateColor(segment: string, color: string, opacity: string): void {
const bars = <NodeListOf<HTMLElement>> document.querySelectorAll('[data-vs-segment-type=' + segment + ']');
for (const bar of bars) {
bar.style.backgroundColor = color;
bar.style.opacity = opacity;
}
}
set(timestamps, types, duration) {
set(timestamps: number[][], types: string[], duration: number): void {
while (this.container.firstChild) {
this.container.removeChild(this.container.firstChild);
}
@ -158,7 +158,7 @@ class PreviewBar {
width = (timestamps[i][1] - timestamps[i][0]) / duration * 100;
width = Math.floor(width * 100) / 100;
let bar = this.createBar();
const bar = this.createBar();
bar.setAttribute('data-vs-segment-type', types[i]);
bar.style.backgroundColor = Config.config.barTypes[types[i]].color;
@ -171,14 +171,14 @@ class PreviewBar {
}
}
createBar() {
let bar = document.createElement('li');
createBar(): HTMLLIElement {
const bar = document.createElement('li');
bar.classList.add('previewbar');
bar.innerHTML = '&nbsp;';
return bar;
}
remove() {
remove(): void {
this.container.remove();
this.container = undefined;
}

View file

@ -6,7 +6,7 @@ import * as CompileConfig from "../config.json";
import Utils from "./utils";
import CategoryChooser from "./render/CategoryChooser";
var utils = new Utils();
const utils = new Utils();
window.addEventListener('DOMContentLoaded', init);
@ -27,19 +27,19 @@ async function init() {
await utils.wait(() => Config.config !== null);
// Set all of the toggle options to the correct option
let optionsContainer = document.getElementById("options");
let optionsElements = optionsContainer.querySelectorAll("*");
const optionsContainer = document.getElementById("options");
const optionsElements = optionsContainer.querySelectorAll("*");
for (let i = 0; i < optionsElements.length; i++) {
switch (optionsElements[i].getAttribute("option-type")) {
case "toggle":
let option = optionsElements[i].getAttribute("sync-option");
let optionResult = Config.config[option];
case "toggle": {
const option = optionsElements[i].getAttribute("sync-option");
const optionResult = Config.config[option];
let checkbox = optionsElements[i].querySelector("input");
let reverse = optionsElements[i].getAttribute("toggle-type") === "reverse";
const checkbox = optionsElements[i].querySelector("input");
const reverse = optionsElements[i].getAttribute("toggle-type") === "reverse";
let confirmMessage = optionsElements[i].getAttribute("confirm-message");
const confirmMessage = optionsElements[i].getAttribute("confirm-message");
if (optionResult != undefined) {
checkbox.checked = optionResult;
@ -76,7 +76,7 @@ async function init() {
// Enable the notice
Config.config["dontShowNotice"] = false;
let showNoticeSwitch = <HTMLInputElement> document.querySelector("[sync-option='dontShowNotice'] > label > label > input");
const showNoticeSwitch = <HTMLInputElement> document.querySelector("[sync-option='dontShowNotice'] > label > label > input");
showNoticeSwitch.checked = true;
}
@ -84,19 +84,20 @@ async function init() {
}
});
break;
case "text-change":
let textChangeOption = optionsElements[i].getAttribute("sync-option");
let textChangeInput = <HTMLInputElement> optionsElements[i].querySelector(".option-text-box");
}
case "text-change": {
const textChangeOption = optionsElements[i].getAttribute("sync-option");
const textChangeInput = <HTMLInputElement> optionsElements[i].querySelector(".option-text-box");
let textChangeSetButton = <HTMLElement> optionsElements[i].querySelector(".text-change-set");
const textChangeSetButton = <HTMLElement> optionsElements[i].querySelector(".text-change-set");
textChangeInput.value = Config.config[textChangeOption];
textChangeSetButton.addEventListener("click", async () => {
// See if anything extra must be done
switch (textChangeOption) {
case "serverAddress":
let result = validateServerAddress(textChangeInput.value);
case "serverAddress": {
const result = validateServerAddress(textChangeInput.value);
if (result !== null) {
textChangeInput.value = result;
@ -106,7 +107,7 @@ async function init() {
// Permission needed on Firefox
if (utils.isFirefox()) {
let permissionSuccess = await new Promise((resolve, reject) => {
const permissionSuccess = await new Promise((resolve, reject) => {
chrome.permissions.request({
origins: [textChangeInput.value + "/"],
permissions: []
@ -117,13 +118,14 @@ async function init() {
}
break;
}
}
Config.config[textChangeOption] = textChangeInput.value;
});
// Reset to the default if needed
let textChangeResetButton = <HTMLElement> optionsElements[i].querySelector(".text-change-reset");
const textChangeResetButton = <HTMLElement> optionsElements[i].querySelector(".text-change-reset");
textChangeResetButton.addEventListener("click", () => {
if (!confirm(chrome.i18n.getMessage("areYouSureReset"))) return;
@ -133,11 +135,12 @@ async function init() {
});
break;
case "private-text-change":
let button = optionsElements[i].querySelector(".trigger-button");
}
case "private-text-change": {
const button = optionsElements[i].querySelector(".trigger-button");
button.addEventListener("click", () => activatePrivateTextChange(<HTMLElement> optionsElements[i]));
let privateTextChangeOption = optionsElements[i].getAttribute("sync-option");
const privateTextChangeOption = optionsElements[i].getAttribute("sync-option");
// See if anything extra must be done
switch (privateTextChangeOption) {
case "invidiousInstances":
@ -145,8 +148,9 @@ async function init() {
}
break;
case "button-press":
let actionButton = optionsElements[i].querySelector(".trigger-button");
}
case "button-press": {
const actionButton = optionsElements[i].querySelector(".trigger-button");
switch(optionsElements[i].getAttribute("sync-option")) {
case "copyDebugInformation":
@ -155,19 +159,21 @@ async function init() {
}
break;
case "keybind-change":
let keybindButton = optionsElements[i].querySelector(".trigger-button");
}
case "keybind-change": {
const keybindButton = optionsElements[i].querySelector(".trigger-button");
keybindButton.addEventListener("click", () => activateKeybindChange(<HTMLElement> optionsElements[i]));
break;
case "display":
}
case "display":{
updateDisplayElement(<HTMLElement> optionsElements[i])
break;
case "number-change":
let numberChangeOption = optionsElements[i].getAttribute("sync-option");
let configValue = Config.config[numberChangeOption];
let numberInput = optionsElements[i].querySelector("input");
}
case "number-change": {
const numberChangeOption = optionsElements[i].getAttribute("sync-option");
const configValue = Config.config[numberChangeOption];
const numberInput = optionsElements[i].querySelector("input");
if (isNaN(configValue) || configValue < 0) {
numberInput.value = Config.defaults[numberChangeOption];
@ -180,6 +186,7 @@ async function init() {
});
break;
}
case "react-CategoryChooserComponent":
new CategoryChooser(optionsElements[i]);
break;
@ -196,8 +203,8 @@ async function init() {
* @param {String} element
*/
function optionsConfigUpdateListener(changes) {
let optionsContainer = document.getElementById("options");
let optionsElements = optionsContainer.querySelectorAll("*");
const optionsContainer = document.getElementById("options");
const optionsElements = optionsContainer.querySelectorAll("*");
for (let i = 0; i < optionsElements.length; i++) {
switch (optionsElements[i].getAttribute("option-type")) {
@ -213,8 +220,8 @@ function optionsConfigUpdateListener(changes) {
* @param element
*/
function updateDisplayElement(element: HTMLElement) {
let displayOption = element.getAttribute("sync-option")
let displayText = Config.config[displayOption];
const displayOption = element.getAttribute("sync-option")
const displayText = Config.config[displayOption];
element.innerText = displayText;
// See if anything extra must be run
@ -232,10 +239,10 @@ function updateDisplayElement(element: HTMLElement) {
* @param option
*/
function invidiousInstanceAddInit(element: HTMLElement, option: string) {
let textBox = <HTMLInputElement> element.querySelector(".option-text-box");
let button = element.querySelector(".trigger-button");
const textBox = <HTMLInputElement> element.querySelector(".option-text-box");
const button = element.querySelector(".trigger-button");
let setButton = element.querySelector(".text-change-set");
const setButton = element.querySelector(".text-change-set");
setButton.addEventListener("click", async function(e) {
if (textBox.value == "" || textBox.value.includes("/") || textBox.value.includes("http")) {
alert(chrome.i18n.getMessage("addInvidiousInstanceError"));
@ -248,7 +255,7 @@ function invidiousInstanceAddInit(element: HTMLElement, option: string) {
Config.config[option] = instanceList;
let checkbox = <HTMLInputElement> document.querySelector("#support-invidious input");
const checkbox = <HTMLInputElement> document.querySelector("#support-invidious input");
checkbox.checked = true;
invidiousOnClick(checkbox, "supportInvidious");
@ -261,7 +268,7 @@ function invidiousInstanceAddInit(element: HTMLElement, option: string) {
}
});
let resetButton = element.querySelector(".invidious-instance-reset");
const resetButton = element.querySelector(".invidious-instance-reset");
resetButton.addEventListener("click", function(e) {
if (confirm(chrome.i18n.getMessage("resetInvidiousInstanceAlert"))) {
// Set to a clone of the default
@ -298,7 +305,7 @@ function invidiousInit(checkbox: HTMLInputElement, option: string) {
* @param checkbox
* @param option
*/
async function invidiousOnClick(checkbox: HTMLInputElement, option: string) {
async function invidiousOnClick(checkbox: HTMLInputElement, option: string): Promise<void> {
return new Promise((resolve) => {
if (checkbox.checked) {
utils.setupExtraSitePermissions(function (granted) {
@ -323,20 +330,20 @@ async function invidiousOnClick(checkbox: HTMLInputElement, option: string) {
* @param element
*/
function activateKeybindChange(element: HTMLElement) {
let button = element.querySelector(".trigger-button");
const button = element.querySelector(".trigger-button");
if (button.classList.contains("disabled")) return;
button.classList.add("disabled");
let option = element.getAttribute("sync-option");
const option = element.getAttribute("sync-option");
let currentlySet = Config.config[option] !== null ? chrome.i18n.getMessage("keybindCurrentlySet") : "";
const currentlySet = Config.config[option] !== null ? chrome.i18n.getMessage("keybindCurrentlySet") : "";
let status = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status");
const status = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status");
status.innerText = chrome.i18n.getMessage("keybindDescription") + currentlySet;
if (Config.config[option] !== null) {
let statusKey = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status-key");
const statusKey = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status-key");
statusKey.innerText = Config.config[option];
}
@ -352,19 +359,19 @@ function activateKeybindChange(element: HTMLElement) {
* @param e
*/
function keybindKeyPressed(element: HTMLElement, e: KeyboardEvent) {
var key = e.key;
const key = e.key;
if (["Shift", "Control", "Meta", "Alt", "ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Tab"].indexOf(key) !== -1) {
// Wait for more
document.addEventListener("keydown", (e) => keybindKeyPressed(element, e), {once: true});
} else {
let button: HTMLElement = element.querySelector(".trigger-button");
let option = element.getAttribute("sync-option");
const button: HTMLElement = element.querySelector(".trigger-button");
const option = element.getAttribute("sync-option");
// Make sure keybind isn't used by the other listener
// TODO: If other keybindings are going to be added, we need a better way to find the other keys used.
let otherKeybind = (option === "startSponsorKeybind") ? Config.config['submitKeybind'] : Config.config['startSponsorKeybind'];
const otherKeybind = (option === "startSponsorKeybind") ? Config.config['submitKeybind'] : Config.config['startSponsorKeybind'];
if (key === otherKeybind) {
closeKeybindOption(element, button);
@ -381,10 +388,10 @@ function keybindKeyPressed(element: HTMLElement, e: KeyboardEvent) {
Config.config[option] = key;
let status = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status");
const status = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status");
status.innerText = chrome.i18n.getMessage("keybindDescriptionComplete");
let statusKey = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status-key");
const statusKey = <HTMLElement> element.querySelector(".option-hidden-section > .keybind-status-key");
statusKey.innerText = key;
button.classList.remove("disabled");
@ -408,13 +415,13 @@ function closeKeybindOption(element: HTMLElement, button: HTMLElement) {
* @param element
*/
function activatePrivateTextChange(element: HTMLElement) {
let button = element.querySelector(".trigger-button");
const button = element.querySelector(".trigger-button");
if (button.classList.contains("disabled")) return;
button.classList.add("disabled");
let textBox = <HTMLInputElement> element.querySelector(".option-text-box");
let option = element.getAttribute("sync-option");
const textBox = <HTMLInputElement> element.querySelector(".option-text-box");
const option = element.getAttribute("sync-option");
// See if anything extra must be done
switch (option) {
@ -427,21 +434,22 @@ function activatePrivateTextChange(element: HTMLElement) {
// See if anything extra must be done
switch (option) {
case "*":
let jsonData = JSON.parse(JSON.stringify(Config.localConfig));
case "*": {
const jsonData = JSON.parse(JSON.stringify(Config.localConfig));
// Fix segmentTimes data as it is destroyed from the JSON stringify
jsonData.segmentTimes = Config.encodeStoredItem(Config.localConfig.segmentTimes);
result = JSON.stringify(jsonData);
break;
}
}
textBox.value = result;
let setButton = element.querySelector(".text-change-set");
const setButton = element.querySelector(".text-change-set");
setButton.addEventListener("click", async () => {
let confirmMessage = element.getAttribute("confirm-message");
const confirmMessage = element.getAttribute("confirm-message");
if (confirmMessage === null || confirm(chrome.i18n.getMessage(confirmMessage))) {
@ -449,14 +457,14 @@ function activatePrivateTextChange(element: HTMLElement) {
switch (option) {
case "*":
try {
let newConfig = JSON.parse(textBox.value);
const newConfig = JSON.parse(textBox.value);
for (const key in newConfig) {
Config.config[key] = newConfig[key];
}
Config.convertJSON();
if (newConfig.supportInvidious) {
let checkbox = <HTMLInputElement> document.querySelector("#support-invidious > label > label > input");
const checkbox = <HTMLInputElement> document.querySelector("#support-invidious > label > label > input");
checkbox.checked = true;
await invidiousOnClick(checkbox, "supportInvidious");
@ -503,7 +511,7 @@ function validateServerAddress(input: string): string {
function copyDebugOutputToClipboard() {
// Build output debug information object
let output = {
const output = {
debug: {
userAgent: navigator.userAgent,
platform: navigator.platform,
@ -528,7 +536,7 @@ function copyDebugOutputToClipboard() {
.then(() => {
alert(chrome.i18n.getMessage("copyDebugInformationComplete"));
})
.catch(err => {
.catch((err) => {
alert(chrome.i18n.getMessage("copyDebugInformationFailed"));
});;
});
}

View file

@ -2,7 +2,7 @@ import Config from "./config";
import Utils from "./utils";
import { SponsorTime, SponsorHideType } from "./types";
var utils = new Utils();
const utils = new Utils();
interface MessageListener {
(request: any, sender: any, callback: (response: any) => void): void;
@ -38,14 +38,14 @@ class MessageHandler {
}
//make this a function to allow this to run on the content page
async function runThePopup(messageListener?: MessageListener) {
var messageHandler = new MessageHandler(messageListener);
async function runThePopup(messageListener?: MessageListener): Promise<void> {
const messageHandler = new MessageHandler(messageListener);
utils.localizeHtmlPage();
await utils.wait(() => Config.config !== null);
var PageElements: any = {};
const PageElements: any = {};
[
"sponsorblockPopup",
@ -126,7 +126,7 @@ async function runThePopup(messageListener?: MessageListener) {
let currentVideoID = null;
//show proper disable skipping button
let disableSkipping = Config.config.disableSkipping;
const disableSkipping = Config.config.disableSkipping;
if (disableSkipping != undefined && disableSkipping) {
PageElements.disableSkipping.style.display = "none";
PageElements.enableSkipping.style.display = "unset";
@ -135,7 +135,7 @@ async function runThePopup(messageListener?: MessageListener) {
//if the don't show notice again variable is true, an option to
// disable should be available
let dontShowNotice = Config.config.dontShowNotice;
const dontShowNotice = Config.config.dontShowNotice;
if (dontShowNotice != undefined && dontShowNotice) {
PageElements.showNoticeAgain.style.display = "unset";
}
@ -152,13 +152,13 @@ async function runThePopup(messageListener?: MessageListener) {
PageElements.sponsorTimesContributionsContainer.classList.remove("hidden");
//get the userID
let userID = Config.config.userID;
const userID = Config.config.userID;
if (userID != undefined) {
//there are probably some views on these submissions then
//get the amount of views from the sponsors submitted
utils.sendRequestToServer("GET", "/api/getViewsForUser?userID=" + userID, function(response) {
if (response.status == 200) {
let viewCount = JSON.parse(response.responseText).viewCount;
const viewCount = JSON.parse(response.responseText).viewCount;
if (viewCount != 0) {
if (viewCount > 1) {
PageElements.sponsorTimesViewsDisplayEndWord.innerText = chrome.i18n.getMessage("Segments");
@ -175,7 +175,7 @@ async function runThePopup(messageListener?: MessageListener) {
//get this time in minutes
utils.sendRequestToServer("GET", "/api/getSavedTimeForUser?userID=" + userID, function(response) {
if (response.status == 200) {
let minutesSaved = JSON.parse(response.responseText).timeSaved;
const minutesSaved = JSON.parse(response.responseText).timeSaved;
if (minutesSaved != 0) {
if (minutesSaved != 1) {
PageElements.sponsorTimesOthersTimeSavedEndWord.innerText = chrome.i18n.getMessage("minsLower");
@ -222,15 +222,15 @@ async function runThePopup(messageListener?: MessageListener) {
}, onTabs);
function onTabs(tabs) {
messageHandler.sendMessage(tabs[0].id, {message: 'getVideoID'}, function(result) {
if (result != undefined && result.videoID) {
currentVideoID = result.videoID;
loadTabData(tabs);
} else if (result == undefined && chrome.runtime.lastError) {
//this isn't a YouTube video then, or at least the content script is not loaded
displayNoVideo();
}
});
messageHandler.sendMessage(tabs[0].id, {message: 'getVideoID'}, function(result) {
if (result != undefined && result.videoID) {
currentVideoID = result.videoID;
loadTabData(tabs);
} else if (result == undefined && chrome.runtime.lastError) {
// this isn't a YouTube video then, or at least the content script is not loaded
displayNoVideo();
}
});
}
function loadTabData(tabs) {
@ -241,7 +241,7 @@ async function runThePopup(messageListener?: MessageListener) {
}
//load video times for this video
let sponsorTimesStorage = Config.config.segmentTimes.get(currentVideoID);
const sponsorTimesStorage = Config.config.segmentTimes.get(currentVideoID);
if (sponsorTimesStorage != undefined && sponsorTimesStorage.length > 0) {
if (sponsorTimesStorage[sponsorTimesStorage.length - 1] != undefined && sponsorTimesStorage[sponsorTimesStorage.length - 1].segment.length < 2) {
startTimeChosen = true;
@ -322,7 +322,7 @@ async function runThePopup(messageListener?: MessageListener) {
}
function startSponsorCallback(response) {
let sponsorTimesIndex = sponsorTimes.length - (startTimeChosen ? 1 : 0);
const sponsorTimesIndex = sponsorTimes.length - (startTimeChosen ? 1 : 0);
if (sponsorTimes[sponsorTimesIndex] == undefined) {
sponsorTimes[sponsorTimesIndex] = {
@ -334,7 +334,7 @@ async function runThePopup(messageListener?: MessageListener) {
sponsorTimes[sponsorTimesIndex].segment[startTimeChosen ? 1 : 0] = response.time;
let localStartTimeChosen = startTimeChosen;
const localStartTimeChosen = startTimeChosen;
Config.config.segmentTimes.set(currentVideoID, sponsorTimes);
//send a message to the client script
@ -363,19 +363,19 @@ async function runThePopup(messageListener?: MessageListener) {
if (request.sponsorTimes != undefined) {
// Sort list by start time
let segmentTimes = request.sponsorTimes
const segmentTimes = request.sponsorTimes
.sort((a, b) => a.segment[1] - b.segment[1])
.sort((a, b) => a.segment[0] - b.segment[0]);
//add them as buttons to the issue reporting container
let container = document.getElementById("issueReporterTimeButtons");
const container = document.getElementById("issueReporterTimeButtons");
for (let i = 0; i < segmentTimes.length; i++) {
let UUID = segmentTimes[i].UUID;
const UUID = segmentTimes[i].UUID;
let sponsorTimeButton = document.createElement("button");
const sponsorTimeButton = document.createElement("button");
sponsorTimeButton.className = "segmentTimeButton popupElement";
let prefix = chrome.i18n.getMessage("category_" + segmentTimes[i].category) + ": ";
const prefix = chrome.i18n.getMessage("category_" + segmentTimes[i].category) + ": ";
let extraInfo = "";
if (segmentTimes[i].hidden === SponsorHideType.Downvoted) {
@ -388,28 +388,28 @@ async function runThePopup(messageListener?: MessageListener) {
sponsorTimeButton.innerText = prefix + getFormattedTime(segmentTimes[i].segment[0]) + " " + chrome.i18n.getMessage("to") + " " + getFormattedTime(segmentTimes[i].segment[1]) + extraInfo;
let categoryColorCircle = document.createElement("span");
const categoryColorCircle = document.createElement("span");
categoryColorCircle.id = "sponsorTimesCategoryColorCircle" + UUID;
categoryColorCircle.style.backgroundColor = Config.config.barTypes[segmentTimes[i].category].color;
categoryColorCircle.classList.add("dot");
categoryColorCircle.classList.add("sponsorTimesCategoryColorCircle");
let votingButtons = document.createElement("div");
const votingButtons = document.createElement("div");
votingButtons.classList.add("votingButtons");
//thumbs up and down buttons
let voteButtonsContainer = document.createElement("div");
const voteButtonsContainer = document.createElement("div");
voteButtonsContainer.id = "sponsorTimesVoteButtonsContainer" + UUID;
voteButtonsContainer.setAttribute("align", "center");
voteButtonsContainer.style.display = "none"
let upvoteButton = document.createElement("img");
const upvoteButton = document.createElement("img");
upvoteButton.id = "sponsorTimesUpvoteButtonsContainer" + UUID;
upvoteButton.className = "voteButton";
upvoteButton.src = chrome.extension.getURL("icons/thumbs_up.svg");
upvoteButton.addEventListener("click", () => vote(1, UUID));
let downvoteButton = document.createElement("img");
const downvoteButton = document.createElement("img");
downvoteButton.id = "sponsorTimesDownvoteButtonsContainer" + UUID;
downvoteButton.className = "voteButton";
downvoteButton.src = chrome.extension.getURL("icons/thumbs_down.svg");
@ -425,12 +425,12 @@ async function runThePopup(messageListener?: MessageListener) {
});
// Will contain request status
let voteStatusContainer = document.createElement("div");
const voteStatusContainer = document.createElement("div");
voteStatusContainer.id = "sponsorTimesVoteStatusContainer" + UUID;
voteStatusContainer.classList.add("sponsorTimesVoteStatusContainer");
voteStatusContainer.style.display = "none";
let thanksForVotingText = document.createElement("div");
const thanksForVotingText = document.createElement("div");
thanksForVotingText.id = "sponsorTimesThanksForVotingText" + UUID;
thanksForVotingText.classList.add("sponsorTimesThanksForVotingText");
voteStatusContainer.appendChild(thanksForVotingText);
@ -553,13 +553,13 @@ async function runThePopup(messageListener?: MessageListener) {
}
function addVoteMessage(message, UUID) {
let voteButtonsContainer = document.getElementById("sponsorTimesVoteButtonsContainer" + UUID);
const voteButtonsContainer = document.getElementById("sponsorTimesVoteButtonsContainer" + UUID);
voteButtonsContainer.style.display = "none";
let voteStatusContainer = document.getElementById("sponsorTimesVoteStatusContainer" + UUID);
const voteStatusContainer = document.getElementById("sponsorTimesVoteStatusContainer" + UUID);
voteStatusContainer.style.removeProperty("display");
let thanksForVotingText = document.getElementById("sponsorTimesThanksForVotingText" + UUID);
const thanksForVotingText = document.getElementById("sponsorTimesThanksForVotingText" + UUID);
thanksForVotingText.innerText = message;
}
@ -587,15 +587,15 @@ async function runThePopup(messageListener?: MessageListener) {
//converts time in seconds to minutes:seconds
function getFormattedTime(seconds) {
let minutes = Math.floor(seconds / 60);
let secondsDisplayNumber = Math.round(seconds - minutes * 60);
const minutes = Math.floor(seconds / 60);
const secondsDisplayNumber = Math.round(seconds - minutes * 60);
let secondsDisplay = String(secondsDisplayNumber);
if (secondsDisplayNumber < 10) {
//add a zero
secondsDisplay = "0" + secondsDisplay;
}
let formatted = minutes + ":" + secondsDisplay;
const formatted = minutes + ":" + secondsDisplay;
return formatted;
}
@ -669,7 +669,7 @@ async function runThePopup(messageListener?: MessageListener) {
}
//remove this channel
let index = whitelistedChannels.indexOf(response.channelID);
const index = whitelistedChannels.indexOf(response.channelID);
whitelistedChannels.splice(index, 1);
//change button
@ -717,14 +717,14 @@ async function runThePopup(messageListener?: MessageListener) {
//converts time in seconds to minutes
function getTimeInMinutes(seconds) {
let minutes = Math.floor(seconds / 60);
const minutes = Math.floor(seconds / 60);
return minutes;
}
//converts time in seconds to seconds past the last minute
function getTimeInFormattedSeconds(seconds) {
let minutes = seconds % 60;
const minutes = seconds % 60;
let secondsFormatted = minutes.toFixed(3);
if (minutes < 10) {
@ -742,7 +742,7 @@ async function runThePopup(messageListener?: MessageListener) {
* @returns {string}
*/
function getFormattedHours(minues) {
let hours = Math.floor(minues / 60);
const hours = Math.floor(minues / 60);
return (hours > 0 ? hours + "h " : "") + (minues % 60).toFixed(1);
}

View file

@ -14,7 +14,7 @@ class SkipNotice {
skipNoticeRef: React.MutableRefObject<SkipNoticeComponent>;
constructor(segments: SponsorTime[], autoSkip: boolean = false, contentContainer: ContentContainer) {
constructor(segments: SponsorTime[], autoSkip = false, contentContainer: ContentContainer) {
this.segments = segments;
this.autoSkip = autoSkip;
this.contentContainer = contentContainer;
@ -24,7 +24,7 @@ class SkipNotice {
|| document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
if (referenceNode == null) {
//for embeds
let player = document.getElementById("player");
const player = document.getElementById("player");
referenceNode = player.firstChild as HTMLElement;
let index = 1;
@ -40,7 +40,7 @@ class SkipNotice {
referenceNode = document.querySelector("#main-panel.ytmusic-player-page");
}
let amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
const amountOfPreviousNotices = document.getElementsByClassName("sponsorSkipNotice").length;
//this is the suffix added at the end of every id
let idSuffix = "";
for (const segment of this.segments) {
@ -63,7 +63,7 @@ class SkipNotice {
);
}
close() {
close(): void {
ReactDOM.unmountComponentAtNode(this.noticeElement);
this.noticeElement.remove();

View file

@ -2,18 +2,19 @@ import * as React from "react";
import * as ReactDOM from "react-dom";
import SubmissionNoticeComponent from "../components/SubmissionNoticeComponent";
import { ContentContainer } from "../types";
class SubmissionNotice {
// Contains functions and variables from the content script needed by the skip notice
contentContainer: () => any;
contentContainer: () => unknown;
callback: () => any;
callback: () => unknown;
noticeRef: React.MutableRefObject<SubmissionNoticeComponent>;
noticeElement: HTMLDivElement;
constructor(contentContainer: () => any, callback: () => any) {
constructor(contentContainer: ContentContainer, callback: () => unknown) {
this.noticeRef = React.createRef();
this.contentContainer = contentContainer;
@ -24,7 +25,7 @@ class SubmissionNotice {
|| document.getElementById("movie_player") || document.querySelector("#player-container .video-js");
if (referenceNode == null) {
//for embeds
let player = document.getElementById("player");
const player = document.getElementById("player");
referenceNode = player.firstChild as HTMLElement;
let index = 1;
@ -51,11 +52,11 @@ class SubmissionNotice {
);
}
update() {
update(): void {
this.noticeRef.current.forceUpdate();
}
close() {
close(): void {
ReactDOM.unmountComponentAtNode(this.noticeElement);
this.noticeElement.remove();

View file

@ -63,6 +63,21 @@ interface PreviewBarOption {
opacity: string
}
interface Registration {
message: string,
id: string,
allFrames: boolean,
js: browser.extensionTypes.ExtensionFileOrCode[],
css: browser.extensionTypes.ExtensionFileOrCode[],
matches: string[]
}
interface BackgroundScriptContainer {
registerFirefoxContentScript: (opts: Registration) => void,
unregisterFirefoxContentScript: (id: string) => void
}
type VideoID = string;
export {
@ -74,5 +89,7 @@ export {
SponsorTime,
VideoID,
SponsorHideType,
PreviewBarOption
};
PreviewBarOption,
Registration,
BackgroundScriptContainer
};

View file

@ -1,12 +1,12 @@
import Config from "./config";
import { CategorySelection, SponsorTime, FetchResponse } from "./types";
import { CategorySelection, SponsorTime, FetchResponse, BackgroundScriptContainer, Registration } from "./types";
import * as CompileConfig from "../config.json";
class Utils {
// Contains functions needed from the background script
backgroundScriptContainer: any = null;
backgroundScriptContainer: BackgroundScriptContainer | null = null;
// Used to add content scripts and CSS required
js = [
@ -19,24 +19,24 @@ class Utils {
"popup.css"
];
constructor(backgroundScriptContainer?: any) {
constructor(backgroundScriptContainer?: BackgroundScriptContainer) {
this.backgroundScriptContainer = backgroundScriptContainer;
}
// Function that can be used to wait for a condition before returning
async wait(condition, timeout = 5000, check = 100) {
async wait(condition: () => HTMLElement | boolean, timeout = 5000, check = 100): Promise<HTMLElement | boolean> {
return await new Promise((resolve, reject) => {
setTimeout(() => reject("TIMEOUT"), timeout);
let intervalCheck = () => {
let result = condition();
const intervalCheck = () => {
const result = condition();
if (result !== false) {
resolve(result);
clearInterval(interval);
};
}
};
let interval = setInterval(intervalCheck, check);
const interval = setInterval(intervalCheck, check);
//run the check once first, this speeds it up a lot
intervalCheck();
@ -51,12 +51,12 @@ class Utils {
*
* @param {CallableFunction} callback
*/
setupExtraSitePermissions(callback) {
setupExtraSitePermissions(callback: (granted: boolean) => void): void {
// Request permission
let permissions = ["declarativeContent"];
if (this.isFirefox()) permissions = [];
let self = this;
const self = this;
chrome.permissions.request({
origins: this.getInvidiousInstancesRegex(),
@ -79,20 +79,20 @@ class Utils {
*
* For now, it is just SB.config.invidiousInstances.
*/
setupExtraSiteContentScripts() {
let self = this;
setupExtraSiteContentScripts(): void {
const self = this;
if (this.isFirefox()) {
let firefoxJS = [];
const firefoxJS = [];
for (const file of this.js) {
firefoxJS.push({file});
}
let firefoxCSS = [];
const firefoxCSS = [];
for (const file of this.css) {
firefoxCSS.push({file});
}
let registration = {
const registration: Registration = {
message: "registerContentScript",
id: "invidious",
allFrames: true,
@ -108,7 +108,7 @@ class Utils {
}
} else {
chrome.declarativeContent.onPageChanged.removeRules(["invidious"], function() {
let conditions = [];
const conditions = [];
for (const regex of self.getInvidiousInstancesRegex()) {
conditions.push(new chrome.declarativeContent.PageStateMatcher({
pageUrl: { urlMatches: regex }
@ -116,7 +116,7 @@ class Utils {
}
// Add page rule
let rule = {
const rule = {
id: "invidious",
conditions,
// This API is experimental and not visible by the TypeScript compiler
@ -135,9 +135,9 @@ class Utils {
/**
* Removes the permission and content script registration.
*/
removeExtraSiteRegistration() {
removeExtraSiteRegistration(): void {
if (this.isFirefox()) {
let id = "invidious";
const id = "invidious";
if (this.backgroundScriptContainer) {
this.backgroundScriptContainer.unregisterFirefoxContentScript(id);
@ -163,7 +163,7 @@ class Utils {
* @param sponsorTimes
*/
getSegmentsFromSponsorTimes(sponsorTimes: SponsorTime[]): number[][] {
let segments: number[][] = [];
const segments: number[][] = [];
for (const sponsorTime of sponsorTimes) {
segments.push(sponsorTime.segment);
}
@ -193,19 +193,19 @@ class Utils {
}
}
localizeHtmlPage() {
localizeHtmlPage(): void {
//Localize by replacing __MSG_***__ meta tags
var objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children;
for (var j = 0; j < objects.length; j++) {
var obj = objects[j];
const objects = document.getElementsByClassName("sponsorBlockPageBody")[0].children;
for (let j = 0; j < objects.length; j++) {
const obj = objects[j];
let localizedMessage = this.getLocalizedMessage(obj.innerHTML.toString());
const localizedMessage = this.getLocalizedMessage(obj.innerHTML.toString());
if (localizedMessage) obj.innerHTML = localizedMessage;
}
}
getLocalizedMessage(text) {
var valNewH = text.replace(/__MSG_(\w+)__/g, function(match, v1) {
getLocalizedMessage(text: string): string | false {
const valNewH = text.replace(/__MSG_(\w+)__/g, function(match, v1) {
return v1 ? chrome.i18n.getMessage(v1) : "";
});
@ -219,8 +219,8 @@ class Utils {
/**
* @returns {String[]} Invidious Instances in regex form
*/
getInvidiousInstancesRegex() {
var invidiousInstancesRegex = [];
getInvidiousInstancesRegex(): string[] {
const invidiousInstancesRegex: string[] = [];
for (const url of Config.config.invidiousInstances) {
invidiousInstancesRegex.push("https://*." + url + "/*");
invidiousInstancesRegex.push("http://*." + url + "/*");
@ -229,11 +229,11 @@ class Utils {
return invidiousInstancesRegex;
}
generateUserID(length = 36) {
let charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
generateUserID(length = 36): string {
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
let result = "";
if (window.crypto && window.crypto.getRandomValues) {
let values = new Uint32Array(length);
const values = new Uint32Array(length);
window.crypto.getRandomValues(values);
for (let i = 0; i < length; i++) {
result += charset[values[i] % charset.length];
@ -253,7 +253,7 @@ class Utils {
* @param {int} statusCode
* @returns {string} errorMessage
*/
getErrorMessage(statusCode) {
getErrorMessage(statusCode: number): string {
let errorMessage = "";
if([400, 429, 409, 502, 0].includes(statusCode)) {
@ -298,7 +298,7 @@ class Utils {
* @param callback
*/
async asyncRequestToServer(type: string, address: string, data = {}): Promise<FetchResponse> {
let serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
return await (this.asyncRequestToCustomServer(type, serverAddress + address, data));
}
@ -310,8 +310,8 @@ class Utils {
* @param address The address to add to the SponsorBlock server address
* @param callback
*/
sendRequestToServer(type: string, address: string, callback?: (response: FetchResponse) => void) {
let serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
sendRequestToServer(type: string, address: string, callback?: (response: FetchResponse) => void): void {
const serverAddress = Config.config.testingServer ? CompileConfig.testingServerAddress : Config.config.serverAddress;
// Ask the background script to do the work
chrome.runtime.sendMessage({
@ -324,15 +324,15 @@ class Utils {
}
getFormattedTime(seconds: number, precise?: boolean): string {
let hours = Math.floor(seconds / 60 / 60);
let minutes = Math.floor(seconds / 60) % 60;
const hours = Math.floor(seconds / 60 / 60);
const minutes = Math.floor(seconds / 60) % 60;
let minutesDisplay = String(minutes);
let secondsNum = seconds % 60;
if (!precise) {
secondsNum = Math.floor(secondsNum);
}
let secondsDisplay: string = String(precise ? secondsNum.toFixed(3) : secondsNum);
let secondsDisplay = String(precise ? secondsNum.toFixed(3) : secondsNum);
if (secondsNum < 10) {
//add a zero
@ -343,7 +343,7 @@ class Utils {
minutesDisplay = "0" + minutesDisplay;
}
let formatted = (hours ? hours + ":" : "") + minutesDisplay + ":" + secondsDisplay;
const formatted = (hours ? hours + ":" : "") + minutesDisplay + ":" + secondsDisplay;
return formatted;
}