- // ==UserScript==
- // @name Yays! (Yet Another Youtube Script)
- // @namespace youtube
- // @description A lightweight and non-intrusive userscript that control video playback and set the preferred player size and playback quality on YouTube.
- // @version 1.15.1
- // @author Eugene Nouvellieu <eugenox_gmail_com>
- // @license MIT License
- // @include http*://*.youtube.com/*
- // @include http*://youtube.com/*
- // @run-at document-end
- // @noframes
- // @grant unsafeWindow
- // @grant GM_deleteValue
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_xmlhttpRequest
- // @homepageURL https://eugenox.appspot.com/script/yays
- // @icon https://eugenox.appspot.com/blob/yays/yays.icon.png
- // ==/UserScript==
-
- // Copyright (c) 2012-2015 Eugene Nouvellieu <eugenox_gmail_com>
- //
- // 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.
-
- function YAYS(unsafeWindow) {
-
- 'use strict';
-
- /*
- * Meta.
- */
-
- var Meta = {
- title: 'Yays! (Yet Another Youtube Script)',
- version: '1.15.1',
- releasedate: 'Aug 16, 2015',
- site: 'https://eugenox.appspot.com/script/yays',
- ns: 'yays'
- };
-
- /*
- * Utility functions.
- */
-
- function each(iterable, callback, scope) {
- if ('length' in iterable) {
- for (var i = 0, len = iterable.length; i < len; ++i) {
- callback.call(scope, i, iterable[i]);
- }
- }
- else {
- for (var key in iterable) {
- if (iterable.hasOwnProperty(key)) {
- callback.call(scope, key, iterable[key]);
- }
- }
- }
- }
-
- function map() {
- var
- args = Array.prototype.constructor.apply([], arguments),
- callback = args.shift() || bind(Array.prototype.constructor, []),
- results = [],
- i, len;
-
- if (args.length > 1) {
- var getter = function(arg) { return arg[i]; };
- len = Math.max.apply(Math, map(function(arg) { return arg.length; }, args));
-
- for (i = 0; i < len; ++i) {
- results.push(callback.apply(null, map(getter, args)));
- }
- }
- else {
- var arg = args[0];
- len = arg.length;
-
- for (i = 0; i < len; ++i) {
- results.push(callback(arg[i]));
- }
- }
-
- return results;
- }
-
- function unique(values) {
- values.sort();
-
- for (var i = 0, j; i < values.length; ) {
- j = i;
-
- while (values[j] === values[j - 1]) {
- j++;
- }
-
- if (j - i) {
- values.splice(i, j - i);
- }
- else {
- ++i;
- }
- }
-
- return values;
- }
-
- function combine(keys, values) {
- var object = {};
-
- map(function(key, value) { object[key] = value; }, keys, values);
-
- return object;
- }
-
- function merge(target, source, override) {
- override = override === undefined || override;
-
- for (var key in source) {
- if (override || ! target.hasOwnProperty(key)) {
- target[key] = source[key];
- }
- }
-
- return target;
- }
-
- function extend(base, proto) {
- function T() {}
- T.prototype = base.prototype;
-
- return merge(new T(), proto);
- }
-
- function noop() {
- return;
- }
-
- function bind(func, scope, args) {
- if (args && args.length > 0) {
- return func.bind.apply(func, [scope].concat(args));
- }
-
- return func.bind(scope);
- }
-
- function intercept(original, extension) {
- original = original || noop;
-
- return function() {
- extension.apply(this, arguments);
-
- return original.apply(this, arguments);
- };
- }
-
- function asyncCall(func, scope, args) {
- window.setTimeout(bind(func, scope, args), 0);
- }
-
- function asyncProxy(func) {
- return function() {
- asyncCall(func, this, Array.prototype.slice.call(arguments));
- };
- }
-
- function buildURL(path, parameters) {
- var query = [];
- each(parameters, function(key, value) { query.push(key.concat('=', encodeURIComponent(value))); });
-
- return path.concat('?', query.join('&'));
- }
-
- function parseJSON(data) {
- if (typeof JSON != 'undefined') {
- return JSON.parse(data);
- }
-
- return eval('(' + data + ')');
- }
-
- /*
- * Script context.
- */
-
- var Context = (function() {
- function BasicContext(context, namespace) {
- if (context) {
- this._scope = this._createNamespace(context._scope, namespace);
- this._ns = context._ns + '.' + namespace;
- }
- else {
- this._scope = unsafeWindow;
- this._ns = 'window';
- }
- }
-
- BasicContext.prototype = {
- _scope: null,
- _ns: null,
-
- _createNamespace: function(scope, name) {
- return scope[name] = {};
- },
-
- protect: function(entity) {
- return entity;
- },
-
- publish: function(name, entity) {
- this._scope[name] = this.protect(entity);
-
- return this._ns + '.' + name;
- },
-
- revoke: function(name) {
- delete this._scope[name];
- }
- };
-
- var Context;
-
- if (typeof exportFunction == 'function') {
- Context = function(context, namespace) {
- BasicContext.call(this, context, namespace);
- };
-
- Context.prototype = extend(BasicContext, {
- _createNamespace: function(scope, name) {
- return createObjectIn(scope, {defineAs: name});
- },
-
- protect: function(entity) {
- switch (typeof entity) {
- case 'function':
- return exportFunction(entity, this._scope);
-
- case 'object':
- return cloneInto(entity, this._scope);
- }
- }
- });
- }
- else {
- Context = BasicContext;
- }
-
- return Context;
- })();
-
- var
- pageContext = new Context(),
- scriptContext = new Context(pageContext, Meta.ns);
-
- /*
- * Console singleton.
- */
-
- var Console = {
- debug: function() {
- unsafeWindow.console.debug('[' + Meta.ns + ']' + Array.prototype.join.call(arguments, ' '));
- }
- };
-
- /*
- * DOM Helper singleton.
- */
-
- var DH = {
- ELEMENT_NODE: 1,
-
- build: function(def) {
- switch (Object.prototype.toString.call(def)) {
- case '[object Object]':
- var node = this.createElement(def.tag || 'div');
-
- if ('style' in def) {
- this.style(node, def.style);
- }
-
- if ('attributes' in def) {
- this.attributes(node, def.attributes);
- }
-
- if ('listeners' in def) {
- this.listeners(node, def.listeners);
- }
-
- if ('children' in def) {
- this.append(node, def.children);
- }
-
- return node;
-
- case '[object String]':
- return this.createTextNode(def);
-
- default:
- return def;
- }
- },
-
- id: bind(document.getElementById, document),
- query: bind(document.querySelectorAll, document),
- createElement: bind(document.createElement, document),
- createTextNode: bind(document.createTextNode, document),
-
- style: function(node, style) {
- each(style, node.style.setProperty, node.style);
- },
-
- append: function(node, children) {
- each([].concat(children), function(i, child) { node.appendChild(this.build(child)); }, this);
- node.normalize();
- },
-
- insertAfter: function(node, children) {
- var parent = node.parentNode, sibling = node.nextSibling;
- if (sibling) {
- each([].concat(children), function(i, child) { parent.insertBefore(this.build(child), sibling); }, this);
- parent.normalize();
- }
- else {
- this.append(parent, children);
- }
- },
-
- prepend: function(node, children) {
- if (node.hasChildNodes()) {
- each([].concat(children), function(i, child) { node.insertBefore(this.build(child), node.firstChild); }, this);
- }
- else {
- this.append(node, children);
- }
- },
-
- remove: function(node) {
- node.parentNode.removeChild(node);
- },
-
- attributes: function(node, attributes) {
- each(attributes, node.setAttribute, node);
- },
-
- hasClass: function(node, cls) {
- return node.hasAttribute('class') && new RegExp('\\b' + cls + '\\b').test(node.getAttribute('class'));
- },
-
- addClass: function(node, clss) {
- node.setAttribute('class', node.hasAttribute('class') ? unique(node.getAttribute('class').concat(' ', clss).trim().split(/ +/)).join(' ') : clss);
- },
-
- delClass: function(node, clss) {
- if (node.hasAttribute('class')) {
- node.setAttribute('class', node.getAttribute('class').replace(new RegExp('\\s*\\b(?:' + clss.replace(/ +/g, '|') + ')\\b\\s*', 'g'), ' ').trim());
- }
- },
-
- listeners: function(node, listeners) {
- each(listeners, function(type, listener) { this.on(node, type, listener); }, this);
- },
-
- on: function(node, type, listener) {
- node.addEventListener(type, listener, false);
- },
-
- un: function(node, type, listener) {
- node.removeEventListener(type, listener, false);
- },
-
- unwrap: function(element) {
- try {
- return XPCNativeWrapper.unwrap(element);
- }
- catch (e) {
- return element;
- }
- },
-
- walk: function(node, path) {
- var steps = path.split('/'), step = null;
-
- while (node && (step = steps.shift())) {
- if (step == '..') {
- node = node.parentNode;
- continue;
- }
-
- var selector = /^(\w*)(?:\[(\d+)\])?$/.exec(step), name = selector[1], index = Number(selector[2]) || 0;
-
- for (var i = 0, j = 0, nodes = node.childNodes; node = nodes.item(i); ++i) {
- if (node.nodeType == this.ELEMENT_NODE && (! name || node.tagName.toLowerCase() == name) && j++ == index) {
- break;
- }
- }
- }
-
- return node;
- },
-
- closest: function(node, predicate) {
- do {
- if (predicate(node)) {
- return node;
- }
- } while ((node = node.parentNode) && node.nodeType == this.ELEMENT_NODE);
-
- return null;
- }
- };
-
- /*
- * i18n
- */
-
- var _ = (function() {
- var vocabulary = ["Playback", "START", "PAUSE", "STOP", "AUTO PAUSE", "AUTO STOP", "Set default playback state", "Quality", "AUTO", "ORIGINAL", "Set default video quality", "Size", "AUTO", "WIDE", "Set default player size", "Player settings", "Help"];
- function translation(language) {
- switch (language) {
- // Hungarian - eugenox
- case 'hu':
- return ["Lej\u00e1tsz\u00e1s", "ELIND\u00cdTVA", "SZ\u00dcNETELTETVE", "MEG\u00c1LL\u00cdTVA", "AUTOMATIKUS SZ\u00dcNETELTET\u00c9S", "AUTOMATIKUS MEG\u00c1LL\u00cdT\u00c1S", "Lej\u00e1tsz\u00e1s alap\u00e9rtelmezett \u00e1llapota", "Min\u0151s\u00e9g", "AUTO", "EREDETI", "Vide\u00f3k alap\u00e9rtelmezett felbont\u00e1sa", "M\u00e9ret", "AUTO", "SZ\u00c9LES", "Lej\u00e1tsz\u00f3 alap\u00e9rtelmezett m\u00e9rete", "Lej\u00e1tsz\u00f3 be\u00e1ll\u00edt\u00e1sai", "S\u00fag\u00f3"];
- // Dutch - Mike-RaWare
- case 'nl':
- return [null, null, null, null, null, null, null, "Kwaliteit", "AUTOMATISCH", null, "Stel standaard videokwaliteit in", null, null, null, null, null, null];
- // Spanish - yonane, Dinepada, jdarlan
- case 'es':
- return ["Reproducci\u00f3n autom\u00e1tica", "Iniciar", "Pausar", "Detener", "Auto pausa", "Auto detener", "Fijar reproducci\u00f3n autom\u00e1tica", "Calidad", "Auto", "Original", "Calidad por defecto", "Tama\u00f1o", "Autom\u00e1tico", "Ancho", "Tama\u00f1o del reproductor predeterminado", "Configuraci\u00f3n", "Ayuda"];
- // German - xemino, ich01
- case 'de':
- return ["Wiedergabe", "Start", "Pause", "Stop", "Auto-Pause", "Auto-Stop", "Standardm\u00e4\u00dfigen Wiedergabezustand setzen", "Qualit\u00e4t", "Auto", "Original", "Standard Video Qualit\u00e4t setzen", "Gr\u00f6\u00dfe", "Auto", "Breit", "Standard Player Gr\u00f6\u00dfe setzen", "Einstellungen", "Hilfe"];
- // Portuguese - Pitukinha
- case 'pt':
- return [null, null, null, null, null, null, null, "Qualidade", "AUTOM\u00c1TICO", null, "Defini\u00e7\u00e3o padr\u00e3o de v\u00eddeo", null, null, null, null, "Configura\u00e7\u00e3o do usu\u00e1rio", null];
- // Greek - TastyTeo
- case 'el':
- return ["\u0391\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae", null, "\u03a0\u0391\u03a5\u03a3\u0397", "\u03a3\u03a4\u0391\u039c\u0391\u03a4\u0397\u039c\u0391", "\u0391\u03a5\u03a4\u039f\u039c\u0391\u03a4\u0397 \u03a0\u0391\u03a5\u03a3\u0397", "\u0391\u03a5\u03a4\u039f\u039c\u0391\u03a4\u039f \u03a3\u03a4\u0391\u039c\u0391\u03a4\u0397\u039c\u0391", "\u039f\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7\u03c2 \u03ba\u03b1\u03c4\u03ac\u03c3\u03c4\u03b1\u03c3\u03b7\u03c2 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ae\u03c2", "\u03a0\u03bf\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1", "\u0391\u03a5\u03a4\u039f\u039c\u0391\u03a4\u039f", "\u03a0\u03a1\u039f\u0395\u03a0\u0399\u039b\u039f\u0393\u0397", "\u039f\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7\u03c2 \u03c0\u03bf\u03b9\u03cc\u03c4\u03b7\u03c4\u03b1\u03c2 \u03b2\u03af\u03bd\u03c4\u03b5\u03bf", "\u039c\u03ad\u03b3\u03b5\u03b8\u03bf\u03c2", "\u0391\u03a5\u03a4\u039f\u039c\u0391\u03a4\u039f", "\u03a0\u039b\u0391\u03a4\u03a5", "\u039f\u03c1\u03b9\u03c3\u03bc\u03cc\u03c2 \u03c0\u03c1\u03bf\u03b5\u03c0\u03b9\u03bb\u03b5\u03b3\u03bc\u03ad\u03bd\u03b7\u03c2 \u03b1\u03bd\u03ac\u03bb\u03c5\u03c3\u03b7\u03c2 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ad\u03b1", "\u0395\u03c0\u03b9\u03bb\u03bf\u03b3\u03ad\u03c2 \u03b1\u03bd\u03b1\u03c0\u03b1\u03c1\u03b1\u03b3\u03c9\u03b3\u03ad\u03b1", "\u0392\u03bf\u03ae\u03b8\u03b5\u03b9\u03b1"];
- // French - eXa
- case 'fr':
- return [null, null, null, null, null, null, null, "Qualit\u00e9", "AUTO", "ORIGINAL", "Qualit\u00e9 par d\u00e9faut", "Taille", "AUTO", "LARGE", "Taille par d\u00e9faut du lecteur", "Options du lecteur", "Aide"];
- // Slovenian - Paranoia.Com
- case 'sl':
- return [null, null, null, null, null, null, null, "Kakovost", "Samodejno", null, "Nastavi privzeto kakovost videa", null, null, null, null, "Nastavitve predvajalnika", "Pomo\u010d"];
- // Russian - an1k3y
- case 'ru':
- return [null, null, null, null, null, null, null, "\u041a\u0430\u0447\u0435\u0441\u0442\u0432\u043e", "\u0410\u0412\u0422\u041e", null, "\u041a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0432\u0438\u0434\u0435\u043e \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e", "\u0420\u0410\u0417\u041c\u0415\u0420", null, "\u0420\u0410\u0417\u0412\u0415\u0420\u041d\u0423\u0422\u042c", "\u0420\u0430\u0437\u043c\u0435\u0440 \u0432\u0438\u0434\u0435\u043e \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e", "\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u043f\u043b\u0435\u0435\u0440\u0430", "\u041f\u043e\u043c\u043e\u0449\u044c"];
- // Hebrew - baryoni
- case 'iw':
- return [null, null, null, null, null, null, null, "\u05d0\u05d9\u05db\u05d5\u05ea", "\u05d0\u05d5\u05d8\u05d5\u05de\u05d8\u05d9\u05ea", null, "\u05d4\u05d2\u05d3\u05e8 \u05d0\u05ea \u05d0\u05d9\u05db\u05d5\u05ea \u05d1\u05e8\u05d9\u05e8\u05ea \u05d4\u05de\u05d7\u05d3\u05dc \u05e9\u05dc \u05d4\u05d5\u05d9\u05d3\u05d0\u05d5", "\u05d2\u05d5\u05d3\u05dc", null, "\u05e8\u05d7\u05d1", "\u05d4\u05d2\u05d3\u05e8 \u05d0\u05ea \u05d2\u05d5\u05d3\u05dc \u05d1\u05e8\u05d9\u05e8\u05ea \u05d4\u05de\u05d7\u05d3\u05dc \u05e9\u05dc \u05d4\u05e0\u05d2\u05df", "\u05d4\u05d2\u05d3\u05e8\u05d5\u05ea \u05e0\u05d2\u05df", "\u05e2\u05d6\u05e8\u05d4"];
- // Chinese - blankhang
- case 'zh':
- return ["\u64ad\u653e\u6a21\u5f0f", "\u64ad\u653e", "\u6682\u505c", "\u505c\u6b62", "\u81ea\u52a8\u6682\u505c", "\u81ea\u52a8\u505c\u6b62", "\u8bbe\u7f6e\u9ed8\u8ba4\u64ad\u653e\u6a21\u5f0f", "\u89c6\u9891\u8d28\u91cf", "\u81ea\u52a8", "\u539f\u753b", "\u8bbe\u7f6e\u9ed8\u8ba4\u89c6\u9891\u8d28\u91cf", "\u64ad\u653e\u5668\u5927\u5c0f", "\u81ea\u52a8", "\u5bbd\u5c4f", "\u8bbe\u7f6e\u64ad\u653e\u5668\u9ed8\u8ba4\u5927\u5c0f", "\u64ad\u653e\u5668\u8bbe\u7f6e", "\u5e2e\u52a9"];
- // Polish - mkvs
- case 'pl':
- return ["Odtwarzanie", "URUCHOM", "WSTRZYMAJ", "ZATRZYMAJ", "AUTOMATYCZNIE WSTRZYMAJ", "AUTOMATYCZNIE ZATRZYMAJ", "Ustaw domy\u015blny stan odtwarzania", "Jako\u015b\u0107", "AUTOMATYCZNA", "ORYGINALNA", "Ustaw domy\u015bln\u0105 jako\u015b\u0107 film\u00f3w", "Rozmiar", "AUTOMATYCZNY", "SZEROKI", "Ustaw domy\u015blny rozmiar odtwarzacza", "Ustawienia odtwarzacza", "Pomoc"];
- // Swedish - eson
- case 'sv':
- return ["Uppspelning", "START", "PAUS", "STOPP", "AUTOPAUS", "AUTOSTOPP", "Ange uppspelningsl\u00e4ge", "Kvalitet", "AUTO", "ORIGINAL", "Ange standardkvalitet", "Storlek", "AUTO", "BRED", "Ange standardstorlek", "Inst\u00e4llningar", "Hj\u00e4lp"];
- // Ukrainian - mukolah
- case 'uk':
- return ["\u0421\u0442\u0430\u043d \u043f\u0440\u043e\u0433\u0440\u0430\u0432\u0430\u0447\u0430", "\u0412\u0406\u0414\u0422\u0412\u041e\u0420\u0418\u0422\u0418", "\u041f\u0420\u0418\u0417\u0423\u041f\u0418\u041d\u0418\u0422\u0418", "\u0417\u0423\u041f\u0418\u041d\u0418\u0422\u0418", "\u0410\u0412\u0422\u041e\u041c\u0410\u0422\u0418\u0427\u041d\u0415 \u041f\u0420\u0418\u0417\u0423\u041f\u0418\u041d\u0415\u041d\u041d\u042f", "\u0410\u0412\u0422\u041e\u041c\u0410\u0422\u0418\u0427\u041d\u0415 \u0417\u0423\u041f\u0418\u041d\u0415\u041d\u041d\u042f", "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0438\u0439 \u0441\u0442\u0430\u043d \u0432\u0456\u0434\u0442\u0432\u043e\u0440\u0435\u043d\u043d\u044f", "\u042f\u043a\u0456\u0441\u0442\u044c", "\u0410\u0412\u0422\u041e\u041c\u0410\u0422\u0418\u0427\u041d\u041e", "\u041e\u0420\u0418\u0413\u0406\u041d\u0410\u041b\u042c\u041d\u0410", "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0443 \u044f\u043a\u0456\u0441\u0442\u044c \u0432\u0456\u0434\u0442\u0432\u043e\u0440\u0435\u043d\u043d\u044f", "\u0420\u043e\u0437\u043c\u0456\u0440", "\u0410\u0412\u0422\u041e\u041c\u0410\u0422\u0418\u0427\u041d\u0418\u0419", "\u0428\u0418\u0420\u041e\u041a\u0418\u0419", "\u0412\u0438\u0431\u0435\u0440\u0456\u0442\u044c \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442\u043d\u0438\u0439 \u0440\u043e\u0437\u043c\u0456\u0440 \u043f\u0440\u043e\u0433\u0440\u0430\u0432\u0430\u0447\u0430", "\u041d\u0430\u043b\u0430\u0448\u0442\u0443\u0432\u0430\u043d\u043d\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u0432\u0430\u0447\u0430", "\u0414\u043e\u043f\u043e\u043c\u043e\u0433\u0430"];
- }
-
- return [];
- }
-
- var dictionary = combine(vocabulary, translation((document.documentElement.lang || 'en').substr(0, 2)));
-
- return function(text) {
- return dictionary[text] || text;
- };
- })();
-
- /**
- * @class ScopedStorage
- */
- function ScopedStorage(scope, namespace) {
- this._scope = scope;
- this._ns = namespace;
- }
-
- ScopedStorage.prototype = {
- _scope: null,
- _ns: null,
-
- getItem: function(key) {
- return this._scope.getItem(this._ns + '.' + key);
- },
-
- setItem: function(key, value) {
- this._scope.setItem(this._ns + '.' + key, value);
- },
-
- removeItem: function(key) {
- this._scope.removeItem(this._ns + '.' + key);
- }
- };
-
- var scriptStorage = new ScopedStorage(unsafeWindow.localStorage, Meta.ns);
-
- /*
- * Configuration handler singleton.
- */
-
- var Config = (function() {
- // Greasemonkey compatible
- if (typeof GM_getValue == 'function') {
- return {
- get: GM_getValue,
- set: GM_setValue,
- del: GM_deleteValue
- };
- }
-
- var configStorage = new ScopedStorage(scriptStorage, 'config');
-
- // HTML5
- return {
- get: function(key) {
- return configStorage.getItem(key);
- },
-
- set: function(key, value) {
- configStorage.setItem(key, value);
- },
-
- del: function(key) {
- configStorage.removeItem(key);
- }
- };
- })();
-
- /**
- * @class JSONRequest
- * Create XHR or JSONP requests.
- */
- var JSONRequest = (function() {
- var Request = null;
-
- // XHR
- if (typeof GM_xmlhttpRequest == 'function') {
- Request = function(url, parameters, callback) {
- this._callback = callback;
-
- GM_xmlhttpRequest({
- method: 'GET',
- url: buildURL(url, parameters),
- onload: bind(this._onLoad, this)
- });
- };
-
- Request.prototype = {
- _onLoad: function(response) {
- this._callback(parseJSON(response.responseText));
- }
- };
- }
- // JSONP
- else {
- Request = function(url, parameters, callback) {
- this._callback = callback;
- this._id = 'jsonp_' + Request.counter++;
-
- parameters.callback = scriptContext.publish(this._id, bind(this._onLoad, this));
-
- this._scriptNode = document.body.appendChild(DH.build({
- tag: 'script',
- attributes: {
- 'type': 'text/javascript',
- 'src': buildURL(url, parameters)
- }
- }));
- };
-
- Request.counter = 0;
-
- Request.prototype = {
- _callback: null,
- _id: null,
- _scriptNode: null,
-
- _onLoad: function(response) {
- this._callback(response);
-
- scriptContext.revoke(this._id);
-
- document.body.removeChild(this._scriptNode);
- }
- };
- }
-
- return Request;
- })();
-
- /*
- * Update checker.
- */
-
- (function() {
- if (new Date().valueOf() - Number(scriptStorage.getItem('update_checked_at')) < 86400000) { // 1 day
- return;
- }
-
- var popup = null;
-
- new JSONRequest(Meta.site + '/changelog', {version: Meta.version}, function(changelog) {
- scriptStorage.setItem('update_checked_at', new Date().valueOf().toFixed());
-
- if (changelog && changelog.length) {
- popup = renderPopup(changelog);
- }
- });
-
- function renderPopup(changelog) {
- return document.body.appendChild(DH.build({
- style: {
- 'position': 'fixed',
- 'bottom': '0',
- 'width': '100%',
- 'z-index': '1000',
- 'background-color': '#f1f1f1',
- 'border-top': '1px solid #cccccc'
- },
- children: {
- style: {
- 'margin': '15px'
- },
- children: [{
- tag: 'strong',
- children: ['There is an update available for ', Meta.title, '.']
- }, {
- tag: 'p',
- style: {
- 'margin': '10px 0'
- },
- children: [
- 'You are using version ', {
- tag: 'strong',
- children: Meta.version
- }, ', released on ', {
- tag: 'em',
- children: Meta.releasedate
- }, '. Please consider updating to the latest version.'
- ]
- }, {
- style: {
- 'margin': '10px 0',
- 'max-height': '150px',
- 'overflow-y': 'auto'
- },
- children: {
- tag: 'a',
- children: 'Show changes',
- listeners: {
- click: function(e) {
- e.preventDefault();
-
- DH.insertAfter(e.target, map(function(entry) {
- return {
- style: {
- 'margin-bottom': '5px'
- },
- children: [{
- tag: 'strong',
- children: entry.version
- }, ' ', {
- tag: 'em',
- children: ['(', entry.date, ')']
- }, {
- style: {
- 'padding': '0 0 2px 10px',
- 'white-space': 'pre'
- },
- children: [].concat(entry.note).join('\n')
- }]
- };
- }, [].concat(changelog)));
-
- DH.remove(e.target);
- }
- }
- }
- }, {
- children: map(function(text, handler) {
- return DH.build({
- tag: 'button',
- attributes: {
- 'type': 'button',
- 'class': 'yt-uix-button yt-uix-button-default'
- },
- style: {
- 'margin-right': '10px',
- 'padding': '5px 15px'
- },
- children: text,
- listeners: {
- 'click': handler
- }
- });
- }, ['Update', 'Dismiss'], [openDownloadSite, removePopup])
- }]
- }
- }));
- }
-
- function removePopup() {
- document.body.removeChild(popup);
- }
-
- function openDownloadSite() {
- removePopup();
- unsafeWindow.open(buildURL(Meta.site + '/download', {version: Meta.version}));
- }
- })();
-
- /*
- * Migrations.
- */
-
- (function(currentVersion) {
- var previousVersion = Config.get('version') || scriptStorage.getItem('version') || '1.0';
-
- if (previousVersion == currentVersion) {
- return;
- }
-
- previousVersion = map(Number, previousVersion.split('.'));
-
- each([
- {
- // Added "144p" to the quality levels.
- version: '1.7', apply: function() {
- var videoQuality = Number(Config.get('video_quality'));
- if (videoQuality > 0 && videoQuality < 7) {
- Config.set('video_quality', ++videoQuality);
- }
- }
- },
- {
- // Autoplay reworked.
- version: '1.8', apply: function() {
- switch (Number(Config.get('auto_play'))) {
- case 1: // OFF > PAUSE
- Config.set('video_playback', 1);
- break;
-
- case 2: // AUTO > AUTO PAUSE
- Config.set('video_playback', 3);
- break;
- }
-
- Config.del('auto_play');
- }
- },
- {
- // Added "1440p" to the quality levels.
- version: '1.10', apply: function() {
- var videoQuality = Number(Config.get('video_quality'));
- if (videoQuality > 6) {
- Config.set('video_quality', ++videoQuality);
- }
- }
- },
- {
- // Introduced the ScopedStorage class.
- version: '1.14', apply: function() {
- // Using a unique ScopedStorage for config outside of GM.
- each(['video_playback', 'video_quality', 'player_size', 'version'], function(i, key) {
- var value = scriptStorage.getItem(key);
-
- if (value) {
- Config.set(key, value);
-
- scriptStorage.removeItem(key);
- }
- });
-
- // Removed from the config.
- Config.del('update_checked_at');
- }
- },
- {
- // Removed "FIT" from player sizes.
- version: '1.15', apply: function() {
- if (Number(Config.get('player_size')) == 2) {
- Config.set('player_size', 1);
- }
- }
- }
- ], function(i, migration) {
- var migrationVersion = map(Number, migration.version.split('.'));
-
- for (var j = 0, parts = Math.max(previousVersion.length, migrationVersion.length); j < parts; ++j) {
- if ((previousVersion[j] || 0) < (migrationVersion[j] || 0)) {
- Console.debug('Applying migration', migration.version);
-
- migration.apply();
-
- break;
- }
- }
- });
-
- Config.set('version', currentVersion);
- })(Meta.version);
-
- /**
- * @class Player
- */
- function Player(element) {
- this._element = element;
- this._context = new Context(scriptContext, 'player' + Player._elements.indexOf(element));
-
- this._exportApiInterface();
-
- Console.debug('Player ready');
-
- this._muted = Number(this.isMuted() && ! Number(Player._storage.getItem('muted')));
-
- this._addStateChangeListener();
- }
-
- merge(Player, {
- UNSTARTED: -1,
- ENDED: 0,
- PLAYING: 1,
- PAUSED: 2,
- BUFFERING: 3,
- CUED: 5,
-
- _elements: [],
-
- _storage: new ScopedStorage(scriptStorage, 'player'),
-
- test: function(element) {
- return typeof element.getApiInterface == 'function';
- },
-
- create: function(element) {
- switch (element.tagName) {
- case 'EMBED':
- return new FlashPlayer(element);
- case 'DIV':
- return new HTML5Player(element);
- }
-
- throw 'Unknown player type';
- },
-
- initialize: function(element) {
- if (this._elements.indexOf(element) > -1) {
- throw 'Player already initialized';
- }
-
- var index = this._elements.indexOf(null);
-
- if (index > -1) {
- this._elements[index] = element;
- }
- else {
- this._elements.push(element);
- }
-
- return this.create(element);
- },
-
- invalidate: function(player) {
- this._elements[this._elements.indexOf(player._element)] = null;
-
- player.invalidate();
- }
- });
-
- Player.prototype = {
- _element: null,
- _context: null,
- _muted: 0,
-
- _exportApiInterface: function() {
- each(this._element.getApiInterface(), function(i, method) {
- if (! (method in this)) {
- this[method] = bind(this._element[method], this._element);
- }
- }, this);
- },
-
- _unexportApiInterface: function() {
- each(this._element.getApiInterface(), function(i, method) {
- if (this.hasOwnProperty(method)) {
- delete this[method];
- }
- }, this);
- },
-
- _onStateChange: function(state) {
- Console.debug('State changed to', ['unstarted', 'ended', 'playing', 'paused', 'buffering', undefined, 'cued'][state + 1]);
-
- this.onStateChange(state);
- },
-
- _addStateChangeListener: function() {
- this.addEventListener('onStateChange', this._context.publish('onPlayerStateChange', asyncProxy(bind(this._onStateChange, this))));
- },
-
- _removeStateChangeListener: function() {
- this.removeEventListener('onStateChange', this._context.publish('onPlayerStateChange', noop));
- },
-
- invalidate: function() {
- this._removeStateChangeListener();
- this._unexportApiInterface();
-
- delete this.onStateChange;
- delete this._element;
-
- Console.debug('Player invalidated');
-
- this.invalidate = noop;
- },
-
- onStateChange: noop,
-
- isPlayerState: function() {
- return Array.prototype.indexOf.call(arguments, this.getPlayerState()) > -1;
- },
-
- getVideoId: function() {
- try {
- return this.getVideoData().video_id;
- }
- catch (e) {
- return (this.getVideoUrl().match(/\bv=([\w-]+)/) || [, undefined])[1];
- }
- },
-
- restartPlayback: function() {
- if (this.getCurrentTime() > 60) {
- Console.debug('Restart threshold exceeded');
-
- return;
- }
-
- var
- code = (location.hash + location.search).match(/\bt=(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s?)?/) || new Array(4),
- seconds = (Number(code[1]) || 0) * 3600 + (Number(code[2]) || 0) * 60 + (Number(code[3]) || 0);
-
- this.seekTo(seconds, true);
- },
-
- resetState: function() {
- this.seekTo(this.getCurrentTime(), true);
- },
-
- mute: function() {
- if (! this._muted++) {
- this._element.mute();
-
- Player._storage.setItem('muted', '1');
-
- Console.debug('Player muted');
- }
- },
-
- unMute: function() {
- if (! --this._muted) {
- this._element.unMute();
-
- Player._storage.setItem('muted', '0');
-
- Console.debug('Player unmuted');
- }
- },
-
- playVideo: function() {
- this._element.playVideo();
-
- Console.debug('Playback started');
- },
-
- pauseVideo: function() {
- this._element.pauseVideo();
-
- Console.debug('Playback paused');
- },
-
- stopVideo: function() {
- this._element.stopVideo();
-
- Console.debug('Playback stopped');
- },
-
- setPlaybackQuality: function(quality) {
- this._element.setPlaybackQuality(quality);
-
- Console.debug('Quality changed to', quality);
- },
-
- setStageMode: noop
- };
-
- /**
- * @class FlashPlayer
- */
- function FlashPlayer(element) {
- Player.call(this, element);
- }
-
- FlashPlayer.prototype = extend(Player, {
- _exportApiInterface: function() {
- try {
- Player.prototype._exportApiInterface.call(this);
- }
- catch (e) {
- throw 'Player has not loaded yet';
- }
- },
-
- _unexportApiInterface: function() {
- try {
- Player.prototype._unexportApiInterface.call(this);
- }
- catch (e) {
- Console.debug('Player has unloaded');
- }
- },
-
- _removeStateChangeListener: function() {
- try {
- Player.prototype._removeStateChangeListener.call(this);
- }
- catch (e) {
- Console.debug('Player has unloaded');
- }
- }
- });
-
- /**
- * @class HTML5Player
- */
- function HTML5Player(element) {
- Player.call(this, element);
- }
-
- HTML5Player.prototype = extend(Player, {
- restartPlayback: function() {
- Player.prototype.restartPlayback.call(this);
-
- this.restartPlayback = noop;
- },
-
- setStageMode: function(enable) {
- this.setSizeStyle(true, enable);
- }
- });
-
- /**
- * @class Button
- */
- function Button(label, tooltip) {
- this._node = DH.build(this._def(tooltip, label, this._indicator = DH.build('-')));
- }
-
- Button.prototype = {
- _indicator: null,
- _node: null,
-
- _def: function(tooltip, label, indicator) {
- return {
- tag: 'button',
- attributes: {
- 'type': 'button',
- 'class': 'yt-uix-button yt-uix-button-default yt-uix-tooltip',
- 'title': tooltip
- },
- listeners: {
- 'click': bind(this._onClick, this)
- },
- style: {
- 'margin': '0 0.5%'
- },
- children: [{
- tag: 'span',
- attributes: {
- 'class': 'yt-uix-button-content'
- },
- children: label
- }, {
- tag: 'span',
- style: {
- 'font-size': '14px',
- 'margin-left': '5px'
- },
- attributes: {
- 'class': 'yt-uix-button-content'
- },
- children: indicator
- }]
- };
- },
-
- _onClick: function() {
- this.handler();
- this.refresh();
- },
-
- refresh: function() {
- this._indicator.data = this.display();
- },
-
- render: function() {
- this.refresh();
- return this._node;
- },
-
- handler: noop,
- display: noop
- };
-
- /**
- * @class PlayerOption
- */
- function PlayerOption(player, key) {
- this._player = player;
- this._key = key;
- }
-
- PlayerOption.prototype = {
- _player: null,
- _key: null,
-
- get: function() {
- return Number(Config.get(this._key) || '0');
- },
-
- set: function(value) {
- Config.set(this._key, Number(value));
- },
-
- apply: noop,
- cease: noop
- };
-
- /**
- * @class PlayerOption.Button
- */
- PlayerOption.Button = function(option) {
- Button.call(this, this.label, this.tooltip);
-
- this._option = option;
- };
-
- PlayerOption.Button.extend = function(attributes) {
- var superclass = this;
-
- function Button(option) {
- superclass.call(this, option);
- }
-
- Button.prototype = extend(superclass, attributes);
-
- return Button;
- };
-
- PlayerOption.Button.prototype = extend(Button, {
- _option: null,
-
- label: null,
- tooltip: null,
- states: null,
-
- handler: function() {
- this._option.set((this._option.get() + 1) % this.states.length);
- },
-
- display: function() {
- return this.states[this._option.get()];
- }
- });
-
- /**
- * @class SilentPlayerOption
- */
- function SilentPlayerOption(player, key) {
- PlayerOption.call(this, player, key);
- }
-
- SilentPlayerOption.prototype = extend(PlayerOption, {
- _muted: false,
-
- mute: function(state) {
- if (this._muted != state) {
- if (state) {
- this._player.mute();
- }
- else {
- this._player.unMute();
- }
-
- this._muted = state;
- }
- },
-
- cease: function() {
- this.mute(false);
- }
- });
-
- /**
- * @class VideoPlayback
- */
- function VideoPlayback(player) {
- SilentPlayerOption.call(this, player, 'video_playback');
-
- switch (this.get()) {
- case 0: // PLAY
- this._applied = true;
- break;
-
- case 3: // AUTO PAUSE
- case 4: // AUTO STOP
- // Video is visible or opened in the same window.
- if (this._isVisible() || unsafeWindow.history.length > 1) {
- this._applied = true;
- }
- // Video is opened in a background tab.
- else {
- this._handler = pageContext.protect(bind(this._handler, this));
-
- DH.on(unsafeWindow, 'focus', this._handler);
- DH.on(unsafeWindow, 'blur', this._handler);
- }
- break;
- }
- }
-
- VideoPlayback.prototype = extend(SilentPlayerOption, {
- _applied: false,
- _timer: null,
-
- _handler: function(e) {
- switch (e.type) {
- case 'focus':
- if (this._timer === null) {
- this._timer = window.setTimeout(bind(function() {
- if (this._applied) {
- this._player.resetState();
- this._player.playVideo();
-
- Console.debug('Playback autostarted');
- }
- else {
- this._applied = true;
-
- Console.debug('Playback not affected');
-
- this.mute(false);
- }
-
- DH.un(unsafeWindow, 'focus', this._handler);
- DH.un(unsafeWindow, 'blur', this._handler);
-
- this._timer = null;
- }, this), 500);
- }
- break;
-
- case 'blur':
- if (this._timer !== null) {
- clearTimeout(this._timer);
-
- this._timer = null;
- }
- break;
- }
- },
-
- // @see http://www.w3.org/TR/page-visibility/
- _isVisible: function() {
- var doc = unsafeWindow.document;
- return doc.hidden === false || doc.mozHidden === false || doc.webkitHidden === false;
- },
-
- apply: function() {
- if (! this._applied) {
- this.mute(true);
-
- if (this._player.isPlayerState(Player.PLAYING)) {
- this._applied = true;
-
- this._player.restartPlayback();
-
- if (this.get() % 2) { // (AUTO) PAUSE
- this._player.pauseVideo();
- }
- else { // (AUTO) STOP
- this._player.stopVideo();
- }
-
- this.mute(false);
- }
- }
- }
- });
-
- /**
- * @class VideoPlayback.Button
- */
- VideoPlayback.Button = PlayerOption.Button.extend({
- label: _('Playback'),
- tooltip: _('Set default playback state'),
- states: [_('START'), _('PAUSE'), _('STOP'), _('AUTO PAUSE'), _('AUTO STOP')]
- });
-
- /**
- * @class VideoQuality
- */
- function VideoQuality(player) {
- SilentPlayerOption.call(this, player, 'video_quality');
-
- this._applied = ! this.get();
- }
-
- VideoQuality.prototype = extend(SilentPlayerOption, {
- _applied: false,
-
- apply: function() {
- if (! this._applied) {
- this.mute(true);
-
- if (this._player.isPlayerState(Player.PLAYING, Player.PAUSED)) {
- this._applied = true;
-
- var quality = ['tiny', 'small', 'medium', 'large', 'hd720', 'hd1080', 'hd1440', 'highres'][this.get() - 1];
-
- if (quality != this._player.getPlaybackQuality()) {
- this._player.restartPlayback();
- this._player.setPlaybackQuality(quality);
- }
-
- asyncCall(this.apply, this);
- }
- } else if (this._player.isPlayerState(Player.PLAYING, Player.CUED)) {
- this.mute(false);
- }
- }
- });
-
- /**
- * @class VideoQuality.Button
- */
- VideoQuality.Button = PlayerOption.Button.extend({
- label: _('Quality'),
- tooltip: _('Set default video quality'),
- states: [_('AUTO'), '144p', '240p', '360p', '480p', '720p', '1080p', '1440p', _('ORIGINAL')]
- });
-
- /**
- * @class PlayerSize
- */
- function PlayerSize(player) {
- PlayerOption.call(this, player, 'player_size');
- }
-
- PlayerSize.prototype = extend(PlayerOption, {
- apply: function() {
- var mode = this.get(), rules = [];
-
- switch (mode) {
- case 4: // 1080p
- rules.push(
- '.watch-stage-mode .player-width {',
- 'left: -960.0px !important;',
- 'width: 1920px !important;',
- '}',
-
- '.watch-stage-mode .player-height {',
- 'height: 1110px !important;',
- '}',
-
- '.watch-stage-mode .html5-video-content[style*="640"], .watch-stage-mode .html5-video-content[style*="360"], .watch-stage-mode .html5-main-video[style*="640"], .watch-stage-mode .html5-main-video[style*="360"]', '{',
- 'transform: matrix(3, 0, 0, 3, 640, 360) !important; -o-transform: matrix(3, 0, 0, 3, 640, 360) !important; -moz-transform: matrix(3, 0, 0, 3, 640, 360) !important; -webkit-transform: matrix(3, 0, 0, 3, 640, 360) !important;',
- '}',
-
- '.watch-stage-mode .html5-video-content[style*="854"], .watch-stage-mode .html5-video-content[style*="480"], .watch-stage-mode .html5-main-video[style*="854"], .watch-stage-mode .html5-main-video[style*="480"]', '{',
- 'transform: matrix(2.24824, 0, 0, 2.24824, 533, 299.578) !important; -o-transform: matrix(2.24824, 0, 0, 2.24824, 533, 299.578) !important; -moz-transform: matrix(2.24824, 0, 0, 2.24824, 533, 299.578) !important; -webkit-transform: matrix(2.24824, 0, 0, 2.24824, 533, 299.578) !important;',
- '}',
-
- '.watch-stage-mode .html5-video-content[style*="1280"], .watch-stage-mode .html5-video-content[style*="720"], .watch-stage-mode .html5-main-video[style*="1280"], .watch-stage-mode .html5-main-video[style*="720"]', '{',
- 'transform: matrix(1.5, 0, 0, 1.5, 320, 180) !important; -o-transform: matrix(1.5, 0, 0, 1.5, 320, 180) !important; -moz-transform: matrix(1.5, 0, 0, 1.5, 320, 180) !important; -webkit-transform: matrix(1.5, 0, 0, 1.5, 320, 180) !important;',
- '}'
- );
-
- break;
-
- case 3: // 720p
- rules.push(
- '.watch-stage-mode .player-width {',
- 'left: -640.0px !important;',
- 'width: 1280px !important;',
- '}',
-
- '.watch-stage-mode .player-height {',
- 'height: 750px !important;',
- '}',
-
- '.watch-stage-mode .html5-video-content[style*="640"], .watch-stage-mode .html5-video-content[style*="360"], .watch-stage-mode .html5-main-video[style*="640"], .watch-stage-mode .html5-main-video[style*="360"]', '{',
- 'transform: matrix(2, 0, 0, 2, 320, 180) !important; -o-transform: matrix(2, 0, 0, 2, 320, 180) !important; -moz-transform: matrix(2, 0, 0, 2, 320, 180) !important; -webkit-transform: matrix(2, 0, 0, 2, 320, 180) !important;',
- '}',
-
- '.watch-stage-mode .html5-video-content[style*="854"], .watch-stage-mode .html5-video-content[style*="480"], .watch-stage-mode .html5-main-video[style*="854"], .watch-stage-mode .html5-main-video[style*="480"]', '{',
- 'transform: matrix(1.49883, 0, 0, 1.49883, 213, 119.719) !important; -o-transform: matrix(1.49883, 0, 0, 1.49883, 213, 119.719) !important; -moz-transform: matrix(1.49883, 0, 0, 1.49883, 213, 119.719) !important; -webkit-transform: matrix(1.49883, 0, 0, 1.49883, 213, 119.719) !important;',
- '}'
- );
-
- break;
-
- case 2: // 480p
- rules.push(
- '.watch-stage-mode .player-width {',
- 'left: -427.0px !important;',
- 'width: 854px !important;',
- '}',
-
- '.watch-stage-mode .player-height {',
- 'height: 510px !important;',
- '}',
-
- '.watch-stage-mode .html5-video-content[style*="640"], .watch-stage-mode .html5-video-content[style*="360"], .watch-stage-mode .html5-main-video[style*="640"], .watch-stage-mode .html5-main-video[style*="360"]', '{',
- 'transform: matrix(1.33438, 0, 0, 1.33438, 107, 60.1875) !important; -o-transform: matrix(1.33438, 0, 0, 1.33438, 107, 60.1875) !important; -moz-transform: matrix(1.33438, 0, 0, 1.33438, 107, 60.1875) !important; -webkit-transform: matrix(1.33438, 0, 0, 1.33438, 107, 60.1875) !important;',
- '}',
-
- '.watch-stage-mode .html5-video-content[style*="1280"], .watch-stage-mode .html5-video-content[style*="720"], .watch-stage-mode .html5-main-video[style*="1280"], .watch-stage-mode .html5-main-video[style*="720"]', '{',
- 'transform: matrix(0.667188, 0, 0, 0.667188, -213, -119.812) !important; -o-transform: matrix(0.667188, 0, 0, 0.667188, -213, -119.812) !important; -moz-transform: matrix(0.667188, 0, 0, 0.667188, -213, -119.812) !important; -webkit-transform: matrix(0.667188, 0, 0, 0.667188, -213, -119.812) !important;',
- '}'
- );
-
- break;
-
- case 1: // WIDE
- break;
-
- default:
- return;
- }
-
- if (rules.length) {
- rules.push(
- '.watch-stage-mode .html5-main-video {',
- 'z-index: -1;',
- '}'
- );
-
- DH.id('yays-player-size') || DH.append(document.body, {
- tag: 'style',
- attributes: {
- 'id': 'yays-player-size',
- 'type': 'text/css'
- },
- children: rules
- });
- }
-
- var page = DH.id('page');
-
- DH.delClass(page, 'watch-non-stage-mode');
- DH.addClass(page, 'watch-stage-mode watch-wide');
-
- this._player.setStageMode(true);
-
- Console.debug('Size set to', ['wide', '480p', '720p', '1080p'][mode - 1]);
- }
- });
-
- /**
- * @class PlayerSize.Button
- */
- PlayerSize.Button = PlayerOption.Button.extend({
- label: _('Size'),
- tooltip: _('Set default player size'),
- states: [_('AUTO'), _('WIDE'), '480p', '720p', '1080p']
- });
-
- /**
- * @class UI
- * Abstract UI class.
- */
- function UI(content) {
- this.content = content;
- this.button = DH.build(this._def.button(bind(this.toggle, this)));
- this.panel = DH.build(this._def.panel(content));
- }
-
- merge(UI, {
- instance: null,
-
- initialize: function(type, content) {
- if (this.instance) {
- this.instance.destroy();
- }
-
- return this.instance = new type(content);
- }
- });
-
- UI.prototype = {
- _def: {
- icon: function(def) {
- def = merge({tag: 'img', attributes: {}}, def);
-
- def.attributes.src = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAs0lEQVQoz4VRuRGDQAxcQDNLeKEzl2BCl+CQkIyjA3dCCYQugxJowWQ4AocEnnPAWTxjhlUirUar0y2wAGs6Otb4DyZ0PpKZjQAmcpVOMjwQezaTlzzlJvGnA8BGJ7fRABHvsNjDSd4hLloOKMZgDFBgUO48v91RlWg9U6/K1UU6EuIAIYD8Jzyv8EkOgKUe1U8NWvbKlYJW1QwqVpsN7eFHTR6kNCvhnpaG6dKTXbNwZPcX5uNiEvATXN0AAAAASUVORK5CYII=';
-
- return def;
- },
-
- button: function(click) {
- return {
- listeners: {
- 'click': click
- }
- };
- },
-
- panel: function(content) {
- return [{
- style: {
- 'margin-bottom': '10px'
- },
- children: [{
- tag: 'strong',
- children: _('Player settings')
- }, {
- tag: 'a',
- attributes: {
- 'href': Meta.site,
- 'target': '_blank'
- },
- style: {
- 'margin-left': '4px',
- 'vertical-align': 'super',
- 'font-size': '10px'
- },
- children: _('Help')
- }]
- }, {
- style: {
- 'text-align': 'center'
- },
- children: content.render()
- }];
- }
- },
-
- content: null,
- button: null,
- panel: null,
-
- destroy: function() {
- DH.remove(this.button);
- DH.remove(this.panel);
- },
-
- toggle: function() {
- this.content.refresh();
- }
- };
-
- /**
- * @class UI.Content
- */
- UI.Content = function(buttons) {
- this._buttons = buttons;
- };
-
- UI.Content.prototype = {
- _buttons: null,
-
- render: function() {
- return map(function(button) { return button.render(); }, this._buttons);
- },
-
- refresh: function() {
- each(this._buttons, function(i, button) { button.refresh(); });
- }
- };
-
- /**
- * @class UI.Requirement
- */
- UI.Requirement = function(queries) {
- this._queries = [].concat(queries);
- };
-
- UI.Requirement.prototype = {
- _queries: null,
-
- test: function() {
- return DH.query(this._queries.join(', ')).length >= this._queries.length;
- }
- };
-
- /**
- * @class WatchUI
- */
- function WatchUI(buttons) {
- UI.call(this, new UI.Content(buttons));
- }
-
- WatchUI.prototype = extend(UI, {
- _def: {
- panel: function(content) {
- return {
- attributes: {
- 'id': 'action-panel-yays',
- 'class': 'action-panel-content hid',
- 'data-panel-loaded': 'true'
- },
- children: UI.prototype._def.panel(content)
- };
- }
- }
- });
-
- /**
- * @class Watch8UI
- */
- function Watch8UI(buttons) {
- WatchUI.call(this, buttons);
-
- DH.append(DH.id('watch8-secondary-actions'), this.button);
- DH.append(DH.id('watch-action-panels'), this.panel);
- }
-
- Watch8UI.requirement = new UI.Requirement(['#page.watch #watch8-secondary-actions', '#page.watch #watch-action-panels']);
-
- Watch8UI.prototype = extend(WatchUI, {
- _def: {
- button: function(click) {
- return {
- tag: 'span',
- children: {
- tag: 'button',
- attributes: {
- 'type': 'button',
- 'class': 'action-panel-trigger yt-uix-button yt-uix-button-empty yt-uix-button-has-icon yt-uix-button-opacity yt-uix-button-size-default yt-uix-tooltip',
- 'data-button-toggle': 'true',
- 'data-trigger-for': 'action-panel-yays',
- 'data-tooltip-text': _('Player settings')
- },
- listeners: {
- 'click': click
- },
- children: {
- tag: 'span',
- attributes: {
- 'class': 'yt-uix-button-icon-wrapper'
- },
- children: UI.prototype._def.icon({
- attributes: {
- 'class': 'yt-uix-button-icon'
- }
- })
- }
- }
- };
- },
-
- panel: WatchUI.prototype._def.panel
- }
- });
-
- /**
- * @class ChannelUI
- */
- function ChannelUI(buttons) {
- UI.call(this, new UI.Content(buttons));
-
- DH.append(DH.id('channel-navigation-menu'), {
- tag: 'li',
- children: [this.button, this.panel]
- });
- }
-
- ChannelUI.requirement = new UI.Requirement('#channel-navigation-menu');
-
- ChannelUI.prototype = extend(UI, {
- _def: {
- button: function(click) {
- return {
- tag: 'button',
- attributes: {
- 'type': 'button',
- 'role': 'button',
- 'class': 'yt-uix-button yt-uix-button-empty yt-uix-button-epic-nav-item yt-uix-tooltip flip',
- 'data-button-menu-id': 'yays-panel-dropdown',
- 'data-tooltip-text': _('Player settings')
- },
- style: {
- 'position': 'absolute',
- 'right': '20px',
- 'width': '30px'
- },
- listeners: {
- 'click': click
- },
- children: {
- tag: 'span',
- attributes: {
- 'class': 'yt-uix-button-icon-wrapper'
- },
- style: {
- 'opacity': '0.75'
- },
- children: UI.prototype._def.icon()
- }
- };
- },
-
- panel: function(content) {
- return {
- attributes: {
- 'id': 'yays-panel-dropdown',
- 'class': 'epic-nav-item-dropdown hid'
- },
- style: {
- 'padding': '5px 10px 10px',
- 'width': '450px'
- },
- children: UI.prototype._def.panel(content)
- };
- }
- }
- });
-
- /*
- * Ready callbacks.
- */
-
- function onReady(player) {
- var
- videoPlayback = new VideoPlayback(player),
- videoQuality = new VideoQuality(player),
- playerSize = new PlayerSize(player),
- previousVideo = player.getVideoId();
-
- player.onStateChange = function() {
- try {
- var currentVideo = player.getVideoId();
-
- if (currentVideo == previousVideo) {
- videoQuality.apply();
- videoPlayback.apply();
- }
- else {
- videoQuality.cease();
- videoPlayback.cease();
-
- throw null;
- }
- }
- catch (e) {
- Player.invalidate(player);
-
- asyncCall(onPlayerReady);
- }
- };
-
- videoQuality.apply();
- videoPlayback.apply();
-
- if (Watch8UI.requirement.test()) {
- playerSize.apply();
-
- UI.initialize(Watch8UI, [
- new VideoQuality.Button(videoQuality),
- new PlayerSize.Button(playerSize),
- new VideoPlayback.Button(videoPlayback)
- ]);
- }
- else if (ChannelUI.requirement.test()) {
- UI.initialize(ChannelUI, [
- new VideoQuality.Button(videoQuality),
- new VideoPlayback.Button(videoPlayback)
- ]);
- }
- }
-
- function onPlayerReady() {
- each(DH.query('video, embed'), function(i, node) {
- var player = DH.closest(node, function(node) { return Player.test(DH.unwrap(node)); });
-
- if (player) {
- try {
- player = Player.initialize(DH.unwrap(player));
-
- onReady(player);
-
- Console.debug('Initialization finished');
- }
- catch (e) {
- Console.debug(e);
- }
- }
- });
- }
-
- onPlayerReady();
-
- // FIXME: The call to an exported function is rejected if a function (or an
- // object with methods) is passed as argument.
- //
- // This restriction can be lifted in FF 33+ by setting the 'allowCallbacks'
- // option when exporting the function.
-
- var node = DH.build({
- tag: 'script',
- attributes: {
- 'type': 'text/javascript'
- }
- });
-
- node.text = 'function onYouTubePlayerReady() { ' + scriptContext.publish('onPlayerReady', intercept(unsafeWindow.onYouTubePlayerReady, asyncProxy(onPlayerReady))) + '(); }';
-
- document.documentElement.appendChild(node);
- document.documentElement.removeChild(node);
-
- } // YAYS
-
- if (window.top === window.self) {
- if (this.unsafeWindow) { // Greasemonkey.
- YAYS(unsafeWindow);
- }
- else {
- var node = document.createElement('script');
- node.setAttribute('type', 'text/javascript');
- node.text = '(' + YAYS.toString() + ')(window);';
-
- document.documentElement.appendChild(node);
- document.documentElement.removeChild(node);
- }
- }