mirror of
https://github.com/ajayyy/SponsorBlock.git
synced 2024-11-10 09:07:45 +01:00
commit
59826aae6d
22 changed files with 1819 additions and 642 deletions
33
.eslintrc.js
Normal file
33
.eslintrc.js
Normal 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
1085
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -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",
|
||||||
|
|
|
@ -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 <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.
|
||||||
|
|
||||||
|
|
||||||
******************************
|
******************************
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
|
|
||||||
******************************
|
******************************
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"));
|
||||||
|
|
|
@ -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();
|
||||||
|
|
210
src/content.ts
210
src/content.ts
|
@ -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);
|
||||||
|
|
|
@ -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 = ' ';
|
bar.innerHTML = ' ';
|
||||||
return bar;
|
return bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
remove() {
|
remove(): void {
|
||||||
this.container.remove();
|
this.container.remove();
|
||||||
this.container = undefined;
|
this.container = undefined;
|
||||||
}
|
}
|
||||||
|
|
136
src/options.ts
136
src/options.ts
|
@ -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"));
|
||||||
});;
|
});
|
||||||
}
|
}
|
86
src/popup.ts
86
src/popup.ts
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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();
|
||||||
|
|
21
src/types.ts
21
src/types.ts
|
@ -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
|
||||||
|
};
|
||||||
|
|
80
src/utils.ts
80
src/utils.ts
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue