Browse Source

Initial commit

master
Dylan Baker 4 years ago
commit
aaa8614f2b
17 changed files with 4570 additions and 0 deletions
  1. 1
    0
      .gitignore
  2. 7
    0
      LICENSE
  3. 21
    0
      README.md
  4. 66
    0
      ext/_locales/en/messages.json
  5. BIN
      ext/icons/icon128.png
  6. BIN
      ext/icons/icon16.png
  7. BIN
      ext/icons/icon19.png
  8. BIN
      ext/icons/icon48.png
  9. 1
    0
      ext/index.js
  10. 26
    0
      ext/manifest.json
  11. 63
    0
      ext/style.css
  12. 4195
    0
      package-lock.json
  13. 19
    0
      package.json
  14. 45
    0
      src/index.js
  15. 62
    0
      src/style.css
  16. 32
    0
      src/templates.js
  17. 32
    0
      webpack.config.js

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
1
+node_modules/

+ 7
- 0
LICENSE View File

@@ -0,0 +1,7 @@
1
+Copyright 2019 Dylan Baker
2
+
3
+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:
4
+
5
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+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.

+ 21
- 0
README.md View File

@@ -0,0 +1,21 @@
1
+# Refined YouTube
2
+
3
+This is a browser extension that lets you view YouTube videos in a plain,
4
+distraction-free page. Upon visiting a YouTube video, the extension will add a
5
+button to the page that, when clicked, will remove everything but the video and
6
+display it against a plain, dark background.
7
+
8
+## Installation
9
+
10
+Clone or download this repository and then load it as an unpacked extension.
11
+This is accomplished in different ways in different browsers.
12
+
13
+Chrome: visit `chrome://extensions`, enable `Developer mode`, and click `Load
14
+unpacked`. Select this repository's `ext` directory.
15
+
16
+Firefox: visit `about:debugging` and click `Load Temporary Add-On`. Select
17
+`ext/manifest.json` in this repository.
18
+
19
+## License
20
+
21
+This extension is open source software under the terms of the MIT License.

+ 66
- 0
ext/_locales/en/messages.json View File

@@ -0,0 +1,66 @@
1
+{
2
+  "l10nTabName": {
3
+    "message":"Localization"
4
+    ,"description":"name of the localization tab"
5
+  }
6
+  ,"l10nHeader": {
7
+    "message":"It does localization too! (this whole tab is, actually)"
8
+    ,"description":"Header text for the localization section"
9
+  }
10
+  ,"l10nIntro": {
11
+    "message":"'L10n' refers to 'Localization' - 'L' an 'n' are obvious, and 10 comes from the number of letters between those two.  It is the process/whatever of displaying something in the language of choice.  It uses 'I18n', 'Internationalization', which refers to the tools / framework supporting L10n.  I.e., something is internationalized if it has I18n support, and can be localized.  Something is localized for you if it is in your language / dialect."
12
+    ,"description":"introduce the basic idea."
13
+  }
14
+  ,"l10nProd": {
15
+    "message":"You <strong>are</strong> planning to allow localization, right?  You have <em>no idea</em> who will be using your extension!  You have no idea who will be translating it!  At least support the basics, it's not hard, and having the framework in place will let you transition much more easily later on."
16
+    ,"description":"drive the point home.  It's good for you."
17
+  }
18
+  ,"l10nFirstParagraph": {
19
+    "message":"When the options page loads, elements decorated with <strong>data-l10n</strong> will automatically be localized!"
20
+    ,"description":"inform that <el data-l10n='' /> elements will be localized on load"
21
+  }
22
+  ,"l10nSecondParagraph": {
23
+    "message":"If you need more complex localization, you can also define <strong>data-l10n-args</strong>.  This should contain <span class='code'>$containerType$</span> filled with <span class='code'>$dataType$</span>, which will be passed into Chrome's i18n API as <span class='code'>$functionArgs$</span>.  In fact, this paragraph does just that, and wraps the args in mono-space font.  Easy!"
24
+    ,"description":"introduce the data-l10n-args attribute.  End on a lame note."
25
+    ,"placeholders": {
26
+      "containerType": {
27
+        "content":"$1"
28
+        ,"example":"'array', 'list', or something similar"
29
+        ,"description":"type of the args container"
30
+      }
31
+      ,"dataType": {
32
+        "content":"$2"
33
+        ,"example":"string"
34
+        ,"description":"type of data in each array index"
35
+      }
36
+      ,"functionArgs": {
37
+        "content":"$3"
38
+        ,"example":"arguments"
39
+        ,"description":"whatever you call what you pass into a function/method.  args, params, etc."
40
+      }
41
+    }
42
+  }
43
+  ,"l10nThirdParagraph": {
44
+    "message":"Message contents are passed right into innerHTML without processing - include any tags (or even scripts) that you feel like.  If you have an input field, the placeholder will be set instead, and buttons will have the value attribute set."
45
+    ,"description":"inform that we handle placeholders, buttons, and direct HTML input"
46
+  }
47
+  ,"l10nButtonsBefore": {
48
+    "message":"Different types of buttons are handled as well.  &lt;button&gt; elements have their html set:"
49
+  }
50
+  ,"l10nButton": {
51
+    "message":"in a <strong>button</strong>"
52
+  }
53
+  ,"l10nButtonsBetween": {
54
+    "message":"while &lt;input type='submit'&gt; and &lt;input type='button'&gt; get their 'value' set (note: no HTML):"
55
+  }
56
+  ,"l10nSubmit": {
57
+    "message":"a <strong>submit</strong> value"
58
+  }
59
+  ,"l10nButtonsAfter": {
60
+    "message":"Awesome, no?"
61
+  }
62
+  ,"l10nExtras": {
63
+    "message":"You can even set <span class='code'>data-l10n</span> on things like the &lt;title&gt; tag, which lets you have translatable page titles, or fieldset &lt;legend&gt; tags, or anywhere else - the default <span class='code'>Boil.localize()</span> behavior will check every tag in the document, not just the body."
64
+    ,"description":"inform about places which may not be obvious, like <title>, etc"
65
+  }
66
+}

