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/firefox-webext-browser": "70.0.1",
"@types/jest": "^24.0.23", "@types/jest": "^24.0.23",
"@types/jquery": "^3.3.31", "@types/jquery": "^3.3.31",
"@typescript-eslint/eslint-plugin": "^4.9.1",
"@typescript-eslint/parser": "^4.9.1",
"copy-webpack-plugin": "^6.0.3", "copy-webpack-plugin": "^6.0.3",
"eslint": "^7.15.0",
"eslint-plugin-react": "^7.21.5",
"jest": "^26.4.0", "jest": "^26.4.0",
"rimraf": "^3.0.0", "rimraf": "^3.0.0",
"ts-jest": "^26.2.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": "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\"", "dev:firefox": "npm run build:dev:firefox && concurrently \"npm run web-run:firefox\" \"npm run build:watch:firefox\"",
"clean": "rimraf dist", "clean": "rimraf dist",
"test": "npx jest" "test": "npx jest",
"lint": "eslint src",
"lint:fix": "eslint src --fix"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View file

@ -529,81 +529,81 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@types/prop-types @types/prop-types
15.7.3 <https://github.com/DefinitelyTyped/DefinitelyTyped> 15.7.3 <https://github.com/DefinitelyTyped/DefinitelyTyped>
MIT License MIT License
Copyright (c) Microsoft Corporation. All rights reserved. Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE SOFTWARE
****************************** ******************************
@types/react @types/react
16.9.22 <https://github.com/DefinitelyTyped/DefinitelyTyped> 16.9.22 <https://github.com/DefinitelyTyped/DefinitelyTyped>
MIT License MIT License
Copyright (c) Microsoft Corporation. All rights reserved. Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE SOFTWARE
****************************** ******************************
@types/react-dom @types/react-dom
16.9.5 <https://github.com/DefinitelyTyped/DefinitelyTyped> 16.9.5 <https://github.com/DefinitelyTyped/DefinitelyTyped>
MIT License MIT License
Copyright (c) Microsoft Corporation. All rights reserved. Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE SOFTWARE
****************************** ******************************
@ -637,26 +637,26 @@ SOFTWARE.
@webassemblyjs/floating-point-hex-parser @webassemblyjs/floating-point-hex-parser
1.8.5 <https://github.com/xtuc/webassemblyjs> 1.8.5 <https://github.com/xtuc/webassemblyjs>
MIT License MIT License
Copyright (c) 2017 Mauro Bringolf Copyright (c) 2017 Mauro Bringolf
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software. copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 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 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
****************************** ******************************
@ -1855,7 +1855,7 @@ THE SOFTWARE.
async-each async-each
1.0.3 <https://github.com/paulmillr/async-each> 1.0.3 <https://github.com/paulmillr/async-each>
license: MIT 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. 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 bluebird
@ -3760,13 +3788,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
color-name color-name
1.1.3 <https://github.com/dfcreative/color-name> 1.1.3 <https://github.com/dfcreative/color-name>
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2015 Dmitry Ivanov 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: 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 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 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 component-emitter
1.3.0 <https://github.com/component/emitter> 1.3.0 <https://github.com/component/emitter>
(The MIT License) (The MIT License)
Copyright (c) 2014 Component contributors <dev@component.io> Copyright (c) 2014 Component contributors <dev@component.io>
Permission is hereby granted, free of charge, to any person Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following Software is furnished to do so, subject to the following
conditions: conditions:
The above copyright notice and this permission notice shall be The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software. included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE. 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. 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 fill-range
@ -5153,6 +5207,34 @@ the licensed code:
DEALINGS IN THE SOFTWARE. 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 gensync
@ -6709,6 +6791,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. 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 nanomatch
@ -8034,26 +8135,26 @@ authors: Mathias Bynens <https://mathiasbynens.be/>
regjsgen regjsgen
0.2.0 <https://github.com/d10/regjsgen> 0.2.0 <https://github.com/d10/regjsgen>
Copyright 2014 Benjamin Tan <demoneaux@gmail.com> (https://d10.github.io/) Copyright 2014 Benjamin Tan <demoneaux@gmail.com> (https://d10.github.io/)
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including "Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish, without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to permit persons to whom the Software is furnished to do so, subject to
the following conditions: the following conditions:
The above copyright notice and this permission notice shall be The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software. included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
****************************** ******************************
@ -8350,7 +8451,7 @@ SOFTWARE.
run-queue run-queue
1.0.3 <https://github.com/iarna/run-queue> 1.0.3 <https://github.com/iarna/run-queue>
license: ISC 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 spdx-license-ids
3.0.5 <https://github.com/shinnn/spdx-license-ids> 3.0.5 <https://github.com/shinnn/spdx-license-ids>
license: CC0-1.0 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 tslib
1.10.0 <https://github.com/Microsoft/tslib> 1.10.0 <https://github.com/Microsoft/tslib>
Apache License Apache License
Version 2.0, January 2004 Version 2.0, January 2004
http://www.apache.org/licenses/ http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions. 1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "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. "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. "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. "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. "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. "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). "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. "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." "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. "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. 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. 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: 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 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 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 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. 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. 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. 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. 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. 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. 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 END OF TERMS AND CONDITIONS
****************************** ******************************

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,7 +10,7 @@ export interface SubmissionNoticeProps {
// Contains functions and variables from the content script needed by the skip notice // Contains functions and variables from the content script needed by the skip notice
contentContainer: ContentContainer; contentContainer: ContentContainer;
callback: () => any; callback: () => unknown;
closeListener: () => void 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 // Contains functions and variables from the content script needed by the skip notice
contentContainer: ContentContainer; contentContainer: ContentContainer;
callback: () => any; callback: () => unknown;
noticeRef: React.MutableRefObject<NoticeComponent>; noticeRef: React.MutableRefObject<NoticeComponent>;
timeEditRefs: React.RefObject<SponsorTimeEditComponent>[]; timeEditRefs: React.RefObject<SponsorTimeEditComponent>[];
@ -39,7 +39,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
this.contentContainer = props.contentContainer; this.contentContainer = props.contentContainer;
this.callback = props.callback; this.callback = props.callback;
let noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle"); const noticeTitle = chrome.i18n.getMessage("confirmNoticeTitle");
// Setup state // Setup state
this.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 // Catch and rerender when the video size changes
//TODO: Use ResizeObserver when it is supported in TypeScript //TODO: Use ResizeObserver when it is supported in TypeScript
this.videoObserver = new MutationObserver(() => { this.videoObserver = new MutationObserver(() => {
@ -61,13 +61,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
}); });
} }
componentWillUnmount() { componentWillUnmount(): void {
if (this.videoObserver) { if (this.videoObserver) {
this.videoObserver.disconnect(); this.videoObserver.disconnect();
} }
} }
render() { render(): React.ReactElement {
return ( return (
<NoticeComponent noticeTitle={this.state.noticeTitle} <NoticeComponent noticeTitle={this.state.noticeTitle}
idSuffix={this.state.idSuffix} idSuffix={this.state.idSuffix}
@ -114,13 +114,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
} }
getSponsorTimeMessages(): JSX.Element[] | JSX.Element { getSponsorTimeMessages(): JSX.Element[] | JSX.Element {
let elements: JSX.Element[] = []; const elements: JSX.Element[] = [];
this.timeEditRefs = []; this.timeEditRefs = [];
let sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting; const sponsorTimes = this.props.contentContainer().sponsorTimesSubmitting;
for (let i = 0; i < sponsorTimes.length; i++) { for (let i = 0; i < sponsorTimes.length; i++) {
let timeRef = React.createRef<SponsorTimeEditComponent>(); const timeRef = React.createRef<SponsorTimeEditComponent>();
elements.push( elements.push(
<SponsorTimeEditComponent key={i} <SponsorTimeEditComponent key={i}
@ -139,7 +139,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
} }
getMessageBoxes(): JSX.Element[] | JSX.Element { getMessageBoxes(): JSX.Element[] | JSX.Element {
let elements: JSX.Element[] = []; const elements: JSX.Element[] = [];
for (let i = 0; i < this.state.messages.length; i++) { for (let i = 0; i < this.state.messages.length; i++) {
elements.push( elements.push(
@ -153,7 +153,7 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
return elements; return elements;
} }
cancel() { cancel(): void {
this.noticeRef.current.close(true); this.noticeRef.current.close(true);
this.contentContainer().resetSponsorSubmissionNotice(); this.contentContainer().resetSponsorSubmissionNotice();
@ -161,13 +161,13 @@ class SubmissionNoticeComponent extends React.Component<SubmissionNoticeProps, S
this.props.closeListener(); this.props.closeListener();
} }
submit() { submit(): void {
// save all items // save all items
for (const ref of this.timeEditRefs) { for (const ref of this.timeEditRefs) {
ref.current.saveEditTimes(); ref.current.saveEditTimes();
} }
let sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting; const sponsorTimesSubmitting = this.props.contentContainer().sponsorTimesSubmitting;
for (const sponsorTime of sponsorTimesSubmitting) { for (const sponsorTime of sponsorTimesSubmitting) {
if (sponsorTime.category === "chooseACategory") { if (sponsorTime.category === "chooseACategory") {
alert(chrome.i18n.getMessage("youMustSelectACategory")); 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 * Callback function when an option is updated
*/ */
@ -149,7 +149,7 @@ var Config: SBObject = {
skipCount: 0, skipCount: 0,
sponsorTimesContributed: 0, sponsorTimesContributed: 0,
submissionCountSinceCategories: 0, submissionCountSinceCategories: 0,
showTimeWithSkips: true, showTimeWithSkips: true,
unsubmittedWarning: true, unsubmittedWarning: true,
disableSkipping: false, disableSkipping: false,
trackViewCount: true, trackViewCount: true,
@ -286,7 +286,7 @@ function configProxy(): any {
} }
}); });
var handler: ProxyHandler<any> = { const handler: ProxyHandler<any> = {
set(obj, prop, value) { set(obj, prop, value) {
Config.localConfig[prop] = value; Config.localConfig[prop] = value;
@ -298,7 +298,7 @@ function configProxy(): any {
}, },
get(obj, prop): any { get(obj, prop): any {
let data = Config.localConfig[prop]; const data = Config.localConfig[prop];
return obj[prop] || data; return obj[prop] || data;
}, },
@ -314,7 +314,7 @@ function configProxy(): any {
return new Proxy({handler}, handler); return new Proxy({handler}, handler);
} }
function fetchConfig() { function fetchConfig(): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
chrome.storage.sync.get(null, function(items) { chrome.storage.sync.get(null, function(items) {
Config.localConfig = <SBConfig> <unknown> items; // Data is ready Config.localConfig = <SBConfig> <unknown> items; // Data is ready
@ -351,7 +351,7 @@ function migrateOldFormats(config: SBConfig) {
if (config.whitelistedChannels.length > 0 && if (config.whitelistedChannels.length > 0 &&
(config.whitelistedChannels[0] == null || config.whitelistedChannels[0].includes("/"))) { (config.whitelistedChannels[0] == null || config.whitelistedChannels[0].includes("/"))) {
const channelURLFixer = async() => { const channelURLFixer = async() => {
let newChannelList: string[] = []; const newChannelList: string[] = [];
for (const item of config.whitelistedChannels) { for (const item of config.whitelistedChannels) {
if (item != null) { if (item != null) {
if (item.includes("/channel/")) { if (item.includes("/channel/")) {
@ -360,7 +360,7 @@ function migrateOldFormats(config: SBConfig) {
// Replace channel URL with channelID // 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) { if (response.ok) {
newChannelList.push((JSON.parse(response.responseText)).authorId); newChannelList.push((JSON.parse(response.responseText)).authorId);
@ -408,9 +408,9 @@ function migrateOldFormats(config: SBConfig) {
// Otherwise junk data // Otherwise junk data
if (Array.isArray(jsonData)) { if (Array.isArray(jsonData)) {
let oldMap = new Map(jsonData); const oldMap = new Map(jsonData);
oldMap.forEach((sponsorTimes: number[][], key) => { oldMap.forEach((sponsorTimes: number[][], key) => {
let segmentTimes: SponsorTime[] = []; const segmentTimes: SponsorTime[] = [];
for (const segment of sponsorTimes) { for (const segment of sponsorTimes) {
segmentTimes.push({ segmentTimes.push({
segment: segment, segment: segment,
@ -442,7 +442,7 @@ async function setupConfig() {
// Reset config // Reset config
function resetConfig() { function resetConfig() {
Config.config = Config.defaults; Config.config = Config.defaults;
}; }
function convertJSON(): void { function convertJSON(): void {
Object.keys(Config.localConfig).forEach(key => { Object.keys(Config.localConfig).forEach(key => {
@ -453,17 +453,17 @@ function convertJSON(): void {
// Add defaults // Add defaults
function addDefaults() { function addDefaults() {
for (const key in Config.defaults) { for (const key in Config.defaults) {
if(!Config.localConfig.hasOwnProperty(key)) { if(!Object.prototype.hasOwnProperty.call(Config.localConfig, key)) {
Config.localConfig[key] = Config.defaults[key]; Config.localConfig[key] = Config.defaults[key];
} else if (key === "barTypes") { } else if (key === "barTypes") {
for (const key2 in Config.defaults[key]) { 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]; Config.localConfig[key][key2] = Config.defaults[key][key2];
} }
} }
} }
} }
}; }
// Sync config // Sync config
setupConfig(); setupConfig();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -63,6 +63,21 @@ interface PreviewBarOption {
opacity: string 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; type VideoID = string;
export { export {
@ -74,5 +89,7 @@ export {
SponsorTime, SponsorTime,
VideoID, VideoID,
SponsorHideType, SponsorHideType,
PreviewBarOption PreviewBarOption,
}; Registration,
BackgroundScriptContainer
};

View file

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