- // ==UserScript==
- // @name HTML5 Video Player Enhance
- // @version 2.9.4.2
- // @description To enhance the functionality of HTML5 Video Player (h5player) supporting all websites using shortcut keys similar to PotPlayer.
- // @author CY Fung
- // @match http://*/*
- // @match https://*/*
- // @run-at document-start
- // @require https://cdnjs.cloudflare.com/ajax/libs/js-sha256/0.9.0/sha256.min.js
- // @require https://cdnjs.cloudflare.com/ajax/libs/mathjs/9.3.2/math.js
- // @namespace https://greasyfork.org/users/371179
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant unsafeWindow
- // ==/UserScript==
-
-
- /**
- * Remarks
- * This script support modern browser only with ES6+.
- * fullscreen and pointerLock buggy in shadowRoot
- * Space Pause not success
- * shift F key issue
- **/
- (function $$($$uWin) {
- 'use strict';
-
- if (!document || !document.documentElement) return window.requestAnimationFrame($$);
-
- const $qmt=($$uWin.queueMicrotask?$$uWin.queueMicrotask:function (callback) {
- Promise.resolve()
- .then(callback)
- .catch(e => setTimeout(() => { throw e; })); // report exceptions
- }).bind($$uWin)
-
- const $bz={boosted:false}
- //document.nmm=[]
-
-
- const $rAf = $$uWin.requestAnimationFrame;
- const $cAf = $$uWin.cancelAnimationFrame;
-
- !(function(window) {
-
-
- const $$setTimeout = window.setTimeout
- const $$clearTimeout = window.clearTimeout
- const $$requestAnimationFrame = window.requestAnimationFrame;
- const $$cancelAnimationFrame = window.cancelAnimationFrame;
-
- window.setTimeout = function() {
- let f = arguments[0]
- let d = arguments[1] || 0;
- if ($bz.boosted && typeof f == 'function' && (arguments.length == 2 || arguments.length == 1)) {
- // f : function arguments.length = 1 or 2
- // setTimeout(f,d)
- if(d>40){
- if(!f.__timeout__g__){
- f.__timeout__g__= function(){
- $qmt(()=>f.apply(this,arguments))
- }
- }
- return ($$setTimeout.call(window, f.__timeout__g__, d) << 1) || 0
- }else{
- return (($$requestAnimationFrame.call(window, f) << 1) | 1 ) || 0
- }
-
- }
- return ($$setTimeout.apply(this, arguments) << 1) || 0
-
- }
-
- window.clearTimeout = function() {
- let cid = arguments[0]
- if (cid > 0 && arguments.length===1) {
- let res;
- if (cid & 1) {
- cid = cid >> 1;
- res = $$cancelAnimationFrame.call(this, cid);
- } else {
- cid = cid >> 1;
- res = $$clearTimeout.call(this,cid)
- }
- return res;
- } else {
- return $$clearTimeout.apply(this, arguments);
- }
- }
-
- })(window.unsafeWindow || window);
-
-
- const $$vPlay = HTMLVideoElement.prototype.play
- const $$vPause = HTMLVideoElement.prototype.pause
- HTMLVideoElement.prototype.play=function(){
- return new Promise((resolve, reject)=>{
- $qmt(()=>{
-
- if(this.__play_cid__>0) window.cancelAnimationFrame(this.__play_cid__);
-
- this.__play_cid__=window.requestAnimationFrame(()=>{
-
- if(this.__play_cid__>0){
- this.__play_cid__=0;
- if(this.paused){
-
- $$vPlay.call(this).then(resolve).catch(e=>{
-
- if(e instanceof DOMException)return;
- throw e;
-
- })
-
- }
- }
-
- })
-
- })
-
- })
-
- }
-
-
- HTMLVideoElement.prototype.pause=function(){
-
-
- $qmt(()=>{
-
-
- if(this.__play_cid__>0) this.__play_cid__=window.cancelAnimationFrame(this.__play_cid__);
-
- this.__play_cid__=window.requestAnimationFrame(()=>{
- if(this.__play_cid__>0){
- this.__play_cid__=0;
-
-
- if(!this.paused){
- $$vPause.call(this)
-
-
-
- }
-
- }
-
-
- })
- })
-
-
- }
-
-
-
- let _debug_h5p_logging_ = false;
-
- try {
- _debug_h5p_logging_ = +window.localStorage.getItem('_h5_player_sLogging_') > 0
- } catch (e) {}
-
-
-
- const SHIFT = 1;
- const CTRL = 2;
- const ALT = 4;
- const TERMINATE = 0x842;
- const _sVersion_ = 1817;
- const str_postMsgData = '__postMsgData__'
- const DOM_ACTIVE_FOUND = 1;
- const DOM_ACTIVE_SRC_LOADED = 2;
- const DOM_ACTIVE_ONCE_PLAYED = 4;
- const DOM_ACTIVE_MOUSE_CLICK = 8;
- const DOM_ACTIVE_MOUSE_IN = 16;
- const DOM_ACTIVE_DELAYED_PAUSED = 32;
- const DOM_ACTIVE_INVALID_PARENT = 2048;
-
- var console = {};
-
- console.log = function() {
- window.console.log(...['[h5p]', ...arguments])
- }
- console.error = function() {
- window.console.error(...['[h5p]', ...arguments])
- }
-
- function makeNoRoot(shadowRoot) {
- const doc = shadowRoot.ownerDocument || document;
- const htmlInShadowRoot = doc.createElement('noroot'); // pseudo element
- const childNodes = [...shadowRoot.childNodes]
- shadowRoot.insertBefore(htmlInShadowRoot, shadowRoot.firstChild)
- for (const childNode of childNodes) htmlInShadowRoot.appendChild(childNode);
- return shadowRoot.querySelector('noroot');
- }
-
- let _endlessloop = null;
- const isIframe = (window.top !== window.self && window.top && window.self);
- const shadowRoots = [];
-
- const getRoot = (elm) => elm.getRootNode instanceof Function ? elm.getRootNode() : (elm.ownerDocument || null);
-
- const isShadowRoot = (elm) => (elm && ('host' in elm)) ? elm.nodeType == 11 && !!elm.host && elm.host.nodeType == 1 : null; //instanceof ShadowRoot
-
-
- const domAppender = (d) => d.querySelector('head') || d.querySelector('html') || d.querySelector('noroot') || null;
-
- const playerConfs = {}
-
- const hanlderResizeVideo = (entries) => {
- const detected_changes = {};
- for (let entry of entries) {
- const player = entry.target.nodeName == "VIDEO" ? entry.target : entry.target.querySelector("VIDEO[_h5ppid]");
- if (!player) continue;
- const vpid = player.getAttribute('_h5ppid');
- if (!vpid) continue;
- if (vpid in detected_changes) continue;
- detected_changes[vpid] = true;
- const wPlayer = $hs.getPlayerBlockElement(player, true)
- if (!wPlayer) continue;
- const layoutBox = wPlayer.parentNode
- if (!layoutBox) continue;
- const tipsDom = layoutBox.querySelector('[_potTips_]');
- if (!tipsDom) continue;
-
- $hs.fixNonBoxingVideoTipsPosition(tipsDom, player);
- window.requestAnimationFrame(() => $hs.fixNonBoxingVideoTipsPosition(tipsDom, player))
-
- }
- };
-
- const $mb = {
-
-
- nightly_isSupportQueueMicrotask: function() {
-
- if ('_isSupportQueueMicrotask' in $mb) return $mb._isSupportQueueMicrotask;
-
- $mb._isSupportQueueMicrotask = false;
- $mb.queueMicrotask = window.queueMicrotask;
- if (typeof $mb.queueMicrotask == 'function') {
- $mb._isSupportQueueMicrotask = true;
- }
-
- return $mb._isSupportQueueMicrotask;
-
- },
-
- stable_isSupportAdvancedEventListener: function() {
-
- if ('_isSupportAdvancedEventListener' in $mb) return $mb._isSupportAdvancedEventListener
- let prop = 0;
- document.createAttribute('z').addEventListener('', null, {
- get passive() {
- prop++;
- },
- get once() {
- prop++;
- }
- });
- return ($mb._isSupportAdvancedEventListener = (prop == 2));
- }
-
- }
-
-
- const $ws = {
- requestAnimationFrame,
- cancelAnimationFrame,
- MutationObserver,
- setInterval,
- clearInterval
- }
- //throw Error if your browser is too outdated. (eg ES6 script, no such window object)
-
- Element.prototype.__matches__ = (Element.prototype.matches || Element.prototype.matchesSelector ||
- Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector ||
- Element.prototype.oMatchesSelector || Element.prototype.webkitMatchesSelector ||
- Element.prototype.matches()); // throw Error if not supported
-
-
- Element.prototype.__requestPointerLock__ = (Element.prototype.requestPointerLock ||
- Element.prototype.mozRequestPointerLock || Element.prototype.webkitRequestPointerLock || function() {});
-
- // Ask the browser to release the pointer
- Document.prototype.__exitPointerLock__ = (Document.prototype.exitPointerLock ||
- Document.prototype.mozExitPointerLock || Document.prototype.webkitExitPointerLock || function() {});
-
- // built-in hash - https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest
- async function digestMessage(message) {
- return window.sha256(message)
- }
-
- const dround = (x) => ~~(x + .5);
-
- const jsonStringify_replacer = function(key, val) {
- if (val && (val instanceof Element || val instanceof Document)) return val.toString();
- return val; // return as is
- };
-
- const jsonParse = function() {
- try {
- return JSON.parse.apply(this, arguments)
- } catch (e) {}
- return null;
- }
- const jsonStringify = function(obj) {
- try {
- return JSON.stringify.call(this, obj, jsonStringify_replacer)
- } catch (e) {}
- return null;
- }
-
- function _postMsg() {
- //async is needed. or error handling for postMessage
- const [win, tag, ...data] = arguments;
- if (typeof tag == 'string') {
- let postMsgObj = {
- tag,
- passing: true,
- winOrder: _postMsg.a
- }
- try {
- let k = 'msg-' + (+new Date)
- win.document[str_postMsgData] = win.document[str_postMsgData] || {}
- win.document[str_postMsgData][k] = data; //direct
- postMsgObj.str = k;
- postMsgObj.stype = 1;
- } catch (e) {}
- if (!postMsgObj.stype) {
- postMsgObj.str = jsonStringify({
- d: data
- })
- if (postMsgObj.str && postMsgObj.str.length) postMsgObj.stype = 2;
- }
- if (!postMsgObj.stype) {
- postMsgObj.str = "" + data;
- postMsgObj.stype = 0;
- }
- win.postMessage(postMsgObj, '*');
- }
-
- }
-
- function postMsg() {
- let win = window;
- let a = 0;
- while (win = win.parent) {
- _postMsg.a = ++a;
- _postMsg(win, ...arguments)
- if (win == top) break;
- }
- }
-
-
- function crossBrowserTransition(type) {
- if (crossBrowserTransition['_result_' + type]) return crossBrowserTransition['_result_' + type]
- let el = document.createElement("fakeelement");
-
- const capital = (x) => x[0].toUpperCase() + x.substr(1);
- const capitalType = capital(type);
-
- const transitions = {
- [type]: `${type}end`,
- [`O${capitalType}`]: `o${capitalType}End`,
- [`Moz${capitalType}`]: `${type}end`,
- [`Webkit${capitalType}`]: `webkit${capitalType}End`,
- [`MS${capitalType}`]: `MS${capitalType}End`
- }
-
- for (let styleProp in transitions) {
- if (el.style[styleProp] !== undefined) {
- return (crossBrowserTransition['_result_' + type] = transitions[styleProp]);
- }
- }
- }
-
- function isInOperation(elm) {
- let elmInFocus = elm || document.activeElement;
- if (!elmInFocus) return false;
- let res1 = elmInFocus.__matches__(
- 'a[href],link[href],button,input:not([type="hidden"]),select,textarea,iframe,frame,menuitem,[draggable],[contenteditable]'
- );
- return res1;
- }
-
- const fn_toString = (f, n = 50) => {
- let s = (f + "");
- if (s.length > 2 * n + 5) {
- s = s.substr(0, n) + ' ... ' + s.substr(-n);
- }
- return s
- };
-
- function consoleLog() {
- if (!_debug_h5p_logging_) return;
- if (isIframe) postMsg('consoleLog', ...arguments);
- else console.log.apply(console, arguments);
- }
-
- function consoleLogF() {
- if (isIframe) postMsg('consoleLog', ...arguments);
- else console.log.apply(console, arguments);
- }
-
- class AFLooperArray extends Array {
- constructor() {
- super();
- this.activeLoopsCount = 0;
- this.cid = 0;
- this.loopingFrame = this.loopingFrame.bind(this);
- }
-
- loopingFrame() {
- if (!this.cid) return; //cancelled
- for (const opt of this) {
- if (opt.isFunctionLooping) opt.qn(opt.fn);
- }
- }
-
- get isArrayLooping() {
- return this.cid > 0;
- }
-
- loopStart() {
- this.cid = $ws.setInterval(this.loopingFrame, 300);
- }
- loopStop() {
- if (this.cid) $ws.clearInterval(this.cid);
- this.cid = 0;
- }
- appendLoop(fn) {
- if (typeof fn != 'function' || !this) return;
- const opt = new AFLooperFunc(fn, this);
- super.push(opt);
- return opt;
- }
- }
-
- class AFLooperFunc {
- constructor(fn, bind) {
- this._looping = false;
- this.fn = fn.bind(this);
- if ($mb.nightly_isSupportQueueMicrotask()) this.qn = $mb.queueMicrotask;
- else this.qn = this.fn; //qn(fn) = qn() = fn()
- this.bind = bind;
- }
- get isFunctionLooping() {
- return this._looping;
- }
- loopingStart() {
- if (this._looping === false) {
- this._looping = true;
- if (++this.bind.activeLoopsCount == 1) this.bind.loopStart();
- }
- }
- loopingStop() {
- if (this._looping === true) {
- this._looping = false;
- if (--this.bind.activeLoopsCount == 0) this.bind.loopStop();
- }
- }
- }
-
- function decimalEqual(a, b) {
- return Math.round(a * 100000000) == Math.round(b * 100000000)
- }
-
- function nonZeroNum(a) {
- return a > 0 || a < 0;
- }
-
- class PlayerConf {
-
- get scaleFactor() {
- return this.mFactor * this.vFactor;
- }
-
- cssTransform() {
-
- const playerConf = this;
- const player = playerConf.domElement;
- if (!player) return;
- const videoScale = playerConf.scaleFactor;
-
- let {
- x,
- y
- } = playerConf.translate;
-
- let [_x, _y] = ((playerConf.rotate % 180) == 90) ? [y, x] : [x, y];
-
-
- if ((playerConf.rotate % 360) == 270) _x = -_x;
- if ((playerConf.rotate % 360) == 90) _y = -_y;
-
- var s = [
- playerConf.rotate > 0 ? 'rotate(' + playerConf.rotate + 'deg)' : '',
- !decimalEqual(videoScale, 1.0) ? 'scale(' + videoScale + ')' : '',
- (nonZeroNum(_x) || nonZeroNum(_y)) ? `translate(${_x}px, ${_y}px)` : '',
- ];
-
- player.style.transform = s.join(' ').trim()
-
- }
-
- constructor() {
-
- this.translate = {
- x: 0,
- y: 0
- };
- this.rotate = 0;
- this.mFactor = 1.0;
- this.vFactor = 1.0;
- this.fps = 30;
- this.filter_key = {};
- this.filter_view_units = {
- 'hue-rotate': 'deg',
- 'blur': 'px'
- };
- this.filterReset();
-
- }
-
- setFilter(prop, f) {
-
- let oldValue = this.filter_key[prop];
- if (typeof oldValue != 'number') return;
- let newValue = f(oldValue)
- if (oldValue != newValue) {
-
- newValue = +newValue.toFixed(6); //javascript bug
-
- }
-
- this.filter_key[prop] = newValue
- this.filterSetup();
-
- return newValue;
-
-
-
- }
-
- filterSetup(options) {
-
- let ums = GM_getValue("unsharpen_mask")
- if (!ums) ums = ""
-
- let view = []
- let playerElm = $hs.player();
- if (!playerElm) return;
- for (let view_key in this.filter_key) {
- let filter_value = +((+this.filter_key[view_key] || 0).toFixed(3))
- let addTo = true;
- switch (view_key) {
- case 'brightness':
- /* fall through */
- case 'contrast':
- /* fall through */
- case 'saturate':
- if (decimalEqual(filter_value, 1.0)) addTo = false;
- break;
- case 'hue-rotate':
- /* fall through */
- case 'blur':
- if (decimalEqual(filter_value, 0.0)) addTo = false;
- break;
- }
- let view_unit = this.filter_view_units[view_key] || ''
- if (addTo) view.push(`${view_key}(${filter_value}${view_unit})`)
- this.filter_key[view_key] = Number(+this.filter_key[view_key] || 0)
- }
- if (ums) view.push(`url("#_h5p_${ums}")`);
- if (options && options.grey) view.push('url("#grey1")');
- playerElm.style.filter = view.join(' ').trim(); //performance in firefox is bad
- }
-
- filterReset() {
- this.filter_key['brightness'] = 1.0
- this.filter_key['contrast'] = 1.0
- this.filter_key['saturate'] = 1.0
- this.filter_key['hue-rotate'] = 0.0
- this.filter_key['blur'] = 0.0
- this.filterSetup()
- }
-
- }
-
- const Store = {
- prefix: '_h5_player',
- save: function(k, v) {
- if (!Store.available()) return false;
- if (typeof v != 'string') return false;
- Store.LS.setItem(Store.prefix + k, v)
- let sk = fn_toString(k + "", 30);
- let sv = fn_toString(v + "", 30);
- consoleLog(`localStorage Saved "${sk}" = "${sv}"`)
- return true;
-
- },
- read: function(k) {
- if (!Store.available()) return false;
- let v = Store.LS.getItem(Store.prefix + k)
- let sk = fn_toString(k + "", 30);
- let sv = fn_toString(v + "", 30);
- consoleLog(`localStorage Read "${sk}" = "${sv}"`);
- return v;
-
- },
- remove: function(k) {
-
- if (!Store.available()) return false;
- Store.LS.removeItem(Store.prefix + k)
- let sk = fn_toString(k + "", 30);
- consoleLog(`localStorage Removed "${sk}"`)
- return true;
- },
- clearInvalid: function(sVersion) {
- if (!Store.available()) return false;
-
- //let sVersion=1814;
- if (+Store.read('_sVersion_') < sVersion) {
- Store._keys()
- .filter(s => s.indexOf(Store.prefix) === 0)
- .forEach(key => window.localStorage.removeItem(key))
- Store.save('_sVersion_', sVersion + '')
- return 2;
- }
- return 1;
-
- },
- available: function() {
- if (Store.LS) return true;
- if (!window) return false;
- const localStorage = window.localStorage;
- if (!localStorage) return false;
- if (typeof localStorage != 'object') return false;
- if (!('getItem' in localStorage)) return false;
- if (!('setItem' in localStorage)) return false;
- Store.LS = localStorage;
- return true;
-
- },
- _keys: function() {
- return Object.keys(localStorage);
- },
- _setItem: function(key, value) {
- return localStorage.setItem(key, value)
- },
- _getItem: function(key) {
- return localStorage.getItem(key)
- },
- _removeItem: function(key) {
- return localStorage.removeItem(key)
- }
-
- }
-
- const domTool = {
- nopx: (x) => +x.replace('px', ''),
- cssWH: function(m, r) {
- if (!r) r = getComputedStyle(m, null);
- let c = (x) => +x.replace('px', '');
- return {
- w: m.offsetWidth || c(r.width),
- h: m.offsetHeight || c(r.height)
- }
- },
- _isActionBox_1: function(vEl, pEl) {
-
- const vElCSS = domTool.cssWH(vEl);
- let vElCSSw = vElCSS.w;
- let vElCSSh = vElCSS.h;
-
- let vElx = vEl;
- const res = [];
- //let mLevel = 0;
- if (vEl && pEl && vEl != pEl && pEl.contains(vEl)) {
- while (vElx && vElx != pEl) {
- vElx = vElx.parentNode;
- let vElx_css = null;
- if (isShadowRoot(vElx)) {} else {
- vElx_css = getComputedStyle(vElx, null);
- let vElx_wp = domTool.nopx(vElx_css.paddingLeft) + domTool.nopx(vElx_css.paddingRight)
- vElCSSw += vElx_wp
- let vElx_hp = domTool.nopx(vElx_css.paddingTop) + domTool.nopx(vElx_css.paddingBottom)
- vElCSSh += vElx_hp
- }
- res.push({
- //level: ++mLevel,
- padW: vElCSSw,
- padH: vElCSSh,
- elm: vElx,
- css: vElx_css
- })
-
- }
- }
-
- // in the array, each item is the parent of video player
- //res.vEl_cssWH = vElCSS
-
- return res;
-
- },
- _isActionBox: function(vEl, walkRes, pEl_idx) {
-
- function absDiff(w1, w2, h1, h2) {
- const w = (w1 - w2),
- h = h1 - h2;
- return [(w > 0 ? w : -w), (h > 0 ? h : -h)]
- }
-
- function midPoint(rect) {
- return {
- x: (rect.left + rect.right) / 2,
- y: (rect.top + rect.bottom) / 2
- }
- }
-
- const parentCount = walkRes.length;
- if (pEl_idx >= 0 && pEl_idx < parentCount) {} else {
- return;
- }
- const pElr = walkRes[pEl_idx]
- if (!pElr.css) {
- //shadowRoot
- return true;
- }
-
- const pEl = pElr.elm;
-
- //prevent activeElement==body
- const pElCSS = domTool.cssWH(pEl, pElr.css);
-
- //check prediction of parent dimension
- const d1v = absDiff(pElCSS.w, pElr.padW, pElCSS.h, pElr.padH)
-
- const d1x = d1v[0] < 10
- const d1y = d1v[1] < 10;
-
- if (d1x && d1y) return true; //both edge along the container - fit size
- if (!d1x && !d1y) return false; //no edge along the container - body contain the video element, fixed width&height
-
- //case: youtube video fullscreen
-
- //check centre point
-
- const pEl_rect = pEl.getBoundingClientRect()
- const vEl_rect = vEl.getBoundingClientRect()
-
- const pEl_center = midPoint(pEl_rect)
- const vEl_center = midPoint(vEl_rect)
-
- const d2v = absDiff(pEl_center.x, vEl_center.x, pEl_center.y, vEl_center.y);
-
- const d2x = d2v[0] < 10;
- const d2y = d2v[1] < 10;
-
- return (d2x && d2y);
-
- },
- getRect: function(element) {
- let rect = element.getBoundingClientRect();
- let scroll = domTool.getScroll();
- return {
- pageX: rect.left + scroll.left,
- pageY: rect.top + scroll.top,
- screenX: rect.left,
- screenY: rect.top
- };
- },
- getScroll: function() {
- return {
- left: document.documentElement.scrollLeft || document.body.scrollLeft,
- top: document.documentElement.scrollTop || document.body.scrollTop
- };
- },
- getClient: function() {
- return {
- width: document.compatMode == 'CSS1Compat' ? document.documentElement.clientWidth : document.body.clientWidth,
- height: document.compatMode == 'CSS1Compat' ? document.documentElement.clientHeight : document.body.clientHeight
- };
- },
- addStyle: //GM_addStyle,
- function(css, head) {
- if (!head) {
- let _doc = document.documentElement;
- head = domAppender(_doc);
- }
- let doc = head.ownerDocument;
- let style = doc.createElement('style');
- style.type = 'text/css';
- style.textContent = css;
- head.appendChild(style);
- //console.log(document.head,style,'add style')
- return style;
- },
- eachParentNode: function(dom, fn) {
- let parent = dom.parentNode
- while (parent) {
- let isEnd = fn(parent, dom)
- parent = parent.parentNode
- if (isEnd) {
- break
- }
- }
- },
-
- hideDom: function hideDom(selector) {
- let dom = document.querySelector(selector)
- if (dom) {
- $ws.requestAnimationFrame(function() {
- dom.style.opacity = 0;
- dom.style.transform = 'translate(-9999px)';
- dom = null;
- })
- }
- }
- };
-
- const handle = {
-
-
- afPlaybackRecording: async function() {
- const opts = this;
-
- let qTime = +new Date;
- if (qTime >= opts.pTime) {
- opts.pTime = qTime + opts.timeDelta; //prediction of next Interval
- opts.savePlaybackProgress()
- }
-
- },
- savePlaybackProgress: function() {
-
- //this refer to endless's opts
- let player = this.player;
-
- let _uid = this.player_uid; //_h5p_uid_encrypted
- if (!_uid) return;
-
- let shallSave = true;
- let currentTimeToSave = ~~player.currentTime;
-
- if (this._lastSave == currentTimeToSave) shallSave = false;
-
- if (shallSave) {
-
- this._lastSave = currentTimeToSave
-
- //console.log('aasas',this.player_uid, shallSave, '_play_progress_'+_uid, currentTimeToSave)
-
- Store.save('_play_progress_' + _uid, jsonStringify({
- 't': currentTimeToSave
- }))
-
- }
- //console.log('playback logged')
-
- },
- playingWithRecording: function() {
- let player = this.player;
- if (!player.paused && !this.isFunctionLooping) {
- let player = this.player;
- let _uid = player.getAttribute('_h5p_uid_encrypted') || ''
- if (_uid) {
- this.player_uid = _uid;
- this.pTime = 0;
- this.loopingStart();
- }
- }
- }
-
- };
-
- class Momentary extends Map {
- act(uniqueId, fn_start, fn_end, delay) {
- if (!uniqueId) return;
- uniqueId = uniqueId + "";
- const last_cid = this.get(uniqueId);
- if (last_cid > 0) clearTimeout(last_cid);
- fn_start();
- const new_cid = setTimeout(fn_end, delay)
- this.set(uniqueId, new_cid)
- }
- }
-
- const momentary = new Momentary();
-
- const $hs = {
-
- /* 提示文本的字號 */
- fontSize: 16,
- enable: true,
- playerInstance: null,
- playbackRate: 1,
- /* 快進快退步長 */
- skipStep: 5,
-
- /* 獲取當前播放器的實例 */
- player: function() {
- let res = $hs.playerInstance || null;
- if (res && res.parentNode == null) {
- $hs.playerInstance = null;
- res = null;
- }
-
- if (res == null) {
- for (let k in playerConfs) {
- let playerConf = playerConfs[k];
- if (playerConf && playerConf.domElement && playerConf.domElement.parentNode) return playerConf.domElement;
- }
- }
- return res;
- },
-
- pictureInPicture: function(videoElm) {
- if (document.pictureInPictureElement) {
- document.exitPictureInPicture();
- } else if ('requestPictureInPicture' in videoElm) {
- videoElm.requestPictureInPicture()
- } else {
- $hs.tips('PIP is not supported.');
- }
- },
-
- getPlayerConf: function(video) {
-
- if (!video) return null;
- let vpid = video.getAttribute('_h5ppid') || null;
- if (!vpid) return null;
- return playerConfs[vpid] || null;
-
- },
-
- handlerVideoPlaying: function(evt) {
- const videoElm = evt.target || this || null;
-
- if (!videoElm || videoElm.nodeName != "VIDEO") return;
-
- const vpid = videoElm.getAttribute('_h5ppid')
-
- if (!vpid) return;
- if ($hs.cid_playHook > 0) clearTimeout($hs.cid_playHook);
- $hs.cid_playHook = setTimeout(function() {
- let onlyPlayed = null;
- for (var k in playerConfs) {
- if (k == vpid) {
- if (playerConfs[k].domElement.paused === false) onlyPlayed = true;
- } else if (playerConfs[k].domElement.paused === false) {
- onlyPlayed = false;
- break;
- }
- }
- if (onlyPlayed === true) {
- $hs.focusHookVDoc = getRoot(videoElm)
- $hs.focusHookVId = vpid
- }
- $bv.boostVideoPerformanceActivate();
- }, 100)
-
- const playerConf = $hs.getPlayerConf(videoElm)
-
- $hs._actionBoxObtain(videoElm);
- if (playerConf) {
- if (playerConf.timeout_pause > 0) playerConf.timeout_pause = clearTimeout(playerConf.timeout_pause);
- playerConf.lastPauseAt = 0
- playerConf.domActive |= DOM_ACTIVE_ONCE_PLAYED;
- playerConf.domActive &= ~DOM_ACTIVE_DELAYED_PAUSED;
- }
-
- $hs.swtichPlayerInstance();
-
-
-
- $hs.onVideoTriggering();
-
- if (!$hs.enable) return $hs.tips(false);
-
- if (videoElm._isThisPausedBefore_) consoleLog('resumed')
- let _pausedbefore_ = videoElm._isThisPausedBefore_
-
- if (videoElm.playpause_cid) {
- clearTimeout(videoElm.playpause_cid);
- videoElm.playpause_cid = 0;
- }
- let _last_paused = videoElm._last_paused
- videoElm._last_paused = videoElm.paused
- if (_last_paused === !videoElm.paused) {
- videoElm.playpause_cid = setTimeout(() => {
- if (videoElm.paused === !_last_paused && !videoElm.paused && _pausedbefore_) {
- $hs.tips('Playback resumed', undefined, 2500)
- }
- }, 90)
- }
-
- /* 播放的時候進行相關同步操作 */
-
- if (!videoElm._record_continuous) {
-
- /* 同步之前設定的播放速度 */
- $hs.setPlaybackRate()
-
- if (!_endlessloop) _endlessloop = new AFLooperArray();
-
- videoElm._record_continuous = _endlessloop.appendLoop(handle.afPlaybackRecording);
- videoElm._record_continuous._lastSave = -999;
-
- videoElm._record_continuous.timeDelta = 2000;
- videoElm._record_continuous.player = videoElm
- videoElm._record_continuous.savePlaybackProgress = handle.savePlaybackProgress;
- videoElm._record_continuous.playingWithRecording = handle.playingWithRecording;
- }
-
- videoElm._record_continuous.playingWithRecording(videoElm); //try to start recording
-
- videoElm._isThisPausedBefore_ = false;
-
- },
- handlerVideoPause: function(evt) {
-
- const videoElm = evt.target || this || null;
-
- if (!videoElm || videoElm.nodeName != "VIDEO") return;
-
- const vpid = videoElm.getAttribute('_h5ppid')
-
- if (!vpid) return;
- if ($hs.cid_playHook > 0) clearTimeout($hs.cid_playHook);
- $hs.cid_playHook = setTimeout(function() {
- let allPaused = true;
- for (var k in playerConfs) {
- if (playerConfs[k].domElement.paused === false) {
- allPaused = false;
- break;
- }
- }
- if (allPaused) {
- $hs.focusHookVDoc = getRoot(videoElm)
- $hs.focusHookVId = vpid
- }
- $bv.boostVideoPerformanceDeactivate();
- }, 100)
-
-
-
- const playerConf = $hs.getPlayerConf(videoElm)
- if (playerConf) {
- playerConf.lastPauseAt = +new Date;
- playerConf.timeout_pause = setTimeout(() => {
- if (playerConf.lastPauseAt > 0) playerConf.domActive |= DOM_ACTIVE_DELAYED_PAUSED;
- }, 600)
- }
-
- if (!$hs.enable) return $hs.tips(false);
- consoleLog('pause')
- videoElm._isThisPausedBefore_ = true;
-
- let _last_paused = videoElm._last_paused
- videoElm._last_paused = videoElm.paused
- if (videoElm.playpause_cid) {
- clearTimeout(videoElm.playpause_cid);
- videoElm.playpause_cid = 0;
- }
- if (_last_paused === !videoElm.paused) {
- videoElm.playpause_cid = setTimeout(() => {
- if (videoElm.paused === !_last_paused && videoElm.paused) {
- $hs._tips(videoElm, 'Playback paused', undefined, 2500)
- }
- }, 90)
- }
-
-
- if (videoElm._record_continuous && videoElm._record_continuous.isFunctionLooping) {
- setTimeout(function() {
- if (videoElm.paused === true && !videoElm._record_continuous.isFunctionLooping)
- videoElm._record_continuous.savePlaybackProgress(); //savePlaybackProgress once before stopping //handle.savePlaybackProgress;
- }, 380)
- videoElm._record_continuous.loopingStop();
- }
-
-
- },
- handlerVideoVolumeChange: function(evt) {
-
- const videoElm = evt.target || this || null;
-
- if (videoElm.volume >= 0) {} else {
- return;
- }
- let cVol = videoElm.volume;
- let cMuted = videoElm.muted;
-
- if (cVol === videoElm._volume_p && cMuted === videoElm._muted_p) {
- // nothing changed
- } else if (cVol === videoElm._volume_p && cMuted !== videoElm._muted_p) {
- // muted changed
- } else { // cVol != pVol
-
- // only volume changed
-
- let shallShowTips = videoElm._volume >= 0; //prevent initialization
-
- if (!cVol) {
- videoElm.muted = true;
- } else if (cMuted) {
- videoElm.muted = false;
- videoElm._volume = cVol;
- } else if (!cMuted) {
- videoElm._volume = cVol;
- }
- consoleLog('volume changed')
-
- if (shallShowTips)
- $hs._tips(videoElm, 'Volume: ' + dround(videoElm.volume * 100) + '%', undefined, 3000)
-
- }
-
- videoElm._volume_p = cVol
- videoElm._muted_p = cMuted
-
- },
- handlerVideoLoadedMetaData: function(evt) {
- const videoElm = evt.target || this || null;
-
- if (!videoElm || videoElm.nodeName != "VIDEO") return;
-
- consoleLog('video size', videoElm.videoWidth + ' x ' + videoElm.videoHeight);
-
- let vpid = videoElm.getAttribute('_h5ppid') || null;
- if (!vpid || !videoElm.currentSrc) return;
-
- if ($hs.varSrcList[vpid] != videoElm.currentSrc) {
- $hs.varSrcList[vpid] = videoElm.currentSrc;
- $hs.videoSrcFound(videoElm);
- $hs._actionBoxObtain(videoElm);
- }
- if (!videoElm._onceVideoLoaded) {
- videoElm._onceVideoLoaded = true;
- playerConfs[vpid].domActive |= DOM_ACTIVE_SRC_LOADED;
- }
-
- },
- handlerElementMouseEnter: function(evt) {
- if ($hs.intVideoInitCount > 0) {} else {
- return;
- }
- const actionBoxRelation = $hs.getActionBoxRelationFromDOM(evt.target);
- if (!actionBoxRelation) return;
- const actionBox = actionBoxRelation.actionBox
- if (!actionBox) return;
- const vpid = actionBox.getAttribute('_h5p_actionbox_');
- const videoElm = actionBoxRelation.player;
- if (!videoElm) return;
-
- $hs._actionBoxObtain(videoElm);
-
- const playerConf = $hs.getPlayerConf(videoElm)
- if (playerConf) {
- momentary.act("actionBoxMouseEnter",
- () => {
- playerConf.domActive |= DOM_ACTIVE_MOUSE_IN;
- },
- () => {
- playerConf.domActive &= ~DOM_ACTIVE_MOUSE_IN;
- },
- 300)
- }
-
- },
- handlerElementMouseDown: function(evt) {
-
- function notAtVideo() {
- if ($hs.focusHookVDoc) $hs.focusHookVDoc = null
- if ($hs.focusHookVId) $hs.focusHookVId = ''
- }
-
- if ($hs.intVideoInitCount > 0) {} else {
- return notAtVideo();
- }
-
- const actionBoxRelation = $hs.getActionBoxRelationFromDOM(evt.target);
- if (!actionBoxRelation) return notAtVideo();
- const actionBox = actionBoxRelation.actionBox
- if (!actionBox) return notAtVideo();
- const vpid = actionBox.getAttribute('_h5p_actionbox_');
- const videoElm = actionBoxRelation.player;
- if (!videoElm) return notAtVideo();
-
- if (vpid) {
- $hs.focusHookVDoc = getRoot(videoElm)
- $hs.focusHookVId = vpid
- }
-
-
-
- $hs._actionBoxObtain(videoElm);
-
- const playerConf = $hs.getPlayerConf(videoElm)
- if (playerConf) {
- momentary.act("actionBoxClicking",
- () => {
- playerConf.domActive |= DOM_ACTIVE_MOUSE_CLICK;
- },
- () => {
- playerConf.domActive &= ~DOM_ACTIVE_MOUSE_CLICK;
- },
- 300)
- }
- $hs.swtichPlayerInstance();
-
- },
- handlerElementWheelTuneVolume: function(evt) { //shift + wheel
-
- if ($hs.intVideoInitCount > 0) {} else {
- return;
- }
-
- const actionBoxRelation = $hs.getActionBoxRelationFromDOM(evt.target);
- if (!actionBoxRelation) return;
- const actionBox = actionBoxRelation.actionBox
- if (!actionBox) return;
- const vpid = actionBox.getAttribute('_h5p_actionbox_');
- const videoElm = actionBoxRelation.player;
- if (!videoElm) return;
-
- $hs._actionBoxObtain(videoElm);
-
- if (!evt.shiftKey) return;
- if (evt.deltaY) {
- let player = $hs.player();
- if (!player || player != videoElm) return;
-
- if (evt.deltaY > 0) {
-
- if ((player.muted && player.volume === 0) && player._volume > 0) {
-
- player.muted = false;
- player.volume = player._volume;
- } else if (player.muted && (player.volume > 0 || !player._volume)) {
- player.muted = false;
- }
- $hs.tuneVolume(-0.05)
-
- evt.stopPropagation()
- evt.preventDefault()
- return false
-
- } else if (evt.deltaY < 0) {
-
- if ((player.muted && player.volume === 0) && player._volume > 0) {
- player.muted = false;
- player.volume = player._volume;
- } else if (player.muted && (player.volume > 0 || !player._volume)) {
- player.muted = false;
- }
- $hs.tuneVolume(+0.05)
-
- evt.stopPropagation()
- evt.preventDefault()
- return false
-
- }
- }
- },
- debug01: function(evt, videoActive) {
-
- if (!$hs.eventHooks) {
- document.__h5p_eventhooks = ($hs.eventHooks = {
- _debug_: []
- });
- }
- $hs.eventHooks._debug_.push([videoActive, evt.type]);
- // console.log('h5p eventhooks = document.__h5p_eventhooks')
- },
-
- swtichPlayerInstance: function() {
-
- let newPlayerInstance = null;
- const ONLY_PLAYING_NONE = 0x4A00;
- const ONLY_PLAYING_MORE_THAN_ONE = 0x5A00;
- let onlyPlayingInstance = ONLY_PLAYING_NONE;
- for (let k in playerConfs) {
- let playerConf = playerConfs[k] || {};
- let {
- domElement,
- domActive
- } = playerConf;
- if (domElement) {
- if (domActive & DOM_ACTIVE_INVALID_PARENT) continue;
- if (!domElement.parentNode) {
- playerConf.domActive |= DOM_ACTIVE_INVALID_PARENT;
- continue;
- }
- if (domActive & DOM_ACTIVE_MOUSE_CLICK) {
- newPlayerInstance = domElement
- break;
- }
- if (domActive & DOM_ACTIVE_ONCE_PLAYED && (domActive & DOM_ACTIVE_DELAYED_PAUSED) == 0) {
- if (onlyPlayingInstance == ONLY_PLAYING_NONE) onlyPlayingInstance = domElement;
- else onlyPlayingInstance = ONLY_PLAYING_MORE_THAN_ONE;
- }
- }
- }
- if (newPlayerInstance == null && onlyPlayingInstance.nodeType == 1) {
- newPlayerInstance = onlyPlayingInstance;
- }
-
- $hs.playerInstance = newPlayerInstance
-
-
- },
- handlerElementDblClick: function(evt) {
-
- if ($hs.intVideoInitCount > 0) {} else {
- return;
- }
-
- if (document.readyState != "complete") return;
-
- const actionBoxRelation = $hs.getActionBoxRelationFromDOM(evt.target);
- if (!actionBoxRelation) return;
- const actionBox = actionBoxRelation.actionBox
- if (!actionBox) return;
- const vpid = actionBox.getAttribute('_h5p_actionbox_');
- const videoElm = actionBoxRelation.player;
- if (!videoElm) return;
-
- $hs._actionBoxObtain(videoElm);
-
- const playerConf = $hs.getPlayerConf(videoElm)
- if (playerConf) {
- momentary.act("actionBoxClicking",
- () => {
- playerConf.domActive |= DOM_ACTIVE_MOUSE_CLICK;
- },
- () => {
- playerConf.domActive &= ~DOM_ACTIVE_MOUSE_CLICK;
- },
- 600)
- }
- $hs.swtichPlayerInstance()
-
- $hs.onVideoTriggering()
-
- $hs.callFullScreenBtn();
-
- evt.stopPropagation()
- evt.preventDefault()
- return false
-
- },
- handlerDocFocusOut: function(e) {
- let doc = this;
- $hs.focusFxLock = true;
- $ws.requestAnimationFrame(function() {
- $hs.focusFxLock = false;
- if (!$hs.enable) $hs.tips(false);
- else
- if (!doc.hasFocus() && $hs.player() && !$hs.isLostFocus) {
- $hs.isLostFocus = true;
- consoleLog('doc.focusout')
- //$hs.tips('focus is lost', -1);
- }
- });
- },
- handlerDocFocusIn: function(e) {
- let doc = this;
- if ($hs.focusFxLock) return;
- $ws.requestAnimationFrame(function() {
-
- if ($hs.focusFxLock) return;
- if (!$hs.enable) $hs.tips(false);
- else
- if (doc.hasFocus() && $hs.player() && $hs.isLostFocus) {
- $hs.isLostFocus = false;
- consoleLog('doc.focusin')
- $hs.tips(false);
-
- }
- });
- },
-
- handlerWinMessage: async function(e) {
- let tag, ed;
- if (typeof e.data == 'object' && typeof e.data.tag == 'string') {
- tag = e.data.tag;
- ed = e.data
- } else {
- return;
- }
- let msg = null,
- success = 0;
- let msg_str, msg_stype,p
- switch (tag) {
- case 'consoleLog':
- msg_str = ed.str;
- msg_stype = ed.stype;
- if (msg_stype === 1) {
- msg = (document[str_postMsgData] || {})[msg_str] || [];
- success = 1;
- } else if (msg_stype === 2) {
- msg = jsonParse(msg_str);
- if (msg && msg.d) {
- success = 2;
- msg = msg.d;
- }
- } else {
- msg = msg_str
- }
- p = (ed.passing && ed.winOrder) ? [' | from win-' + ed.winOrder] : [];
- if (success) {
- console.log(...msg, ...p)
- //document[ed.data]=null; // also delete the information
- } else {
- console.log('msg--', msg, ...p, ed);
- }
- break;
-
- }
- },
-
- isInActiveMode: function(activeElm, player) {
-
- console.log('check active mode', activeElm, player)
- if (activeElm == player) {
- return true;
- }
-
- for (let vpid in $hs.actionBoxRelations) {
- const actionBox = $hs.actionBoxRelations[vpid].actionBox
- if (actionBox && actionBox.parentNode) {
- if (activeElm == actionBox || actionBox.contains(activeElm)) {
- return true;
- }
- }
- }
-
- let _checkingPass = false;
-
- if (!player) return;
- let layoutBox = $hs.getPlayerBlockElement(player).parentNode;
- if (layoutBox && layoutBox.parentNode && layoutBox.contains(activeElm)) {
- let rpid = player.getAttribute('_h5ppid') || "NULL";
- let actionBox = layoutBox.parentNode.querySelector(`[_h5p_actionbox_="${rpid}"]`); //the box can be layoutBox
- if (actionBox && actionBox.contains(activeElm)) _checkingPass = true;
- }
-
- return _checkingPass
- },
-
-
- toolCheckFullScreen: function(doc) {
- if (typeof doc.fullScreen == 'boolean') return doc.fullScreen;
- if (typeof doc.webkitIsFullScreen == 'boolean') return doc.webkitIsFullScreen;
- if (typeof doc.mozFullScreen == 'boolean') return doc.mozFullScreen;
- return null;
- },
-
- toolFormatCT: function(u) {
-
- let w = Math.round(u, 0)
- let a = w % 60
- w = (w - a) / 60
- let b = w % 60
- w = (w - b) / 60
- let str = ("0" + b).substr(-2) + ":" + ("0" + a).substr(-2);
- if (w) str = w + ":" + str
-
- return str
-
- },
-
- makeFocus: function(player, evt) {
- setTimeout(function() {
- let rpid = player.getAttribute('_h5ppid');
- let actionBox = getRoot(player).querySelector(`[_h5p_actionbox_="${rpid}"]`);
-
- //console.log('p',rpid, player,actionBox,document.activeElement)
- if (actionBox && actionBox != document.activeElement && !actionBox.contains(document.activeElement)) {
- consoleLog('make focus on', actionBox)
- actionBox.focus();
-
- }
-
- }, 300)
- },
-
- loopOutwards: function(startPoint, maxStep) {
-
-
- let c = 0,
- p = startPoint,
- q = null;
- while (p && (++c <= maxStep)) {
- if (p.querySelectorAll('video').length !== 1) {
- return q;
- break;
- }
- q = p;
- p = p.parentNode;
- }
-
- return p || q || null;
-
- },
-
- getActionBlockElement: function(player, layoutBox) {
-
- //player, $hs.getPlayerBlockElement(player).parentNode;
- //player, player.parentNode .... player.parentNode.parentNode.parentNode
-
- //layoutBox: a container element containing video and with innerHeight>=player.innerHeight [skipped wrapping]
- //layoutBox parentSize > layoutBox Size
-
- //actionBox: a container with video and controls
- //can be outside layoutbox (bilibili)
- //assume maximum 3 layers
-
-
- let outerLayout = $hs.loopOutwards(layoutBox, 3); //i.e. layoutBox.parent.parent.parent
-
- const allFullScreenBtns = $hs.queryFullscreenBtnsIndependant(outerLayout)
- let actionBox = null;
-
- // console.log('fa0a', allFullScreenBtns.length, layoutBox)
- if (allFullScreenBtns.length > 0) {
- // console.log('faa', allFullScreenBtns.length)
-
- for (const possibleFullScreenBtn of allFullScreenBtns) possibleFullScreenBtn.setAttribute('__h5p_fsb__', '');
- let pElm = player.parentNode;
- let fullscreenBtns = null;
- while (pElm && pElm.parentNode) {
- fullscreenBtns = pElm.querySelectorAll('[__h5p_fsb__]');
- if (fullscreenBtns.length > 0) {
- break;
- }
- pElm = pElm.parentNode;
- }
- for (const possibleFullScreenBtn of allFullScreenBtns) possibleFullScreenBtn.removeAttribute('__h5p_fsb__');
- if (fullscreenBtns && fullscreenBtns.length > 0) {
- actionBox = pElm;
- fullscreenBtns = $hs.exclusiveElements(fullscreenBtns);
- return {
- actionBox,
- fullscreenBtns
- };
- }
- }
-
- let walkRes = domTool._isActionBox_1(player, layoutBox);
- //walkRes.elm = player... player.parentNode.parentNode (i.e. wPlayer)
- let parentCount = walkRes.length;
-
- if (parentCount - 1 >= 0 && domTool._isActionBox(player, walkRes, parentCount - 1)) {
- actionBox = walkRes[parentCount - 1].elm;
- } else if (parentCount - 2 >= 0 && domTool._isActionBox(player, walkRes, parentCount - 2)) {
- actionBox = walkRes[parentCount - 2].elm;
- } else {
- actionBox = player;
- }
-
- return {
- actionBox,
- fullscreenBtns: []
- };
-
-
-
-
- },
-
- actionBoxRelations: {},
-
- actionBoxRelationClearNodes: function(param) {
-
- if (!param) return;
- let refNode = null;
- let domNodes = null;
-
- if (param.nodeType > 0) {
- refNode = param;
- const rootNode = getRoot(refNode);
- domNodes = rootNode ? rootNode.querySelectorAll(`[_h5p_mo_="${vpid}"]`) : [];
- if (refNode) refNode.removeAttribute('_h5p_mo_');
- } else {
- const actionBoxRelation = param;
- domNodes = actionBoxRelation.domNodes;
- }
-
- if (domNodes) {
- for (const domNode of domNodes) domNode.removeAttribute('_h5p_mo_')
- domNodes.length = 0;
- }
-
-
- },
-
- actionBoxMutationCallback: function(mutations, observer) {
- for (const mutation of mutations) {
- let pElm = mutation.target;
- let vpid = null;
- if (pElm && pElm.nodeType > 0 && (vpid = pElm.getAttribute('_h5p_mo_'))) {
- const actionBoxRelation = $hs.actionBoxRelations[vpid]
- if (actionBoxRelation) {
- actionBoxRelation.mutationCount++;
- } else {
- $hs.actionBoxRelationClearNodes(pElm);
- }
- }
- }
- },
-
-
- getActionBoxRelationFromDOM: function(elm) {
-
- //assume action boxes are mutually exclusive
-
- for (let vpid in $hs.actionBoxRelations) {
- const actionBoxRelation = $hs.actionBoxRelations[vpid];
- const actionBox = actionBoxRelation.actionBox
- //console.log('ab', actionBox)
- if (actionBox && actionBox.parentNode) {
- if (elm == actionBox || actionBox.contains(elm)) {
- return actionBoxRelation;
- }
- }
- }
-
-
- return null;
-
- },
-
-
-
- _actionBoxObtain: function(player) {
-
- if (!player) return null;
- let vpid = player.getAttribute('_h5ppid');
- if (!vpid) return null;
- if (!player.parentNode) return null;
-
- let actionBoxRelation = $hs.actionBoxRelations[vpid],
- layoutBox = null,
- actionBox = null,
- boxSearchResult = null,
- fullscreenBtns = null,
- wPlayer = null;
-
- function a() {
- wPlayer = $hs.getPlayerBlockElement(player);
- layoutBox = wPlayer.parentNode;
- boxSearchResult = $hs.getActionBlockElement(player, layoutBox);
- console.log('box search', boxSearchResult)
- actionBox = boxSearchResult.actionBox
- fullscreenBtns = boxSearchResult.fullscreenBtns
- }
-
- function b(domNodes) {
- $hs.actionBoxRelations[vpid] = {
- player: player,
- wPlayer: wPlayer,
- layoutBox: layoutBox,
- actionBox: actionBox,
- mutationCount: 0,
- domNodes: domNodes,
- fullscreenBtns: fullscreenBtns
- }
- for (const domNode of domNodes) {
- domNode.setAttribute('_h5p_mo_', vpid);
- $hs.actionBoxMutationObserver.observe(domNode, {
- childList: true
- });
- }
- }
-
- if (actionBoxRelation) {
- if (actionBoxRelation.actionBox && actionBoxRelation.actionBox.parentNode && actionBoxRelation.layoutBox && actionBoxRelation.layoutBox.parentNode) {
- if (actionBoxRelation.mutationCount === 0) return actionBoxRelation.actionBox
- a();
- if (actionBox == actionBoxRelation.actionBox && layoutBox == actionBoxRelation.layoutBox && wPlayer == actionBoxRelation.wPlayer) {
- actionBoxRelation.mutationCount = 0;
- actionBoxRelation.fullscreenBtns = fullscreenBtns;
- return actionBox
- }
- }
- $hs.actionBoxRelationClearNodes(actionBoxRelation);
- for (var k in actionBoxRelation) delete actionBoxRelation[k]
- actionBoxRelation = null;
- delete $hs.actionBoxRelations[vpid]
- }
-
- if (boxSearchResult == null) a();
- if (actionBox) {
- actionBox.setAttribute('_h5p_actionbox_', vpid);
- if (!$hs.actionBoxMutationObserver) $hs.actionBoxMutationObserver = new MutationObserver($hs.actionBoxMutationCallback);
- const domNodes = [];
- let pElm = player;
- let containing = 0;
- while (pElm) {
- domNodes.push(pElm);
- if (pElm === actionBox) containing |= 1;
- if (pElm === layoutBox) containing |= 2;
- if (containing === 3) {
- b(domNodes);
- return actionBox
- }
- pElm = pElm.parentNode;
- }
- }
-
- return null;
-
-
- // if (!actionBox.hasAttribute('tabindex')) actionBox.setAttribute('tabindex', '-1');
-
-
-
-
- },
-
- videoSrcFound: function(player) {
-
- // src loaded
-
- if (!player) return;
- let vpid = player.getAttribute('_h5ppid') || null;
- if (!vpid || !player.currentSrc) return;
-
- player._isThisPausedBefore_ = false;
-
- player.removeAttribute('_h5p_uid_encrypted');
-
- if (player._record_continuous) player._record_continuous._lastSave = -999; //first time must save
-
- let uid_A = location.pathname.replace(/[^\d+]/g, '') + '.' + location.search.replace(/[^\d+]/g, '');
- let _uid = location.hostname.replace('www.', '').toLowerCase() + '!' + location.pathname.toLowerCase() + 'A' + uid_A + 'W' + player.videoWidth + 'H' + player.videoHeight + 'L' + (player.duration << 0);
-
- digestMessage(_uid).then(function(_uid_encrypted) {
-
- let d = +new Date;
-
- let recordedTime = null;
-
- ;
- (function() {
- //read the last record only;
-
- let k1 = '_h5_player_play_progress_';
- let k1n = '_play_progress_';
- let k2 = _uid_encrypted;
- let k3 = k1 + k2;
- let k3n = k1n + k2;
- let m2 = Store._keys().filter(key => key.substr(0, k3.length) == k3); //all progress records for this video
- let m2v = m2.map(keyName => +(keyName.split('+')[1] || '0'))
- let m2vMax = Math.max(0, ...m2v)
- if (!m2vMax) recordedTime = null;
- else {
- let _json_recordedTime = null;
- _json_recordedTime = Store.read(k3n + '+' + m2vMax);
- if (!_json_recordedTime) _json_recordedTime = {};
- else _json_recordedTime = jsonParse(_json_recordedTime);
- if (typeof _json_recordedTime == 'object') recordedTime = _json_recordedTime;
- else recordedTime = null;
- recordedTime = typeof recordedTime == 'object' ? recordedTime.t : recordedTime;
- if (typeof recordedTime == 'number' && (+recordedTime >= 0 || +recordedTime <= 0)) {
-
- } else if (typeof recordedTime == 'string' && recordedTime.length > 0 && (+recordedTime >= 0 || +recordedTime <= 0)) {
- recordedTime = +recordedTime
- } else {
- recordedTime = null
- }
- }
- if (recordedTime !== null) {
- player._h5player_lastrecord_ = recordedTime;
- } else {
- player._h5player_lastrecord_ = null;
- }
- if (player._h5player_lastrecord_ > 5) {
- consoleLog('last record playing', player._h5player_lastrecord_);
- setTimeout(function() {
- $hs._tips(player, `Press Shift-R to restore Last Playback: ${$hs.toolFormatCT(player._h5player_lastrecord_)}`, 5000, 4000)
- }, 1000)
- }
-
- })();
- // delay the recording by 5.4s => prevent ads or mis operation
- setTimeout(function() {
-
- let k1 = '_h5_player_play_progress_';
- let k1n = '_play_progress_';
- let k2 = _uid_encrypted;
- let k3 = k1 + k2;
- let k3n = k1n + k2;
-
- //re-read all the localStorage keys
- let m1 = Store._keys().filter(key => key.substr(0, k1.length) == k1); //all progress records in this site
- let p = m1.length + 1;
-
- for (const key of m1) { //all progress records for this video
- if (key.substr(0, k3.length) == k3) {
- Store._removeItem(key); //remove previous record for the current video
- p--;
- }
- }
-
- if (recordedTime !== null) {
- Store.save(k3n + '+' + d, jsonStringify({
- 't': recordedTime
- })) //prevent loss of last record
- }
-
- const _record_max_ = 48;
- const _record_keep_ = 26;
-
- if (p > _record_max_) {
- //exisiting 48 records for one site;
- //keep only 26 records
-
- const comparator = (a, b) => (a.t < b.t ? -1 : a.t > b.t ? 1 : 0);
-
- m1
- .map(keyName => ({
- keyName,
- t: +(keyName.split('+')[1] || '0')
- }))
- .sort(comparator)
- .slice(0, -_record_keep_)
- .forEach((item) => localStorage.removeItem(item.keyName));
-
- consoleLog(`stored progress: reduced to ${_record_keep_}`)
- }
-
- player.setAttribute('_h5p_uid_encrypted', _uid_encrypted + '+' + d);
-
- //try to start recording
- if (player._record_continuous) player._record_continuous.playingWithRecording();
-
- }, 5400);
-
- })
-
- },
- bindDocEvents: function(rootNode) {
- if (!rootNode._onceBindedDocEvents) {
-
- rootNode._onceBindedDocEvents = true;
- rootNode.addEventListener('keydown', $hs.handlerRootKeyDownEvent, true)
- document._debug_rootNode_ = rootNode;
-
- rootNode.addEventListener('mouseenter', $hs.handlerElementMouseEnter, true)
- rootNode.addEventListener('mousedown', $hs.handlerElementMouseDown, true)
- rootNode.addEventListener('dblclick', $hs.handlerElementDblClick, true)
- rootNode.addEventListener('wheel', $hs.handlerElementWheelTuneVolume, {
- passive: false
- });
-
- // wheel - bubble events to keep it simple (i.e. it must be passive:false & capture:false)
-
-
- rootNode.addEventListener('focus', $hs.handlerElementFocus, true)
- rootNode.addEventListener('fullscreenchange', $hs.handlerFullscreenChanged, true)
-
- }
- },
- fireGlobalInit: function() {
- if ($hs.intVideoInitCount != 1) return;
- if (!$hs.varSrcList) $hs.varSrcList = {};
- $hs.isLostFocus = null;
- try {
- //iframe may not be able to control top window
- //error; just ignore with async
- let topDoc = window.top && window.top.document ? window.top.document : null;
- if (topDoc) {
- topDoc.addEventListener('focusout', $hs.handlerDocFocusOut, true)
- topDoc.addEventListener('focusin', $hs.handlerDocFocusIn, true)
- }
-
- } catch (e) {}
- Store.clearInvalid(_sVersion_)
- },
- onVideoTriggering: function() {
-
-
- // initialize a single video player - h5Player.playerInstance
-
- /**
- * 初始化播放器實例
- */
- let player = $hs.playerInstance
- if (!player) return
-
- let vpid = player.getAttribute('_h5ppid');
-
- if (!vpid) return;
-
- let firstTime = !!$hs.initTips()
- if (firstTime) {
- // first time to trigger this player
- if (!player.hasAttribute('playsinline')) player.setAttribute('playsinline', 'playsinline');
- if (!player.hasAttribute('x-webkit-airplay')) player.setAttribute('x-webkit-airplay', 'deny');
- if (!player.hasAttribute('preload')) player.setAttribute('preload', 'auto');
- //player.style['image-rendering'] = 'crisp-edges';
- $hs.playbackRate = $hs.getPlaybackRate()
- }
-
- },
- getPlaybackRate: function() {
- let playbackRate = Store.read('_playback_rate_') || $hs.playbackRate
- return Number(Number(playbackRate).toFixed(1))
- },
- getPlayerBlockElement: function(player, useCache) {
-
- let layoutBox = null,
- wPlayer = null
-
- if (!player || !player.offsetHeight || !player.offsetWidth || !player.parentNode) {
- return null;
- }
-
-
- if (useCache === true) {
- let vpid = player.getAttribute('_h5ppid');
- let actionBoxRelation = $hs.actionBoxRelations[vpid]
- if (actionBoxRelation && actionBoxRelation.mutationCount === 0) {
- return actionBoxRelation.wPlayer
- }
- }
-
-
- //without checkActiveBox, just a DOM for you to append tipsDom
-
- function oWH(elm) {
- return [elm.offsetWidth, elm.offsetHeight].join(',');
- }
-
- function search_nodes() {
-
- wPlayer = player; // NOT NULL
- layoutBox = wPlayer.parentNode; // NOT NULL
-
- while (layoutBox.parentNode && layoutBox.nodeType == 1 && layoutBox.offsetHeight == 0) {
- wPlayer = layoutBox; // NOT NULL
- layoutBox = layoutBox.parentNode; // NOT NULL
- }
- //container must be with offsetHeight
-
- while (layoutBox.parentNode && layoutBox.nodeType == 1 && layoutBox.offsetHeight < player.offsetHeight) {
- wPlayer = layoutBox; // NOT NULL
- layoutBox = layoutBox.parentNode; // NOT NULL
- }
- //container must have height >= player height
-
- const layoutOWH = oWH(layoutBox)
- //const playerOWH=oWH(player)
-
- //skip all inner wraps
- while (layoutBox.parentNode && layoutBox.nodeType == 1 && oWH(layoutBox.parentNode) == layoutOWH) {
- wPlayer = layoutBox; // NOT NULL
- layoutBox = layoutBox.parentNode; // NOT NULL
- }
-
- // oWH of layoutBox.parentNode != oWH of layoutBox and layoutBox.offsetHeight >= player.offsetHeight
-
- }
-
- search_nodes();
-
- if (layoutBox.nodeType == 11) {
- makeNoRoot(layoutBox);
- search_nodes();
- }
-
-
-
- //condition:
- //!layoutBox.parentNode || layoutBox.nodeType != 1 || layoutBox.offsetHeight > player.offsetHeight
-
- // layoutBox is a node contains <video> and offsetHeight>=video.offsetHeight
- // wPlayer is a HTML Element (nodeType==1)
- // you can insert the DOM element into the layoutBox
-
- if (layoutBox && wPlayer && layoutBox.nodeType === 1 && wPlayer.parentNode == layoutBox && layoutBox.parentNode) return wPlayer;
- throw 'unknown error';
-
- },
- getCommonContainer: function(elm1, elm2) {
-
- let box1 = elm1;
- let box2 = elm2;
-
- while (box1 && box2) {
- if (box1.contains(box2) || box2.contains(box1)) {
- break;
- }
- box1 = box1.parentNode;
- box2 = box2.parentNode;
- }
-
- let layoutBox = null;
-
- box1 = (box1 && box1.contains(elm2)) ? box1 : null;
- box2 = (box2 && box2.contains(elm1)) ? box2 : null;
-
- if (box1 && box2) layoutBox = box1.contains(box2) ? box2 : box1;
- else layoutBox = box1 || box2 || null;
-
- return layoutBox
-
- },
- change_layoutBox: function(tipsDom) {
- let player = $hs.player()
- if (!player) return;
- let wPlayer = $hs.getPlayerBlockElement(player, true);
- let layoutBox = wPlayer.parentNode;
-
- if ((layoutBox && layoutBox.nodeType == 1) && (!tipsDom.parentNode || tipsDom.parentNode !== layoutBox)) {
-
- consoleLog('changed_layoutBox')
- layoutBox.insertBefore(tipsDom, wPlayer);
-
- }
- },
-
- _hasEventListener:function(elm, p){
-
- if(elm && elm._listeners){
-
- const cache = elm._listeners[p]
- return cache&&cache.funcCount>0
-
- }
- return false;
-
- },
-
- queryFullscreenBtnsIndependant: function(parentNode) {
-
- let btns = [];
-
- function elmCallback(elm) {
-
- let hasClickListeners = null,
- childElementCount = null,
- isVisible = null,
- btnElm = elm;
- var pElm = elm;
- while (pElm && pElm.nodeType === 1 && pElm != parentNode && pElm.querySelector('video') === null) {
-
- let funcTest= typeof pElm.onclick == 'function' || $hs._hasEventListener(pElm,'click');
- funcTest= funcTest || typeof pElm.onmousedown == 'function' || $hs._hasEventListener(pElm,'mousedown');
- funcTest= funcTest || typeof pElm.onmouseup == 'function' || $hs._hasEventListener(pElm,'mouseup');
-
- if (funcTest) {
- hasClickListeners = true
- btnElm = pElm;
- break;
- }
-
- pElm = pElm.parentNode;
- }
- if (btns.indexOf(btnElm) >= 0) return; //btn>a.fullscreen-1>b.fullscreen-2>c.fullscreen-3
-
- if ('_listeners' in elm) {
-
- //hasClickListeners = elm._listeners && elm._listeners.click && elm._listeners.click.funcCount > 0
-
- }
-
- if ('childElementCount' in elm) {
-
- childElementCount = elm.childElementCount;
-
- }
- if ('offsetParent' in elm) {
- isVisible = !!elm.offsetParent; //works with parent/self display none; not work with visiblity hidden / opacity0
-
- }
-
- if (hasClickListeners) {
- let btn = {
- elm,
- btnElm,
- isVisible,
- hasClickListeners,
- childElementCount,
- isContained: null
- };
-
- //console.log('btnElm', btnElm)
-
- btns.push(btnElm)
-
- }
- }
-
-
- for (const elm of parentNode.querySelectorAll('[class*="full"][class*="screen"]')) {
- let className = (elm.getAttribute('class') || "");
- if (/\b(fullscreen|full-screen)\b/i.test(className.replace(/([A-Z][a-z]+)/g, '-$1-').replace(/[\_\-]+/g, '-'))) {
- elmCallback(elm)
- }
- }
-
-
- for (const elm of parentNode.querySelectorAll('[id*="full"][id*="screen"]')) {
- let idName = (elm.getAttribute('id') || "");
- if (/\b(fullscreen|full-screen)\b/i.test(idName.replace(/([A-Z][a-z]+)/g, '-$1-').replace(/[\_\-]+/g, '-'))) {
- elmCallback(elm)
- }
- }
-
- for (const elm of parentNode.querySelectorAll('[name*="full"][name*="screen"]')) {
- let nName = (elm.getAttribute('name') || "");
- if (/\b(fullscreen|full-screen)\b/i.test(nName.replace(/([A-Z][a-z]+)/g, '-$1-').replace(/[\_\-]+/g, '-'))) {
- elmCallback(elm)
- }
- }
-
-
- return btns;
-
- },
- exclusiveElements: function(elms) {
-
- //not containing others
- let res = [];
-
- for (const roleElm of elms) {
-
- let isContained = false;
- for (const testElm of elms) {
- if (testElm != roleElm && roleElm.contains(testElm)) {
- isContained = true;
- break;
- }
- }
- if (!isContained) res.push(roleElm)
- }
- return res;
-
- },
- callFullScreenBtn: function() {
- console.log('callFullScreenBtn')
-
-
-
- let player = $hs.player()
- if (!player || !player.ownerDocument || !('exitFullscreen' in player.ownerDocument)) return;
-
- let btnElement = null;
-
- let vpid = player.getAttribute('_h5ppid') || null;
-
- if (!vpid) return;
-
-
- const chFull = $hs.toolCheckFullScreen(player.ownerDocument);
-
-
-
- let actionBoxRelation = $hs.actionBoxRelations[vpid];
-
- if (chFull === true) {
- player.ownerDocument.exitFullscreen();
- return;
- } else if (chFull === false) {
- if (actionBoxRelation && actionBoxRelation.actionBox) {
- let actionBox = actionBoxRelation.actionBox;
- let btnElements = actionBoxRelation.fullscreenBtns;
-
- if (btnElements && btnElements.length > 0) {
-
-
- let btnElement_idx = btnElements._only_idx;
-
- if (btnElement_idx >= 0) {
-
- } else if (btnElements.length === 1) {
- btnElement_idx = 0;
- } else if (btnElements.length > 1) {
- //web-fullscreen-on/off ; fullscreen-on/off ....
-
- const strList = btnElements.map(elm => [elm.className || 'null', elm.id || 'null', elm.name || 'null'].join('-').replace(/([A-Z][a-z]+)/g, '-$1-').replace(/[\_\-]+/g, '-'))
-
- const filterOutScores = new Array(strList.length).fill(0);
- const filterInScores = new Array(strList.length).fill(0);
- const filterScores = new Array(strList.length).fill(0);
- for (const [j, str] of strList.entries()) {
- if (/\b(fullscreen|full-screen)\b/i.test(str)) filterInScores[j] += 1
- if (/\b(web-fullscreen|web-full-screen)\b/i.test(str)) filterOutScores[j] += 1
- if (/\b(fullscreen-on|full-screen-on)\b/i.test(str)) filterInScores[j] += 1
- if (/\b(fullscreen-off|full-screen-off)\b/i.test(str)) filterOutScores[j] += 1
- if (/\b(on-fullscreen|on-full-screen)\b/i.test(str)) filterInScores[j] += 1
- if (/\b(off-fullscreen|off-full-screen)\b/i.test(str)) filterOutScores[j] += 1
- }
-
- let maxScore = -1e7;
- for (const [j, str] of strList.entries()) {
- filterScores[j] = filterInScores[j] * 3 - filterOutScores[j] * 2
- if (filterScores[j] > maxScore) maxScore = filterScores[j];
- }
- btnElement_idx = filterScores.indexOf(maxScore)
- if (btnElement_idx < 0) btnElement_idx = 0; //unknown
- }
-
- btnElements._only_idx = btnElement_idx
-
- btnElement = btnElements[btnElement_idx];
-
- //consoleLog('original fullscreen')
- window.requestAnimationFrame(() => btnElement.click());
- return;
-
- }
-
-
- }
-
- }
-
- let gPlayer = getRoot(player).querySelector(`[_h5p_fsElm_="${vpid}"]`); //it is set in fullscreenchange
-
- if (gPlayer) {
-
- } else if (actionBoxRelation && actionBoxRelation.actionBox) {
- gPlayer = actionBoxRelation.actionBox;
- } else if (actionBoxRelation && actionBoxRelation.layoutBox) {
- gPlayer = actionBoxRelation.layoutBox;
- } else {
- gPlayer = player;
- }
-
- consoleLog('DOM fullscreen', gPlayer)
- try {
- gPlayer.requestFullscreen() //known bugs : TypeError: fullscreen error
- } catch (e) {
- consoleLogF('fullscreen error', e)
- }
-
-
- },
- /* 設置播放速度 */
- setPlaybackRate: function(num, flagTips) {
- let player = $hs.player()
- let curPlaybackRate
- if (num) {
- num = +num
- if (num > 0) { // also checking the type of variable
- curPlaybackRate = num < 0.1 ? 0.1 : +(num.toFixed(1))
- } else {
- console.error('h5player: 播放速度轉換出錯')
- return false
- }
- } else {
- curPlaybackRate = $hs.getPlaybackRate()
- }
- /* 記錄播放速度的信息 */
-
- let changed = curPlaybackRate !== player.playbackRate;
-
- if (curPlaybackRate !== player.playbackRate) {
-
- Store.save('_playback_rate_', curPlaybackRate + '')
- $hs.playbackRate = curPlaybackRate
- player.playbackRate = curPlaybackRate
- /* 本身處於1被播放速度的時候不再提示 */
- //if (!num && curPlaybackRate === 1) return;
-
- }
-
- flagTips = (flagTips < 0) ? false : (flagTips > 0) ? true : changed;
- if (flagTips) $hs.tips('Playback speed: ' + player.playbackRate + 'x')
- },
- tuneCurrentTime: function(amount, flagTips) {
- let _amount = +(+amount).toFixed(1);
- let player = $hs.player();
- if (_amount >= 0 || _amount < 0) {} else {
- return;
- }
-
- let newCurrentTime = player.currentTime + _amount;
- if (newCurrentTime < 0) newCurrentTime = 0;
- if (newCurrentTime > player.duration) newCurrentTime = player.duration;
-
- let changed = newCurrentTime != player.currentTime && newCurrentTime >= 0 && newCurrentTime <= player.duration;
-
- if (changed) {
- //player.currentTime = newCurrentTime;
- player.pause();
- let t_ch = (Math.random() / 5 + .75);
- player.currentTime = newCurrentTime * t_ch + player.currentTime * (1.0 - t_ch);
- setTimeout(() => {
- player.play();
- player.currentTime = newCurrentTime;
- }, 33);
- flagTips = (flagTips < 0) ? false : (flagTips > 0) ? true : changed;
- $hs.tips(false);
- if (flagTips) {
- if (_amount > 0) $hs.tips(_amount + ' Sec. Forward', undefined, 3000);
- else $hs.tips(-_amount + ' Sec. Backward', undefined, 3000)
- }
- }
-
- },
- tuneVolume: function(amount) {
- let _amount = +(+amount).toFixed(2);
-
- let player = $hs.player()
-
- let newVol = player.volume + _amount;
- if (newVol < 0) newVol = 0;
- if (newVol > 1) newVol = 1;
- let chVol = player.volume !== newVol && newVol >= 0 && newVol <= 1;
-
- if (chVol) {
-
- if (_amount > 0 && player.volume < 1) {
- player.volume = newVol // positive
- } else if (_amount < 0 && player.volume > 0) {
- player.volume = newVol // negative
- }
- $hs.tips(false);
- $hs.tips('Volume: ' + dround(player.volume * 100) + '%', undefined)
- }
- },
- switchPlayStatus: function() {
- let player = $hs.player()
- if (player.paused) {
- player.play()
- if (player._isThisPausedBefore_) {
- $hs.tips(false);
- $hs.tips('Playback resumed', undefined, 2500)
- }
- } else {
- player.pause()
- $hs.tips(false);
- $hs.tips('Playback paused', undefined, 2500)
- }
- },
- tipsClassName: 'html_player_enhance_tips',
- _tips: function(player, str, duration, order) {
-
-
- if (!player.getAttribute('_h5player_tips')) $hs.initTips();
-
- let tipsSelector = '#' + (player.getAttribute('_h5player_tips') || $hs.tipsClassName) //if this attribute still doesnt exist, set it to the base cls name
- let tipsDom = getRoot(player).querySelector(tipsSelector)
- if (!tipsDom) {
- consoleLog('init h5player tips dom error...')
- return false
- }
- $hs.change_layoutBox(tipsDom);
-
-
-
- if (str === false) {
- tipsDom.textContent = '';
- tipsDom.setAttribute('_potTips_', '0')
- } else {
- order = order || 1000
- tipsDom.tipsOrder = tipsDom.tipsOrder || 0;
-
- let shallDisplay = true
- if (order < tipsDom.tipsOrder && getComputedStyle(tipsDom).opacity > 0) shallDisplay = false
-
- if (shallDisplay) {
- tipsDom._playerElement = player;
- tipsDom._playerVPID = player.getAttribute('_h5ppid');
- tipsDom._playerBlockElm = $hs.getPlayerBlockElement(player, true)
-
- if (duration === undefined) duration = 2000
- tipsDom.textContent = str
- tipsDom.setAttribute('_potTips_', '2')
-
- if (duration > 0) {
- $ws.requestAnimationFrame(() => tipsDom.setAttribute('_potTips_', '1'))
- } else {
- order = -1;
- }
-
- tipsDom.tipsOrder = order
-
- }
-
- }
-
-
- if (window.ResizeObserver) {
- //observe not fire twice for the same element.
- if (!$hs.observer_resizeVideos) $hs.observer_resizeVideos = new ResizeObserver(hanlderResizeVideo)
- $hs.observer_resizeVideos.observe(tipsDom._playerBlockElm.parentNode)
- $hs.observer_resizeVideos.observe(tipsDom._playerBlockElm)
- $hs.observer_resizeVideos.observe(player)
- }
- //ensure function called
- window.requestAnimationFrame(() => $hs.fixNonBoxingVideoTipsPosition(tipsDom, player))
-
- },
- tips: function(str, duration, order) {
- let player = $hs.player()
- if (!player) {
- consoleLog('h5Player Tips:', str)
- return true
- }
-
- return $hs._tips(player, str, duration, order)
-
- },
- listOfTipsDom: [],
- getPotTips: function(arr) {
- const res = [];
- for (const tipsDom of $hs.listOfTipsDom) {
- if (tipsDom && tipsDom.nodeType > 0 && tipsDom.parentNode && tipsDom.ownerDocument) {
- if (tipsDom._playerBlockElm && tipsDom._playerBlockElm.parentNode) {
- const potTipsAttr = tipsDom.getAttribute('_potTips_')
- if (arr.indexOf(potTipsAttr) >= 0) res.push(tipsDom)
- }
- }
- }
- return res;
- },
- initTips: function() {
- /* 設置提示DOM的樣式 */
- let player = $hs.player()
- let shadowRoot = getRoot(player);
- let doc = player.ownerDocument;
- //console.log((document.documentElement.qq=player),shadowRoot,'xax')
- let parentNode = player.parentNode
- let tcn = player.getAttribute('_h5player_tips') || ($hs.tipsClassName + '_' + (+new Date));
- player.setAttribute('_h5player_tips', tcn)
- if (shadowRoot.querySelector('#' + tcn)) return false;
-
- if (!shadowRoot._onceAddedCSS) {
- shadowRoot._onceAddedCSS = true;
-
- let cssStyle = `
- [_potTips_="1"]{
- animation: 2s linear 0s normal forwards 1 delayHide;
- }
- [_potTips_="0"]{
- opacity:0; transform:translate(-9999px);
- }
- [_potTips_="2"]{
- opacity:.95; transform: translate(0,0);
- }
-
- @keyframes delayHide{
- 0%, 99% { opacity:0.95; transform: translate(0,0); }
- 100% { opacity:0; transform:translate(-9999px); }
- }
- ` + `
- [_potTips_]{
- font-weight: bold !important;
- position: absolute !important;
- z-index: 999 !important;
- font-size: ${$hs.fontSize || 16}px !important;
- padding: 0px !important;
- border:none !important;
- background: rgba(0,0,0,0) !important;
- color:#738CE6 !important;
- text-shadow: -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000;
- top: 50%;
- left: 50%;
- max-width:500px;max-height:50px;
- border-radius:3px;
- font-family: 'microsoft yahei', Verdana, Geneva, sans-serif;
- pointer-events: none;
- }
- body div[_potTips_]{
- -webkit-user-select: none !important;
- -moz-user-select: none !important;
- -ms-user-select: none !important;
- user-select: none !important;
- -webkit-touch-callout: none !important;
- -webkit-user-select: none !important;
- -khtml-user-drag: none !important;
- -khtml-user-select: none !important;
- -moz-user-select: none !important;
- -moz-user-select: -moz-none !important;
- -ms-user-select: none !important;
- user-select: none !important;
- }
- `.replace(/\r\n/g, '');
-
-
- let cssContainer = domAppender(shadowRoot);
-
-
- if (!cssContainer) {
- cssContainer = makeNoRoot(shadowRoot)
- }
-
- domTool.addStyle(cssStyle, cssContainer);
-
- }
-
- let tipsDom = doc.createElement('div')
-
- tipsDom.addEventListener(crossBrowserTransition('animation'), function(e) {
- if (this.getAttribute('_potTips_') == '1') {
- this.setAttribute('_potTips_', '0')
- }
- })
-
- tipsDom.id = tcn;
- tipsDom.setAttribute('_potTips_', '0');
- $hs.listOfTipsDom.push(tipsDom);
- $hs.change_layoutBox(tipsDom);
-
- return true;
- },
-
- responsiveSizing: function(container, elm) {
-
- let gcssP = getComputedStyle(container);
-
- let gcssE = getComputedStyle(elm);
-
- //console.log(gcssE.left,gcssP.width)
- let elmBound = {
- left: parseFloat(gcssE.left) / parseFloat(gcssP.width),
- width: parseFloat(gcssE.width) / parseFloat(gcssP.width),
- top: parseFloat(gcssE.top) / parseFloat(gcssP.height),
- height: parseFloat(gcssE.height) / parseFloat(gcssP.height)
- };
-
- let elm00 = [elmBound.left, elmBound.top];
- let elm01 = [elmBound.left + elmBound.width, elmBound.top];
- let elm10 = [elmBound.left, elmBound.top + elmBound.height];
- let elm11 = [elmBound.left + elmBound.width, elmBound.top + elmBound.height];
-
- return {
- elm00,
- elm01,
- elm10,
- elm11,
- plw: elmBound.width,
- plh: elmBound.height
- };
-
- },
-
- fixNonBoxingVideoTipsPosition: function(tipsDom, player) {
-
- if (!tipsDom || !player) return;
-
- let ct = $hs.getCommonContainer(tipsDom, player)
-
- if (!ct) return;
-
- //relative
-
- let elm00 = $hs.responsiveSizing(ct, player).elm00;
-
- if (isNaN(elm00[0]) || isNaN(elm00[1])) {
-
- [tipsDom.style.left, tipsDom.style.top] = [player.style.left, player.style.top];
- //eg auto
- } else {
-
- let rlm00 = elm00.map(t => (t * 100).toFixed(2) + '%');
- [tipsDom.style.left, tipsDom.style.top] = rlm00;
-
- }
-
- // absolute
-
- let _offset = {
- left: 10,
- top: 15
- };
-
- let customOffset = {
- left: _offset.left,
- top: _offset.top
- };
- let p = tipsDom.getBoundingClientRect();
- let q = player.getBoundingClientRect();
- let currentPos = [p.left, p.top];
-
- let targetPos = [q.left + player.offsetWidth * 0 + customOffset.left, q.top + player.offsetHeight * 0 + customOffset.top];
-
- let mL = +tipsDom.style.marginLeft.replace('px', '') || 0;
- if (isNaN(mL)) mL = 0;
- let mT = +tipsDom.style.marginTop.replace('px', '') || 0;
- if (isNaN(mT)) mT = 0;
-
- let z1 = -(currentPos[0] - targetPos[0]);
- let z2 = -(currentPos[1] - targetPos[1]);
-
- if (z1 || z2) {
-
- let y1 = z1 + mL;
- let y2 = z2 + mT;
-
- tipsDom.style.marginLeft = y1 + 'px';
- tipsDom.style.marginTop = y2 + 'px';
-
- }
- },
-
- playerTrigger: function(player, event) {
- if (!player || !event) return
- const pCode = event.code;
- let keyAsm = (event.shiftKey ? SHIFT : 0) | ((event.ctrlKey || event.metaKey) ? CTRL : 0) | (event.altKey ? ALT : 0);
-
-
- let vpid = player.getAttribute('_h5ppid') || null;
- if (!vpid) return;
- let playerConf = playerConfs[vpid]
- if (!playerConf) return;
-
- //shift + key
- if (keyAsm == SHIFT) {
- // 網頁FULLSCREEN
- if (pCode === 'Enter') {
- $hs.callFullScreenBtn()
- return TERMINATE
- } else if (pCode == 'KeyF') {
- //change unsharpen filter
-
- let resList = ["unsharpen3_05", "unsharpen3_10", "unsharpen5_05", "unsharpen5_10", "unsharpen9_05", "unsharpen9_10"]
- let res = (prompt("Enter the unsharpen mask\n(" + resList.map(x => '"' + x + '"').join(', ') + ")", "unsharpen9_05") || "").toLowerCase();
- if (resList.indexOf(res) < 0) res = ""
- GM_setValue("unsharpen_mask", res)
- for (const el of document.querySelectorAll('video[_h5p_uid_encrypted]')) {
- if (el.style.filter == "" || el.style.filter) {
- let filterStr1 = el.style.filter.replace(/\s*url\(\"#_h5p_unsharpen[\d\_]+\"\)/, '');
- let filterStr2 = (res.length > 0 ? ' url("#_h5p_' + res + '")' : '')
- el.style.filter = filterStr1 + filterStr2;
- }
- }
- return TERMINATE
-
- }
- // 進入或退出畫中畫模式
- else if (pCode == 'KeyP') {
- $hs.pictureInPicture(player)
-
- return TERMINATE
- } else if (pCode == 'KeyR') {
- if (player._h5player_lastrecord_ !== null && (player._h5player_lastrecord_ >= 0 || player._h5player_lastrecord_ <= 0)) {
- $hs.setPlayProgress(player, player._h5player_lastrecord_)
-
- return TERMINATE
- }
-
- } else if (pCode == 'KeyO') {
- let _debug_h5p_logging_ch = false;
- try {
- Store._setItem('_h5_player_sLogging_', 1 - Store._getItem('_h5_player_sLogging_'))
- _debug_h5p_logging_ = +Store._getItem('_h5_player_sLogging_') > 0;
- _debug_h5p_logging_ch = true;
- } catch (e) {
-
- }
- consoleLogF('_debug_h5p_logging_', !!_debug_h5p_logging_, 'changed', _debug_h5p_logging_ch)
-
- if (_debug_h5p_logging_ch) {
-
- return TERMINATE
- }
- } else if (pCode == 'KeyT') {
- if (/^blob/i.test(player.currentSrc)) {
- alert(`The current video is ${player.currentSrc}\nSorry, it cannot be opened in PotPlayer.`);
- } else {
- let confirm_res = confirm(`The current video is ${player.currentSrc}\nDo you want to open it in PotPlayer?`);
- if (confirm_res) window.open('potplayer://' + player.currentSrc, '_blank');
- }
- return TERMINATE
- }
-
-
-
- let videoScale = playerConf.vFactor;
-
- function tipsForVideoScaling() {
-
- playerConf.vFactor = +videoScale.toFixed(1);
-
- playerConf.cssTransform();
- let tipsMsg = `視頻縮放率:${ +(videoScale * 100).toFixed(2) }%`
- if (playerConf.translate.x) {
- tipsMsg += `,水平位移:${playerConf.translate.x}px`
- }
- if (playerConf.translate.y) {
- tipsMsg += `,垂直位移:${playerConf.translate.y}px`
- }
- $hs.tips(false);
- $hs.tips(tipsMsg)
-
-
- }
-
- // 視頻畫面縮放相關事件
-
- switch (pCode) {
- // shift+X:視頻縮小 -0.1
- case 'KeyX':
- videoScale -= 0.1
- if (videoScale < 0.1) videoScale = 0.1;
- tipsForVideoScaling();
- return TERMINATE
- break
- // shift+C:視頻放大 +0.1
- case 'KeyC':
- videoScale += 0.1
- if (videoScale > 16) videoScale = 16;
- tipsForVideoScaling();
- return TERMINATE
- break
- // shift+Z:視頻恢復正常大小
- case 'KeyZ':
- videoScale = 1.0
- playerConf.translate.x = 0;
- playerConf.translate.y = 0;
- tipsForVideoScaling();
- return TERMINATE
- break
- case 'ArrowRight':
- playerConf.translate.x += 10
- tipsForVideoScaling();
- return TERMINATE
- break
- case 'ArrowLeft':
- playerConf.translate.x -= 10
- tipsForVideoScaling();
- return TERMINATE
- break
- case 'ArrowUp':
- playerConf.translate.y -= 10
- tipsForVideoScaling();
- return TERMINATE
- break
- case 'ArrowDown':
- playerConf.translate.y += 10
- tipsForVideoScaling();
- return TERMINATE
- break
-
- }
-
- }
- // 防止其它無關組合鍵衝突
- if (!keyAsm) {
- let kControl = null
- let newPBR, oldPBR, nv, numKey;
- switch (pCode) {
- // 方向鍵右→:快進3秒
- case 'ArrowRight':
- $hs.tuneCurrentTime($hs.skipStep);
- return TERMINATE;
- break;
- // 方向鍵左←:後退3秒
- case 'ArrowLeft':
- $hs.tuneCurrentTime(-$hs.skipStep);
- return TERMINATE;
- break;
- // 方向鍵上↑:音量升高 1%
- case 'ArrowUp':
- if ((player.muted && player.volume === 0) && player._volume > 0) {
-
- player.muted = false;
- player.volume = player._volume;
- } else if (player.muted && (player.volume > 0 || !player._volume)) {
- player.muted = false;
- }
- $hs.tuneVolume(0.01);
- return TERMINATE;
- break;
- // 方向鍵下↓:音量降低 1%
- case 'ArrowDown':
-
- if ((player.muted && player.volume === 0) && player._volume > 0) {
-
- player.muted = false;
- player.volume = player._volume;
- } else if (player.muted && (player.volume > 0 || !player._volume)) {
- player.muted = false;
- }
- $hs.tuneVolume(-0.01);
- return TERMINATE;
- break;
- // 空格鍵:暫停/播放
- case 'Space':
- $hs.switchPlayStatus();
- return TERMINATE;
- break;
- // 按鍵X:減速播放 -0.1
- case 'KeyX':
- if (player.playbackRate > 0) {
- $hs.tips(false);
- $hs.setPlaybackRate(player.playbackRate - 0.1);
- return TERMINATE
- }
- break;
- // 按鍵C:加速播放 +0.1
- case 'KeyC':
- if (player.playbackRate < 16) {
- $hs.tips(false);
- $hs.setPlaybackRate(player.playbackRate + 0.1);
- return TERMINATE
- }
-
- break;
- // 按鍵Z:正常速度播放
- case 'KeyZ':
- $hs.tips(false);
- oldPBR = player.playbackRate;
- if (oldPBR != 1.0) {
- player._playbackRate_z = oldPBR;
- newPBR = 1.0;
- } else if (player._playbackRate_z != 1.0) {
- newPBR = player._playbackRate_z || 1.0;
- player._playbackRate_z = 1.0;
- } else {
- newPBR = 1.0
- player._playbackRate_z = 1.0;
- }
- $hs.setPlaybackRate(newPBR, 1)
- return TERMINATE
- break;
- // 按鍵F:下一幀
- case 'KeyF':
- if (window.location.hostname === 'www.netflix.com') return /* netflix 的F鍵是FULLSCREEN的意思 */
- $hs.tips(false);
- if (!player.paused) player.pause()
- player.currentTime += +(1 / playerConf.fps)
- $hs.tips('Jump to: Next frame')
- return TERMINATE
- break;
- // 按鍵D:上一幀
- case 'KeyD':
- $hs.tips(false);
- if (!player.paused) player.pause()
- player.currentTime -= +(1 / playerConf.fps)
- $hs.tips('Jump to: Previous frame')
- return TERMINATE
- break;
- // 按鍵E:亮度增加%
- case 'KeyE':
- $hs.tips(false);
- nv = playerConf.setFilter('brightness', (v) => v + 0.1);
- $hs.tips('Brightness: ' + dround(nv * 100) + '%')
- return TERMINATE
- break;
- // 按鍵W:亮度減少%
- case 'KeyW':
- $hs.tips(false);
- nv = playerConf.setFilter('brightness', (v) => v > 0.1 ? v - 0.1 : 0);
- $hs.tips('Brightness: ' + dround(nv * 100) + '%')
- return TERMINATE
- break;
- // 按鍵T:對比度增加%
- case 'KeyT':
- $hs.tips(false);
- nv = playerConf.setFilter('contrast', (v) => v + 0.1);
- $hs.tips('Contrast: ' + dround(nv * 100) + '%')
- return TERMINATE
- break;
- // 按鍵R:對比度減少%
- case 'KeyR':
- $hs.tips(false);
- nv = playerConf.setFilter('contrast', (v) => v > 0.1 ? v - 0.1 : 0);
- $hs.tips('Contrast: ' + dround(nv * 100) + '%')
- return TERMINATE
- break;
- // 按鍵U:飽和度增加%
- case 'KeyU':
- $hs.tips(false);
- nv = playerConf.setFilter('saturate', (v) => v + 0.1);
- $hs.tips('Saturate: ' + dround(nv * 100) + '%')
- return TERMINATE
- break;
- // 按鍵Y:飽和度減少%
- case 'KeyY':
- $hs.tips(false);
- nv = playerConf.setFilter('saturate', (v) => v > 0.1 ? v - 0.1 : 0);
- $hs.tips('Saturate: ' + dround(nv * 100) + '%')
- return TERMINATE
- break;
- // 按鍵O:色相增加 1 度
- case 'KeyO':
- $hs.tips(false);
- nv = playerConf.setFilter('hue-rotate', (v) => v + 1);
- $hs.tips('Hue: ' + nv + ' deg')
- return TERMINATE
- break;
- // 按鍵I:色相減少 1 度
- case 'KeyI':
- $hs.tips(false);
- nv = playerConf.setFilter('hue-rotate', (v) => v - 1);
- $hs.tips('Hue: ' + nv + ' deg')
- return TERMINATE
- break;
- // 按鍵K:模糊增加 0.1 px
- case 'KeyK':
- $hs.tips(false);
- nv = playerConf.setFilter('blur', (v) => v + 0.1);
- $hs.tips('Blur: ' + nv + ' px')
- return TERMINATE
- break;
- // 按鍵J:模糊減少 0.1 px
- case 'KeyJ':
- $hs.tips(false);
- nv = playerConf.setFilter('blur', (v) => v > 0.1 ? v - 0.1 : 0);
- $hs.tips('Blur: ' + nv + ' px')
- return TERMINATE
- break;
- // 按鍵Q:圖像復位
- case 'KeyQ':
- $hs.tips(false);
- playerConf.filterReset();
- $hs.tips('Video Filter Reset')
- return TERMINATE
- break;
- // 按鍵S:畫面旋轉 90 度
- case 'KeyS':
- $hs.tips(false);
- playerConf.rotate += 90
- if (playerConf.rotate % 360 === 0) playerConf.rotate = 0;
- if (!playerConf.videoHeight || !playerConf.videoWidth) {
- playerConf.videoWidth = playerConf.domElement.videoWidth;
- playerConf.videoHeight = playerConf.domElement.videoHeight;
- }
- if (playerConf.videoWidth > 0 && playerConf.videoHeight > 0) {
-
-
- if ((playerConf.rotate % 180) == 90) {
- playerConf.mFactor = playerConf.videoHeight / playerConf.videoWidth;
- } else {
- playerConf.mFactor = 1.0;
- }
-
-
- playerConf.cssTransform();
-
- $hs.tips('Rotation:' + playerConf.rotate + ' deg')
-
- }
-
- return TERMINATE
- break;
- // 按鍵迴車,進入FULLSCREEN
- case 'Enter':
- //t.callFullScreenBtn();
- break;
- case 'KeyN':
- $hs.pictureInPicture(player);
- return TERMINATE
- break;
- case 'KeyM':
- //console.log('m!', player.volume,player._volume)
-
- if (player.volume >= 0) {
-
- if (!player.volume || player.muted) {
-
- let newVol = player.volume || player._volume || 0.5;
- if (player.volume !== newVol) {
- player.volume = newVol;
- }
- player.muted = false;
- $hs.tips(false);
- $hs.tips('Mute: Off', undefined);
-
- } else {
-
- player._volume = player.volume;
- player._volume_p = player.volume;
- //player.volume = 0;
- player.muted = true;
- $hs.tips(false);
- $hs.tips('Mute: On', undefined);
-
- }
-
- }
-
- return TERMINATE
- break;
- default:
- // 按1-4設置播放速度 49-52;97-100
- numKey = +(event.key)
-
- if (numKey >= 1 && numKey <= 4) {
- $hs.tips(false);
- $hs.setPlaybackRate(numKey, 1)
- return TERMINATE
- }
- }
-
- }
- },
-
- handlerPlayerLockedMouseMove: function(e) {
- let player = $hs.player();
- let rootNode = getRoot(player);
-
- if (rootNode.pointerLockElement != player) {
- player.removeEventListener('mousemove', $hs.handlerPlayerLockedMouseMove)
- return;
- }
-
- let movementX = e.movementX || e.mozMovementX || e.webkitMovementX || 0,
- movementY = e.movementY || e.mozMovementY || e.webkitMovementY || 0;
-
- player.__xyOffset.x += movementX
- player.__xyOffset.y += movementY
- let ld = Math.sqrt(screen.width * screen.width + screen.height * screen.height) * .1
- let md = Math.sqrt(player.__xyOffset.x * player.__xyOffset.x + player.__xyOffset.y * player.__xyOffset.y);
- if (md > ld) $hs.playerActionLeave();
-
- },
-
- playerActionEnter: function() {
- let player = $hs.player();
-
- if (player) {
- player.__requestPointerLock__();
- player.__xyOffset = {
- x: 0,
- y: 0
- };
- player.addEventListener('mousemove', $hs.handlerPlayerLockedMouseMove)
- }
- },
-
- focusHookVDoc: null,
- focusHookVId: '',
-
- playerActionLeave: function() {
- let player = $hs.player();
- if (player) player.removeEventListener('mousemove', $hs.handlerPlayerLockedMouseMove)
- document.__exitPointerLock__();
- },
-
- handlerElementFocus: function(event) {
-
- function notAtVideo() {
- if ($hs.focusHookVDoc) $hs.focusHookVDoc = null
- if ($hs.focusHookVId) $hs.focusHookVId = ''
- }
-
- const hookVideo = $hs.focusHookVDoc && $hs.focusHookVId ? $hs.focusHookVDoc.querySelector(`VIDEO[_h5ppid=${$hs.focusHookVId}]`) : null
-
-
- if (hookVideo && (event.target == hookVideo || event.target.contains(hookVideo))) {
-
-
- } else {
-
- notAtVideo();
-
-
- }
-
- },
-
- handlerFullscreenChanged: function(event) {
-
-
- let videoElm = null,
- videosQuery = null;
- if (event && event.target) {
-
- if (event.target.nodeName == "VIDEO") videoElm = event.target;
- else if (videosQuery = event.target.querySelectorAll("VIDEO")) {
- if (videosQuery.length === 1) videoElm = videosQuery[0]
- }
-
-
- }
-
- if (videoElm) {
-
-
-
- const player = videoElm;
- const vpid = player.getAttribute('_h5ppid')
-
-
- event.target.setAttribute('_h5p_fsElm_', vpid)
-
-
- function hookTheActionedVideo() {
- $hs.focusHookVDoc = getRoot(player)
- $hs.focusHookVId = vpid
- }
-
- hookTheActionedVideo();
- setTimeout(function() {
- hookTheActionedVideo()
- }, 500)
-
-
-
- } else {
-
- $hs.focusHookVDoc = null
- $hs.focusHookVId = ''
-
- }
-
-
- },
-
- /* 按鍵響應方法 */
- handlerRootKeyDownEvent: function(event) {
-
- function notAtVideo() {
- if ($hs.focusHookVDoc) $hs.focusHookVDoc = null
- if ($hs.focusHookVId) $hs.focusHookVId = ''
- }
-
-
-
-
- if ($hs.intVideoInitCount > 0) {} else {
- // return notAtVideo();
- }
-
- // DOM Standard - either .key or .code
- // Here we adopt .code (physical layout)
-
- let pCode = event.code;
- if (typeof pCode != 'string') return;
- let player = $hs.player()
- if (!player) return; // no video tag
-
- let rootNode = getRoot(player);
- let isRequiredListen = false;
-
- let keyAsm = (event.shiftKey ? SHIFT : 0) | ((event.ctrlKey || event.metaKey) ? CTRL : 0) | (event.altKey ? ALT : 0);
-
-
- if (document.fullscreenElement || rootNode.pointerLockElement) {
- isRequiredListen = true;
-
-
- if (!keyAsm && pCode == 'Escape') {
- setTimeout(() => {
- if (document.fullscreenElement) {
- document.exitFullscreen();
- } else if (document.pointerLockElement) {
- $hs.playerActionLeave();
- }
- }, 700);
- return;
- }
-
-
- }
-
- const actionBoxRelation = $hs.getActionBoxRelationFromDOM(event.target)
- let hookVideo = null;
-
- if (actionBoxRelation) {
- $hs.focusHookVDoc = getRoot(actionBoxRelation.player);
- $hs.focusHookVId = actionBoxRelation.player.getAttribute('_h5ppid');
- hookVideo = actionBoxRelation.player;
- } else {
- hookVideo = $hs.focusHookVDoc && $hs.focusHookVId ? $hs.focusHookVDoc.querySelector(`VIDEO[_h5ppid=${$hs.focusHookVId}]`) : null
- }
-
- if (hookVideo) isRequiredListen = true;
-
- //console.log('root key', isRequiredListen, event.target, hookVideo)
-
- if (!isRequiredListen) return;
-
- //console.log('K01')
-
- /* 切換插件的可用狀態 */
- // Shift-`
- if (keyAsm == SHIFT && pCode == 'Backquote') {
- $hs.enable = !$hs.enable;
- $hs.tips(false);
- if ($hs.enable) {
- $hs.tips('啟用h5Player插件')
- } else {
- $hs.tips('禁用h5Player插件')
- }
- // 阻止事件冒泡
- event.stopPropagation()
- event.preventDefault()
- return false
- }
- if (!$hs.enable) {
- consoleLog('h5Player 已禁用~')
- return false
- }
-
- /* 非全局模式下,不聚焦則不執行快捷鍵的操作 */
-
- if (!keyAsm && pCode == 'Enter') { //not NumberpadEnter
- if (!rootNode.pointerLockElement && !document.fullscreenElement) {
- if (rootNode.pointerLockElement != player) {
- $hs.playerActionEnter();
-
- // 阻止事件冒泡
- event.stopPropagation()
- event.preventDefault()
- return false
- }
- } else if (rootNode.pointerLockElement && !document.fullscreenElement) {
- $hs.playerActionLeave();
-
- // 阻止事件冒泡
- event.stopPropagation()
- event.preventDefault()
- return false
- } else if (document.fullscreenElement) {
- document.exitFullscreen();
-
- // 阻止事件冒泡
- event.stopPropagation()
- event.preventDefault()
- return false
- }
- }
-
-
-
- let res = $hs.playerTrigger(player, event)
- if (res == TERMINATE) {
- event.stopPropagation()
- event.preventDefault()
- return false
- }
-
- },
- /* 設置播放進度 */
- setPlayProgress: function(player, curTime) {
- if (!player) return
- if (!curTime || Number.isNaN(curTime)) return
- player.currentTime = curTime
- if (curTime > 3) {
- $hs.tips(false);
- $hs.tips(`Playback Jumps to ${$hs.toolFormatCT(curTime)}`)
- if (player.paused) player.play();
- }
- }
- }
-
- function makeFilter(arr, k) {
- let res = ""
- for (const e of arr) {
- for (const d of e) {
- res += " " + (1.0 * d * k).toFixed(9)
- }
- }
- return res.trim()
- }
-
- function _add_filter(rootElm) {
- let rootView = null;
- if (rootElm && rootElm.nodeType > 0) {
- while (rootElm.parentNode && rootElm.parentNode.nodeType === 1) rootElm = rootElm.parentNode;
- rootView = rootElm.querySelector('body') || rootElm;
- } else {
- return;
- }
-
- if (rootView && rootView.querySelector && !rootView.querySelector('#_h5player_section_')) {
-
- let svgFilterElm = document.createElement('section')
- svgFilterElm.style.position = 'fixed';
- svgFilterElm.style.left = '-999px';
- svgFilterElm.style.width = '1px';
- svgFilterElm.style.top = '-999px';
- svgFilterElm.style.height = '1px';
- svgFilterElm.id = '_h5player_section_'
- let svgXML = `
- <svg id='_h5p_image' version="1.1" xmlns="http://www.w3.org/2000/svg">
- <defs>
- <filter id="_h5p_sharpen1">
- <feConvolveMatrix filterRes="100 100" style="color-interpolation-filters:sRGB" order="3" kernelMatrix="` + `
- -0.3 -0.3 -0.3
- -0.3 3.4 -0.3
- -0.3 -0.3 -0.3`.replace(/[\n\r]+/g, ' ').trim() + `" preserveAlpha="true"/>
- </filter>
- <filter id="_h5p_unsharpen1">
- <feConvolveMatrix style="color-interpolation-filters:sRGB;color-interpolation: sRGB;" order="5" kernelMatrix="` +
- makeFilter([
- [1, 4, 6, 4, 1],
- [4, 16, 24, 16, 4],
- [6, 24, -476, 24, 6],
- [4, 16, 24, 16, 4],
- [1, 4, 6, 4, 1]
- ], -1 / 256) + `" preserveAlpha="false"/>
- </filter>
- <filter id="_h5p_unsharpen3_05">
- <feConvolveMatrix style="color-interpolation-filters:sRGB;color-interpolation: sRGB;" order="3" kernelMatrix="` +
- makeFilter(
- [
- [0.025, 0.05, 0.025],
- [0.05, -1.1, 0.05],
- [0.025, 0.05, 0.025]
- ], -1 / .8) + `" preserveAlpha="false"/>
- </filter>
- <filter id="_h5p_unsharpen3_10">
- <feConvolveMatrix style="color-interpolation-filters:sRGB;color-interpolation: sRGB;" order="3" kernelMatrix="` +
- makeFilter(
- [
- [0.05, 0.1, 0.05],
- [0.1, -1.4, 0.1],
- [0.05, 0.1, 0.05]
- ], -1 / .8) + `" preserveAlpha="false"/>
- </filter>
- <filter id="_h5p_unsharpen5_05">
- <feConvolveMatrix style="color-interpolation-filters:sRGB;color-interpolation: sRGB;" order="5" kernelMatrix="` +
- makeFilter(
- [
- [0.025, 0.1, 0.15, 0.1, 0.025],
- [0.1, 0.4, 0.6, 0.4, 0.1],
- [0.15, 0.6, -18.3, 0.6, 0.15],
- [0.1, 0.4, 0.6, 0.4, 0.1],
- [0.025, 0.1, 0.15, 0.1, 0.025]
- ], -1 / 12.8) + `" preserveAlpha="false"/>
- </filter>
- <filter id="_h5p_unsharpen5_10">
- <feConvolveMatrix style="color-interpolation-filters:sRGB;color-interpolation: sRGB;" order="5" kernelMatrix="` +
- makeFilter(
- [
- [0.05, 0.2, 0.3, 0.2, 0.05],
- [0.2, 0.8, 1.2, 0.8, 0.2],
- [0.3, 1.2, -23.8, 1.2, 0.3],
- [0.2, 0.8, 1.2, 0.8, 0.2],
- [0.05, 0.2, 0.3, 0.2, 0.05]
- ], -1 / 12.8) + `" preserveAlpha="false"/>
- </filter>
- <filter id="_h5p_unsharpen9_05">
- <feConvolveMatrix style="color-interpolation-filters:sRGB;color-interpolation: sRGB;" order="9" kernelMatrix="` +
- makeFilter(
- [
- [0.025, 0.2, 0.7, 1.4, 1.75, 1.4, 0.7, 0.2, 0.025],
- [0.2, 1.6, 5.6, 11.2, 14, 11.2, 5.6, 1.6, 0.2],
- [0.7, 5.6, 19.6, 39.2, 49, 39.2, 19.6, 5.6, 0.7],
- [1.4, 11.2, 39.2, 78.4, 98, 78.4, 39.2, 11.2, 1.4],
- [1.75, 14, 49, 98, -4792.7, 98, 49, 14, 1.75],
- [1.4, 11.2, 39.2, 78.4, 98, 78.4, 39.2, 11.2, 1.4],
- [0.7, 5.6, 19.6, 39.2, 49, 39.2, 19.6, 5.6, 0.7],
- [0.2, 1.6, 5.6, 11.2, 14, 11.2, 5.6, 1.6, 0.2],
- [0.025, 0.2, 0.7, 1.4, 1.75, 1.4, 0.7, 0.2, 0.025]
- ], -1 / 3276.8) + `" preserveAlpha="false"/>
- </filter>
- <filter id="_h5p_unsharpen9_10">
- <feConvolveMatrix style="color-interpolation-filters:sRGB;color-interpolation: sRGB;" order="9" kernelMatrix="` +
- makeFilter(
- [
- [0.05, 0.4, 1.4, 2.8, 3.5, 2.8, 1.4, 0.4, 0.05],
- [0.4, 3.2, 11.2, 22.4, 28, 22.4, 11.2, 3.2, 0.4],
- [1.4, 11.2, 39.2, 78.4, 98, 78.4, 39.2, 11.2, 1.4],
- [2.8, 22.4, 78.4, 156.8, 196, 156.8, 78.4, 22.4, 2.8],
- [3.5, 28, 98, 196, -6308.6, 196, 98, 28, 3.5],
- [2.8, 22.4, 78.4, 156.8, 196, 156.8, 78.4, 22.4, 2.8],
- [1.4, 11.2, 39.2, 78.4, 98, 78.4, 39.2, 11.2, 1.4],
- [0.4, 3.2, 11.2, 22.4, 28, 22.4, 11.2, 3.2, 0.4],
- [0.05, 0.4, 1.4, 2.8, 3.5, 2.8, 1.4, 0.4, 0.05]
- ], -1 / 3276.8) + `" preserveAlpha="false"/>
- </filter>
- <filter id="_h5p_grey1">
- <feColorMatrix values="0.3333 0.3333 0.3333 0 0
- 0.3333 0.3333 0.3333 0 0
- 0.3333 0.3333 0.3333 0 0
- 0 0 0 1 0"/>
- <feColorMatrix type="saturate" values="0" />
- </filter>
- </defs>
- </svg>
- `;
-
- svgFilterElm.innerHTML = svgXML.replace(/[\r\n\s]+/g, ' ').trim();
-
- rootView.appendChild(svgFilterElm);
- }
-
- }
-
- /**
- * 某些網頁用了attachShadow closed mode,需要open才能獲取video標籤,例如百度雲盤
- * 解決參考:
- * https://developers.google.com/web/fundamentals/web-components/shadowdom?hl=zh-cn#closed
- * https://stackoverflow.com/questions/54954383/override-element-prototype-attachshadow-using-chrome-extension
- */
-
- const initForShadowRoot = async (shadowRoot) => {
- try {
- if (shadowRoot && shadowRoot.nodeType > 0 && shadowRoot.mode == 'open' && 'querySelectorAll' in shadowRoot) {
- if (!shadowRoot.host.hasAttribute('_h5p_shadowroot_')) {
- shadowRoot.host.setAttribute('_h5p_shadowroot_', '')
-
- $hs.bindDocEvents(shadowRoot);
- captureVideoEvents(shadowRoot);
-
- shadowRoots.push(shadowRoot)
- }
- }
- } catch (e) {
- console.log('h5Player: initForShadowRoot failed')
- }
- }
-
- function hackAttachShadow() { // attachShadow - DOM Standard
-
- let _prototype_ = window && window.HTMLElement ? window.HTMLElement.prototype : null;
- if (_prototype_ && typeof _prototype_.attachShadow == 'function') {
-
- let _attachShadow = _prototype_.attachShadow
-
- hackAttachShadow = null
- _prototype_.attachShadow = function() {
- let arg = [...arguments];
- if (arg[0] && arg[0].mode) arg[0].mode = 'open';
- let shadowRoot = _attachShadow.apply(this, arg);
- initForShadowRoot(shadowRoot);
- return shadowRoot
- };
-
- _prototype_.attachShadow.toString = () => _attachShadow.toString();
-
- }
-
- }
-
- function hackCreateShadowRoot() { // createShadowRoot - Deprecated
-
- let _prototype_ = window && window.HTMLElement ? window.HTMLElement.prototype : null;
- if (_prototype_ && typeof _prototype_.createShadowRoot == 'function') {
-
- let _createShadowRoot = _prototype_.createShadowRoot;
-
- hackCreateShadowRoot = null
- _prototype_.createShadowRoot = function() {
- const shadowRoot = _createShadowRoot.apply(this, arguments);
- initForShadowRoot(shadowRoot);
- return shadowRoot;
- };
- _prototype_.createShadowRoot.toString = () => _createShadowRoot.toString();
-
- }
- }
-
- /* 事件偵聽hack */
- function hackEventListener() {
- if (!window.EventTarget) return;
- const eventTargetPrototype = window.EventTarget.prototype;
- let _addEventListener = eventTargetPrototype.addEventListener;
- let _removeEventListener = eventTargetPrototype.removeEventListener;
- if (typeof _addEventListener == 'function' && typeof _removeEventListener == 'function') {} else return;
- hackEventListener = null;
-
- class Listeners {
-
- constructor(dom, type) {
- this._dom = dom;
- this._type = type;
- this.listenersCount = 0;
- this.hashList = {};
- }
- get baseFunc() {
- if (this._dom && this._type) {
- return this._dom['on' + this._type];
- }
- }
- get funcCount() {
- if (this._dom && this._type) {
- return (typeof this.baseFunc == 'function') * 1 + (this.listenersCount || 0)
- }
- }
-
-
- }
-
-
-
- let hackedEvtCount = 0;
-
-
- let watchList = ['click','mousedown','mouseup'];
-
- eventTargetPrototype.addEventListener = function() {
-
- let arg = arguments
- let type = arg[0]
- let listener = arg[1]
-
- if (!this || !(this instanceof EventTarget) || typeof type != 'string' || typeof listener != 'function') {
- return _addEventListener.apply(this, arguments)
- //unknown bug?
- }
-
-
- if(typeof arg[2]=='object' && arg[2]){
-
- }
-
- if (watchList.indexOf(type) < 0) {
- if(type=='mousewheel'||type=='touchstart'){
- if(typeof arg[2] =='boolean' || arguments.length==2){
- const options={
- passive:true,
- capture:arg[2]===true
- }
- return _addEventListener.call(this, type, listener, options);
- }
- }
- if(this.nodeType===1 && this.nodeName!="BODY" && this.nodeName!="HTML"){
- if (type=='mouseout' || type=='mouseover' || type=='focusin' || type=='focusout' || type=='mouseenter' || type=='mouseleave' || type=='mousemove'){
- const nListener=listener.__nListener__ || function(){ return window.requestAnimationFrame(()=>listener.apply(this,arguments)) }
- listener.__nListener__=nListener;
- arguments[1]=nListener
- }else{
- //console.log(type)
- }
- }
- return _addEventListener.apply(this, arguments);
- }
-
- let res;
- res = _addEventListener.apply(this, arg)
-
- let boolCapture = (arg[2] && typeof arg[2] == 'object') ? (arg[2].capture === true) : (arg[2] === true)
-
- this._listeners = this._listeners || {}
- this._listeners[type] = this._listeners[type] || new Listeners(this, type)
- let uid = 100000 + (++hackedEvtCount);
- let listenerObj = {
- listener,
- options: arg[2],
- uid: uid,
- useCapture: boolCapture
- }
- this._listeners[type].hashList[uid + ''] = listenerObj;
- this._listeners[type].listenersCount++;
-
- return res
-
- }
- // hack removeEventListener
- eventTargetPrototype.removeEventListener = function() {
-
- let arg = arguments
- let type = arg[0]
- let listener = arg[1]
-
-
- if (!this || !(this instanceof EventTarget) || typeof type != 'string' || typeof listener != 'function') {
- return _removeEventListener.apply(this, arguments)
- //unknown bug?
- }
-
-
-
- if (watchList.indexOf(type) < 0){
- if(typeof listener.__nListener__=='function') arguments[1]=listener.__nListener__
- return _removeEventListener.apply(this, arguments);
- }
-
- let boolCapture = (arg[2] && typeof arg[2] == 'object') ? (arg[2].capture === true) : (arg[2] === true)
-
- let defaultRemoval = true;
- if (this._listeners && this._listeners[type] && this._listeners[type].hashList) {
- let hashList = this._listeners[type].hashList
- for (let k in hashList) {
- if (hashList[k].listener === listener && hashList[k].useCapture == boolCapture) {
- delete hashList[k];
- this._listeners[type].listenersCount--;
- break;
- }
- }
- }
- if (defaultRemoval) return _removeEventListener.apply(this, arg);
-
-
- }
- eventTargetPrototype.addEventListener.toString = () => _addEventListener.toString();
- eventTargetPrototype.removeEventListener.toString = () => _removeEventListener.toString();
-
-
- }
-
-
- function initShadowRoots(rootDoc) {
- function onReady() {
- var treeWalker = rootDoc.createTreeWalker(
- rootDoc.documentElement,
- NodeFilter.SHOW_ELEMENT, {
- acceptNode: (node) => (node.shadowRoot ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP)
- }
- );
- var nodeList = [];
- while (treeWalker.nextNode()) nodeList.push(treeWalker.currentNode);
- for (const node of nodeList) {
- initForShadowRoot(node.shadowRoot)
- }
- }
- if (rootDoc.readyState !== 'loading') {
- onReady();
- } else {
- rootDoc.addEventListener('DOMContentLoaded', onReady);
- }
- }
-
- function captureVideoEvents(rootDoc) {
-
- var g = function(evt) {
-
-
- var domElement = evt.target || this || null
- if (domElement && domElement.nodeType == 1 && domElement.nodeName == "VIDEO") {
- var video = domElement
- if (!domElement.getAttribute('_h5ppid')) handlerVideoFound(video);
- if (domElement.getAttribute('_h5ppid')) {
- switch (evt.type) {
- case 'loadedmetadata':
- return $hs.handlerVideoLoadedMetaData.call(video, evt);
- // case 'playing':
- // return $hs.handlerVideoPlaying.call(video, evt);
- // case 'pause':
- // return $hs.handlerVideoPause.call(video, evt);
- // case 'volumechange':
- // return $hs.handlerVideoVolumeChange.call(video, evt);
- }
- }
- }
-
-
- }
-
- // using capture phase
- rootDoc.addEventListener('loadedmetadata', g, true);
-
- }
-
- function handlerVideoFound(video) {
-
- if (!video) return;
- if (video.getAttribute('_h5ppid')) return;
- let alabel = video.getAttribute('aria-label')
- if (alabel && typeof alabel == "string" && alabel.toUpperCase() == "GIF") return;
-
-
- consoleLog('handlerVideoFound', video)
-
- $hs.intVideoInitCount = ($hs.intVideoInitCount || 0) + 1;
- let vpid = 'h5p-' + $hs.intVideoInitCount
- consoleLog(' - HTML5 Video is detected -', `Number of Videos: ${$hs.intVideoInitCount}`)
- if ($hs.intVideoInitCount === 1) $hs.fireGlobalInit();
- video.setAttribute('_h5ppid', vpid)
-
-
- playerConfs[vpid] = new PlayerConf();
- playerConfs[vpid].domElement = video;
- playerConfs[vpid].domActive = DOM_ACTIVE_FOUND;
-
- let rootNode = getRoot(video);
-
- if (rootNode.host) $hs.getPlayerBlockElement(video); // shadowing
- let rootElm = domAppender(rootNode) || document.documentElement //48763
- _add_filter(rootElm) // either main document or shadow node
-
-
-
- video.addEventListener('playing', $hs.handlerVideoPlaying, true);
- video.addEventListener('pause', $hs.handlerVideoPause, true);
- video.addEventListener('volumechange', $hs.handlerVideoVolumeChange, true);
-
-
-
- }
-
-
-
- hackAttachShadow()
- hackCreateShadowRoot()
- hackEventListener()
-
-
- window.addEventListener('message', $hs.handlerWinMessage, false);
- $hs.bindDocEvents(document);
- captureVideoEvents(document);
- initShadowRoots(document);
-
-
- let windowsLD = (function() {
- let ls_res = [];
- try {
- ls_res = [!!window.localStorage, !!window.top.localStorage];
- } catch (e) {}
- try {
- let winp = window;
- let winc = 0;
- while (winp !== window.top && winp && ++winc) winp = winp.parentNode;
- ls_res.push(winc);
- } catch (e) {}
- return ls_res;
- })();
-
- consoleLogF('- h5Player Plugin Loaded -', ...windowsLD)
-
- function isInCrossOriginFrame() {
- let result = true;
- try {
- if (window.top.localStorage || window.top.location.href) result = false;
- } catch (e) {}
- return result
- }
-
- if (isInCrossOriginFrame()) consoleLog('cross origin frame detected');
-
-
- const $bv = {
-
- uWin: (window.unsafeWindow || window),
-
- boostVideoPerformanceActivate: function() {
-
- if ($bz.boosted) return;
- $bz.boosted = true;
-
- },
-
-
- boostVideoPerformanceDeactivate: function() {
-
- if (!$bz.boosted) return;
- $bz.boosted = false;
-
-
- }
-
- }
-
-
-
-
- })(window.unsafeWindow || window);