BIN
ext/icons/icon128.png View File


BIN
ext/icons/icon16.png View File


BIN
ext/icons/icon19.png View File


BIN
ext/icons/icon48.png View File


+ 1
- 0
ext/index.js View File

@@ -0,0 +1 @@
1
+!function(e){var n={};function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{enumerable:!0,get:o})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,n){if(1&n&&(e=t(e)),8&n)return e;if(4&n&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(t.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&n&&"string"!=typeof e)for(var r in e)t.d(o,r,function(n){return e[n]}.bind(null,r));return o},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},t.p="",t(t.s=1)}([function(e,n,t){},function(e,n,t){"use strict";t.r(n);const o=e=>`\n  <head>\n    <title>Refined YouTube</title>\n    <meta charset="utf8">\n  </head>\n  <body class="refined-youtube">\n    <div id="refined-youtube-container">\n      <iframe\n        width="1280"\n        height="720"\n        src="https://www.youtube.com/embed/${e}"\n        frameborder="0"\n        allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"\n        allowfullscreen\n      ></iframe>\n    </div>\n    <a id="refined-youtube-close-button" href="https://www.youtube.com/watch?v=${e}">\n      x\n    </a>\n  </body>\n`;t(0);window.onload=async()=>{const e=e=>document.querySelector(e),n=e=>{const n=window.location.search.substr(1).split("&").map(e=>e.split("=")).find(n=>n[0]===e);return n?n[1]:void 0},t=n=>e("html").innerHTML=n,r=n("v"),u=!!n("refined");if(void 0!==r)if(u)t(o(r));else{const n=document.createElement("div");n.innerHTML='\n  <div id="refined-youtube-launch-container">\n    <button id="refined-youtube-launch-button">\n      View on Refined YouTube\n    </button>\n    <span id="refined-youtube-launch-close-button">\n      x\n    </span>\n  </div>\n',document.body.append(n);const u=e("#refined-youtube-launch-button"),i=e("#refined-youtube-launch-close-button");u.addEventListener("click",()=>t(o(r))),i.addEventListener("click",()=>{const n=e("#refined-youtube-launch-container");n.style.opacity=0,setTimeout(()=>{n.remove()},1e3)})}}}]);

+ 26
- 0
ext/manifest.json View File

@@ -0,0 +1,26 @@
1
+{
2
+  "name": "Refined YouTube",
3
+  "version": "0.0.1",
4
+  "manifest_version": 2,
5
+  "description": "A more refined YouTube experience",
6
+  "homepage_url": "https://git.sr.ht/~simulacrumparty/refined-youtube",
7
+  "icons": {
8
+    "16": "icons/icon16.png",
9
+    "48": "icons/icon48.png",
10
+    "128": "icons/icon128.png"
11
+  },
12
+  "default_locale": "en",
13
+  "content_scripts": [
14
+    {
15
+      "matches": [
16
+        "https://www.youtube.com/*"
17
+      ],
18
+      "css": [
19
+        "style.css"
20
+      ],
21
+      "js": [
22
+        "index.js"
23
+      ]
24
+    }
25
+  ]
26
+}

+ 63
- 0
ext/style.css View File

@@ -0,0 +1,63 @@
1
+#refined-youtube-launch-container {
2
+  align-items: center;
3
+  background: white;
4
+  border-top: 2px solid #000000;
5
+  bottom: 0;
6
+  box-sizing: border-box;
7
+  display: flex;
8
+  height: 100px;
9
+  justify-content: center;
10
+  position: fixed;
11
+  transition: opacity 1s;
12
+  width: 100vw;
13
+  z-index: 999;
14
+}
15
+
16
+#refined-youtube-launch-button {
17
+  background-color: #CC0000;
18
+  border-radius: 5px;
19
+  color: #FFFFFF;
20
+  cursor: pointer;
21
+  font-family: Roboto;
22
+  font-size: 14px;
23
+  font-weight: 500;
24
+  padding: 12px 20px;
25
+  text-decoration: none;
26
+  text-transform: uppercase;
27
+}
28
+
29
+#refined-youtube-launch-close-button {
30
+  cursor: pointer;
31
+  font-size: 20px;
32
+  position: absolute;
33
+  right: 20px;
34
+  top: 5px;
35
+}
36
+
37
+#refined-youtube-close-button {
38
+  color: #f5f5f5;
39
+  cursor: pointer;
40
+  font-size: 36px;
41
+  position: fixed;
42
+  right: 20px;
43
+  text-decoration: none;
44
+  top: 10px;
45
+}
46
+
47
+#refined-youtube-close-button:hover,
48
+#refined-youtube-launch-close-button:hover {
49
+  transform: scale(1.1);
50
+  transition: transform .2s ease-in-out;
51
+}
52
+
53
+body.refined-youtube {
54
+  background: #1f1f1f;
55
+}
56
+
57
+#refined-youtube-container {
58
+  align-items: center;
59
+  display: flex;
60
+  height: 100vh;
61
+  justify-content: center;
62
+}
63
+

+ 4195
- 0
package-lock.json
File diff suppressed because it is too large
View File


+ 19
- 0
package.json View File

@@ -0,0 +1,19 @@
1
+{
2
+  "name": "ext",
3
+  "version": "1.0.0",
4
+  "description": "",
5
+  "main": "index.js",
6
+  "scripts": {
7
+    "build": "webpack",
8
+    "watch": "webpack --watch"
9
+  },
10
+  "keywords": [],
11
+  "author": "Dylan Baker <dylan@simulacrum.party>",
12
+  "license": "MIT",
13
+  "devDependencies": {
14
+    "css-loader": "^3.0.0",
15
+    "mini-css-extract-plugin": "^0.7.0",
16
+    "webpack": "^4.35.3",
17
+    "webpack-cli": "^3.3.5"
18
+  }
19
+}

+ 45
- 0
src/index.js View File

@@ -0,0 +1,45 @@
1
+import { buildLaunchContainer, buildRefinedPage } from './templates';
2
+import './style.css';
3
+
4
+const run = async () => {
5
+  const $ = (selector) => document.querySelector(selector);
6
+  const fetchParam = (name) => {
7
+    const param = window.location.search
8
+      .substr(1)
9
+      .split('&')
10
+      .map((param) => param.split('='))
11
+      .find((param) => param[0] === name);
12
+    return param ? param[1] : undefined;
13
+  };
14
+
15
+  const refinePage = (page) => ($('html').innerHTML = page);
16
+
17
+  const videoId = fetchParam('v');
18
+  const refineImmediately = fetchParam('refined') ? true : false;
19
+
20
+  if (videoId !== undefined) {
21
+    if (refineImmediately) {
22
+      refinePage(buildRefinedPage(videoId));
23
+    } else {
24
+      const launchContainer = document.createElement('div');
25
+      launchContainer.innerHTML = buildLaunchContainer(videoId);
26
+      document.body.append(launchContainer);
27
+
28
+      const launchButton = $('#refined-youtube-launch-button');
29
+      const launchCloseButton = $('#refined-youtube-launch-close-button');
30
+
31
+      launchButton.addEventListener('click', () =>
32
+        refinePage(buildRefinedPage(videoId))
33
+      );
34
+      launchCloseButton.addEventListener('click', () => {
35
+        const launchContainer = $('#refined-youtube-launch-container');
36
+        launchContainer.style.opacity = 0;
37
+        setTimeout(() => {
38
+          launchContainer.remove();
39
+        }, 1000);
40
+      });
41
+    }
42
+  }
43
+};
44
+
45
+run();

+ 62
- 0
src/style.css View File

@@ -0,0 +1,62 @@
1
+#refined-youtube-launch-container {
2
+  align-items: center;
3
+  background: white;
4
+  border-top: 2px solid #000000;
5
+  bottom: 0;
6
+  box-sizing: border-box;
7
+  display: flex;
8
+  height: 100px;
9
+  justify-content: center;
10
+  position: fixed;
11
+  transition: opacity 1s;
12
+  width: 100vw;
13
+  z-index: 999;
14
+}
15
+
16
+#refined-youtube-launch-button {
17
+  background-color: #CC0000;
18
+  border-radius: 5px;
19
+  color: #FFFFFF;
20
+  cursor: pointer;
21
+  font-family: Roboto;
22
+  font-size: 14px;
23
+  font-weight: 500;
24
+  padding: 12px 20px;
25
+  text-decoration: none;
26
+  text-transform: uppercase;
27
+}
28
+
29
+#refined-youtube-launch-close-button {
30
+  cursor: pointer;
31
+  font-size: 20px;
32
+  position: absolute;
33
+  right: 20px;
34
+  top: 5px;
35
+}
36
+
37
+#refined-youtube-close-button {
38
+  color: #f5f5f5;
39
+  cursor: pointer;
40
+  font-size: 36px;
41
+  position: fixed;
42
+  right: 20px;
43
+  text-decoration: none;
44
+  top: 10px;
45
+}
46
+
47
+#refined-youtube-close-button:hover,
48
+#refined-youtube-launch-close-button:hover {
49
+  transform: scale(1.1);
50
+  transition: transform .2s ease-in-out;
51
+}
52
+
53
+body.refined-youtube {
54
+  background: #1f1f1f;
55
+}
56
+
57
+#refined-youtube-container {
58
+  align-items: center;
59
+  display: flex;
60
+  height: 100vh;
61
+  justify-content: center;
62
+}

+ 32
- 0
src/templates.js View File

@@ -0,0 +1,32 @@
1
+export const buildLaunchContainer = (videoId) => `
2
+  <div id="refined-youtube-launch-container">
3
+    <button id="refined-youtube-launch-button">
4
+      View on Refined YouTube
5
+    </button>
6
+    <span id="refined-youtube-launch-close-button">
7
+      x
8
+    </span>
9
+  </div>
10
+`;
11
+
12
+export const buildRefinedPage = (videoId) => `
13
+  <head>
14
+    <title>Refined YouTube</title>
15
+    <meta charset="utf8">
16
+  </head>
17
+  <body class="refined-youtube">
18
+    <div id="refined-youtube-container">
19
+      <iframe
20
+        width="1280"
21
+        height="720"
22
+        src="https://www.youtube.com/embed/${videoId}"
23
+        frameborder="0"
24
+        allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
25
+        allowfullscreen
26
+      ></iframe>
27
+    </div>
28
+    <a id="refined-youtube-close-button" href="https://www.youtube.com/watch?v=${videoId}">
29
+      x
30
+    </a>
31
+  </body>
32
+`;

+ 32
- 0
webpack.config.js View File

@@ -0,0 +1,32 @@
1
+const path = require('path');
2
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
3
+
4
+module.exports = {
5
+  entry: "./src/index.js",
6
+  output: {
7
+    path: path.resolve(__dirname, "ext"),
8
+    filename: "index.js",
9
+  },
10
+  mode: "production",
11
+  module: {
12
+    rules: [
13
+      {
14
+        test: /\.css$/,
15
+        use: [
16
+          {
17
+            loader: MiniCssExtractPlugin.loader,
18
+            options: {
19
+              publicPath: (resourcePath, context) => {
20
+                return path.relative(path.dirname(resourcePath), context) + '/';
21
+              },
22
+            },
23
+          },
24
+          'css-loader',
25
+        ],
26
+      },
27
+    ]
28
+  },
29
+  plugins: [
30
+    new MiniCssExtractPlugin({ filename: 'style.css' }),
31
+  ],
32
+};

Loading…
Cancel
Save