HTML5 Video Player Enhance

To enhance the functional features of html5 player (h5player) supporting all websites,shortcuts similar to Potplayer - Adjustment of 亮度(brightness),飽和度(saturate),對比度(contrast),速度(playback speed); frame , hue , blur

Versão de: 15/09/2019. Veja: a última versão.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         HTML5 Video Player Enhance
// @version      2.5.10(tw)
// @description  To enhance the functional features of html5 player (h5player) supporting all websites,shortcuts similar to Potplayer - Adjustment of 亮度(brightness),飽和度(saturate),對比度(contrast),速度(playback speed); frame , hue , blur 
// @author       CY Fung
// @match        http://*/*
// @match        https://*/*
// @run-at       document-start
// @grant        GM_addStyle
// @namespace https://greatest.deepsurf.us/users/371179
// ==/UserScript==

/* 元素FULLSCREEN-API,同時相容網頁FULLSCREEN */
class FullScreen {
    constructor (dom, pageMode) {
        this.dom = dom


        // 默認FULLSCREEN模式,如果傳入pageMode則表示進行的是頁面FULLSCREEN操作
        this.pageMode = pageMode || false
        let fullPageStyle = `
._webfullscreen_ {
display: block !important;
position: fixed !important;
width: 100% !important;
height: 100% !important;
top: 0 !important;
left: 0 !important;
background: #000 !important;
}
._webfullscreen_zindex_ {
z-index: 999998 !important;
}
`
        if (!window._hasInitFullPageStyle_) {
            GM_addStyle(fullPageStyle)
            window._hasInitFullPageStyle_ = true
        }

        let c = this.getContainer()
        let enterFn = c.requestFullscreen || c.webkitRequestFullScreen || c.mozRequestFullScreen || c.msRequestFullScreen
       // console.log(enterFn)

    }

    eachParentNode (dom, fn) {
        let parent = dom.parentNode
        while (parent && parent.classList) {
            let isEnd = fn(parent, dom)
            parent = parent.parentNode
            if (isEnd) {
                break
            }
        }
    }

    getContainer () {
        let t = this
        if (t._container_) return t._container_

        let d = t.dom
        let domBox = d.getBoundingClientRect()
        let container = d
        t.eachParentNode(d, function (parentNode) {
            let parentBox = parentNode.getBoundingClientRect()
            if (parentBox.width <= domBox.width && parentBox.height <= domBox.height) {
                container = parentNode
            } else {
                return true
            }
        })
        t._container_ = container
        return container
    }

    isFull () {
        return this.dom.classList.contains('_webfullscreen_')
    }

    isFullScreen (d) {
        d = d || document
        return !!(d.fullscreen || d.webkitIsFullScreen || d.mozFullScreen ||
                  d.fullscreenElement || d.webkitFullscreenElement || d.mozFullScreenElement)
    }

    enterFullScreen () {
        let c = this.getContainer()
        let enterFn = c.requestFullscreen || c.webkitRequestFullScreen || c.mozRequestFullScreen || c.msRequestFullScreen
        enterFn && enterFn.call(c)
    }

    enter () {
        let t = this
        if (t.isFull()) return
        let container = t.getContainer()
        let needSetIndex = false
        if (t.dom === container) {
            needSetIndex = true
        }
        this.eachParentNode(t.dom, function (parentNode) {
            parentNode.classList.add('_webfullscreen_')
            if (container === parentNode || needSetIndex) {
                needSetIndex = true
                parentNode.classList.add('_webfullscreen_zindex_')
            }
        })
        t.dom.classList.add('_webfullscreen_')
        let fullScreenMode = !t.pageMode
        if (fullScreenMode) {
            t.enterFullScreen()
        }
    }

    exitFullScreen () {
        let d = document
        let exitFn = d.exitFullscreen || d.webkitExitFullscreen || d.mozCancelFullScreen || d.msExitFullscreen
        exitFn && exitFn.call(d)
    }

    exit () {
        let t = this
        t.dom.classList.remove('_webfullscreen_')
        this.eachParentNode(t.dom, function (parentNode) {
            parentNode.classList.remove('_webfullscreen_')
            parentNode.classList.remove('_webfullscreen_zindex_')
        })
        let fullScreenMode = !t.pageMode
        if (fullScreenMode || t.isFullScreen()) {
            t.exitFullScreen()
        }
    }

    toggle () {
        this.isFull() ? this.exit() : this.enter()
    }
}

var gbl={};
var gv001={
            isFull: false,
            isIframe: false,
            autoCheckCount: 0
        };


    function exec(fn) {
        var script = document.createElement('script');
        script.setAttribute("type", "application/javascript");
        script.textContent = '(' + fn + ')();';
        document.body.appendChild(script);
        document.body.removeChild(script);
    }

    var ggx=(function() {

        var gv = gv001;

        //Html5规则[播放器最外层],适用于自适应大小HTML5播放器
        var html5Rules = {
            'www.acfun.cn': ['.player-container .player'],
            'www.bilibili.com': ['#bilibiliPlayer'],
            'www.douyu.com': ['#js-player-video-case'],
            'www.huya.com': ['#videoContainer'],
            'www.twitch.tv': ['.player'],
            'www.youtube.com': ['#movie_player'],
            'www.yy.com': ['#player'],
            'www.viu.com':['#viu-player']
        };

        //通用html5播放器
        var generalPlayerRules = ['.dplayer', '.video-js', '.jwplayer'];

        if (window.top !== window.self) {
            gv.isIframe = true;
        }

        if (navigator.language.toLocaleLowerCase() == 'zh-cn') {
            gv.btnText = {
                max: '网页全屏',
                pip: '画中画',
                tip: 'Iframe内视频,请用鼠标点击视频后重试'
            };
        } else {
            gv.btnText = {
                max: 'Maximize',
                pip: 'PicInPic',
                tip: 'Iframe video. Please click on the video and try again'
            };
        }

        var tool = {
            getRect: function(element) {
                var rect = element.getBoundingClientRect();
                var scroll = tool.getScroll();
                return {
                    pageX: rect.left + scroll.left,
                    pageY: rect.top + scroll.top,
                    screenX: rect.left,
                    screenY: rect.top
                };
            },
            isHalfFullClient: function(element) {
                var client = tool.getClient();
                var rect = tool.getRect(element);
                if ((Math.abs(client.width - element.offsetWidth) < 21 && rect.screenX < 20) || (Math.abs(client.height - element.offsetHeight) < 21 && rect.screenY < 10)) {
                    if (Math.abs(element.offsetWidth / 2 + rect.screenX - client.width / 2) < 10 && Math.abs(element.offsetHeight / 2 + rect.screenY - client.height / 2) < 10) {
                        return true;
                    } else {
                        return false;
                    }
                } else {
                    return false;
                }
            },
            isAllFullClient: function(element) {
                var client = tool.getClient();
                var rect = tool.getRect(element);
                if ((Math.abs(client.width - element.offsetWidth) < 21 && rect.screenX < 20) && (Math.abs(client.height - element.offsetHeight) < 21 && rect.screenY < 10)) {
                    return true;
                } else {
                    return false;
                }
            },
            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: function(css) {
                var style = document.createElement('style');
                style.type = 'text/css';
                var node = document.createTextNode(css);
                style.appendChild(node);
                document.head.appendChild(style);
                return style;
            },
            matchRule: function(str, rule) {
                return new RegExp("^" + rule.split("*").join(".*") + "$").test(str);
            },
            createButton: function(id) {
                var btn = document.createElement('tbdiv');
                btn.id = id;
                btn.onclick = function() {
                    maximize.playerControl();
                };
                document.body.appendChild(btn);
                return btn;
            },
            addTip: async function(str) {
                if (!document.getElementById('catTip')) {
                    var tip = document.createElement('tbdiv');
                    tip.id = 'catTip';
                    tip.innerHTML = str;
                    tip.style.cssText = 'transition: all 0.8s ease-out;background: none repeat scroll 0 0 #27a9d8;color: #FFFFFF;font: 1.1em "微软雅黑";margin-left: -250px;overflow: hidden;padding: 10px;position: fixed;text-align: center;bottom: 100px;z-index: 300;',
                        document.body.appendChild(tip);
                    tip.style.right = -tip.offsetWidth - 5 + 'px';
                    await new Promise(resolve => {
                        tip.style.display = 'block';
                        setTimeout(() => {
                            tip.style.right = '25px';
                            resolve('OK');
                        }, 300);
                    });
                    await new Promise(resolve => {
                        setTimeout(() => {
                            tip.style.right = -tip.offsetWidth - 5 + 'px';
                            resolve('OK');
                        }, 3500);
                    });
                    await new Promise(resolve => {
                        setTimeout(() => {
                            document.body.removeChild(tip);
                            resolve('OK');
                        }, 1000);
                    });
                }
            }
        };

        var setButton = {
            init: function() {
                if (!document.getElementById('playerControlBtn')) {
                    init();
                }
                if (gv.isIframe && tool.isHalfFullClient(gv.player)) {
                    window.parent.postMessage('iframeVideo', '*');
                    return;
                }
                this.show();
            },
            show: function() {
                gv.player.removeEventListener('mouseleave', handle.leavePlayer, false);
                gv.player.addEventListener('mouseleave', handle.leavePlayer, false);

                if (!gv.isFull) {
                    document.removeEventListener('scroll', handle.scrollFix, false);
                    document.addEventListener('scroll', handle.scrollFix, false);
                }
                //gv.controlBtn.style.display = 'block';
                //gv.controlBtn.style.visibility = 'visible';
                if (document.pictureInPictureEnabled && gv.player.nodeName != 'OBJECT' && gv.player.nodeName != 'EMBED') {
                   // gv.picinpicBtn.style.display = 'block';
                   // gv.picinpicBtn.style.visibility = 'visible';
                }
                this.locate();
            },
            locate: function() {
                var playerRect = tool.getRect(gv.player);
                gv.controlBtn.style.opacity = '0.5';
                gv.controlBtn.innerHTML = gv.btnText.max;
                gv.controlBtn.style.top = playerRect.screenY - 20 + 'px';
                gv.controlBtn.style.left = playerRect.screenX - 64 + gv.player.offsetWidth + 'px';
                gv.picinpicBtn.style.opacity = '0.5';
                gv.picinpicBtn.innerHTML = gv.btnText.pip;
                gv.picinpicBtn.style.top = gv.controlBtn.style.top;
                gv.picinpicBtn.style.left = playerRect.screenX - 64 + gv.player.offsetWidth - 54 + 'px';
            }
        };

        ggx.setButton=setButton;
        var handle = {
            getPlayer: function(e) {
                if (gv.isFull) {
                    return;
                }
                gv.mouseoverEl = e.target;
                var hostname = document.location.hostname;
                var players = [];
                for (var i in html5Rules) {
                    if (tool.matchRule(hostname, i)) {
                        for (var v of html5Rules[i]) {
                            players = document.querySelectorAll(v);
                            if (players.length > 0) {
                                break;
                            }
                        }
                        break;
                    }
                }
                if (players.length == 0) {
                    for (var v of generalPlayerRules) {
                        players = document.querySelectorAll(v);
                        if (players.length > 0) {
                            break;
                        }
                    }
                }
                if (players.length == 0 && e.target.nodeName != 'VIDEO' && document.querySelectorAll('video').length > 0) {
                    var videos = document.querySelectorAll('video');
                    for (var v of videos) {
                        var vRect = v.getBoundingClientRect();
                        if (e.clientX >= vRect.x - 2 && e.clientX <= vRect.x + vRect.width + 2 && e.clientY >= vRect.y - 2 && e.clientY <= vRect.y + vRect.height + 2 && v.offsetWidth > 399 && v.offsetHeight > 220) {
                            players = [];
                            players[0] = handle.autoCheck(v);
                            gv.autoCheckCount = 1;
                            break;
                        }
                    }
                }
                if (players.length > 0) {
                    var path = e.path || e.composedPath();
                    for (var v of players) {
                        if (path.indexOf(v) > -1) {
                            gv.player = v;
                            setButton.init();
                            return;
                        }
                    }
                }
                switch (e.target.nodeName) {
                    case 'VIDEO':
                    case 'OBJECT':
                    case 'EMBED':
                        if (e.target.offsetWidth > 399 && e.target.offsetHeight > 220) {
                            gv.player = e.target;
                            setButton.init();
                        }
                        break;
                    default:
                        handle.leavePlayer();
                }
            },
            autoCheck: function(v) {
                var tempPlayer, el = v;
                gv.playerChilds = [];
                gv.playerChilds.push(v);
                while (el = el.parentNode) {
                    if (Math.abs(v.offsetWidth - el.offsetWidth) < 15 && Math.abs(v.offsetHeight - el.offsetHeight) < 15) {
                        tempPlayer = el;
                        gv.playerChilds.push(el);
                    } else {
                        break;
                    }
                }
                return tempPlayer;
            },
            leavePlayer: function() {
                if (gv.controlBtn.style.visibility == 'visible') {
                    gv.controlBtn.style.opacity = '';
                    gv.controlBtn.style.visibility = '';
                    gv.picinpicBtn.style.opacity = '';
                    gv.picinpicBtn.style.visibility = '';
                    gv.player.removeEventListener('mouseleave', handle.leavePlayer, false);
                    document.removeEventListener('scroll', handle.scrollFix, false);
                }
            },
            scrollFix: function(e) {
                clearTimeout(gv.scrollFixTimer);
                gv.scrollFixTimer = setTimeout(() => {
                    setButton.locate();
                }, 20);
            },
            hotKey: function(e) {
                //默认退出键为ESC。需要修改为其他快捷键的请搜索"keycode",修改为按键对应的数字。
                if (e.keyCode == 27) {
                    maximize.playerControl();
                }
                //默认画中画快捷键为F2。
                if (e.keyCode == 113) {
                    pictureInPicture();
                }
            },
            receiveMessage: async function(e) {
                switch (e.data) {
                    case 'iframePicInPic':
                        console.log('messege:iframePicInPic');
                        if (!document.pictureInPictureElement) {
                            await document.querySelector('video').requestPictureInPicture()
                                .catch(error => {
                                tool.addTip(gv.btnText.tip);
                            });
                        } else {
                            await document.exitPictureInPicture();
                        }
                        break;
                    case 'iframeVideo':
                        console.log('messege:iframeVideo');
                        if (!gv.isFull) {
                            gv.player = gv.mouseoverEl;
                            setButton.init();
                        }
                        break;
                    case 'parentFull':
                        console.log('messege:parentFull');
                        gv.player = gv.mouseoverEl;
                        if (gv.isIframe) {
                            window.parent.postMessage('parentFull', '*');
                        }
                        maximize.checkParent();
                        maximize.fullWin();
                        if (getComputedStyle(gv.player).left != '0px') {
                            tool.addStyle('#htmlToothbrush #bodyToothbrush .playerToothbrush {left:0px !important;width:100vw !important;}');
                        }
                        gv.isFull = true;
                        break;
                    case 'parentSmall':
                        console.log('messege:parentSmall');
                        if (gv.isIframe) {
                            window.parent.postMessage('parentSmall', '*');
                        }
                        maximize.smallWin();
                        break;
                    case 'innerFull':
                        console.log('messege:innerFull');
                        if (gv.player.nodeName == 'IFRAME') {
                            gv.player.contentWindow.postMessage('innerFull', '*');
                        }
                        maximize.checkParent();
                        maximize.fullWin();
                        break;
                    case 'innerSmall':
                        console.log('messege:innerSmall');
                        if (gv.player.nodeName == 'IFRAME') {
                            gv.player.contentWindow.postMessage('innerSmall', '*');
                        }
                        maximize.smallWin();
                        break;
                }
            }
        };

        var maximize = {
            playerControl: function() {
                if (!gv.player) {
                    return;
                }
                this.checkParent();
                if (!gv.isFull) {
                    if (gv.isIframe) {
                        window.parent.postMessage('parentFull', '*');
                    }
                    if (gv.player.nodeName == 'IFRAME') {
                        gv.player.contentWindow.postMessage('innerFull', '*');
                    }
                    this.fullWin();
                    if (gv.autoCheckCount > 0 && !tool.isHalfFullClient(gv.playerChilds[0])) {
                        if (gv.autoCheckCount > 10) {
                            for (var v of gv.playerChilds) {
                                v.classList.add('videoToothbrush');

                            }
                            return;
                        }
                        var tempPlayer = handle.autoCheck(gv.playerChilds[0]);
                        gv.autoCheckCount++;
                        maximize.playerControl();
                        gv.player = tempPlayer;
                        maximize.playerControl();
                    } else {
                        gv.autoCheckCount = 0;
                    }
                } else {
                    if (gv.isIframe) {
                        window.parent.postMessage('parentSmall', '*');
                    }
                    if (gv.player.nodeName == 'IFRAME') {
                        gv.player.contentWindow.postMessage('innerSmall', '*');
                    }
                    this.smallWin();
                }
            },
            checkParent: function() {
                if (gv.isFull) {
                    return;
                }
                gv.playerParents = [];
                var full = gv.player;
                while (full = full.parentNode) {
                    if (full.nodeName == 'BODY') {
                        break;
                    }
                    if (full.getAttribute) {
                        gv.playerParents.push(full);
                    }
                }
            },
            fullWin: function() {
                if (!gv.isFull) {
                    document.removeEventListener('mouseover', handle.getPlayer, false);
                    gv.backHtmlId = document.body.parentNode.id;
                    gv.backBodyId = document.body.id;
                    if (document.location.hostname == 'www.youtube.com' && document.querySelector('#movie_player .ytp-size-button .ytp-svg-shadow').getBoundingClientRect().width == 20) {
                        document.querySelector('.ytp-size-button').click();
                        gv.ytbStageChange = true;
                    }
                    gv.leftBtn.style.display = 'block';
                    gv.rightBtn.style.display = 'block';
                    gv.picinpicBtn.style.display = '';
                    gv.controlBtn.style.display = '';
                    this.addClass();
                }
                gv.isFull = true;
            },
            addClass: function() {
                document.body.parentNode.id = 'htmlToothbrush';
                document.body.id = 'bodyToothbrush';
                for (var v of gv.playerParents) {
                    v.classList.add('parentToothbrush');
                    //父元素position:fixed会造成层级错乱
                    if (getComputedStyle(v).position == 'fixed') {
                        v.classList.add('absoluteToothbrush');
                    }
                }
                gv.player.classList.add('playerToothbrush');
                if (gv.player.nodeName == 'VIDEO') {
                    gv.backControls = gv.player.controls;
                    gv.player.controls = true;
                }
                window.dispatchEvent(new Event('resize'));
            },
            smallWin: function() {
                document.body.parentNode.id = gv.backHtmlId;
                document.body.id = gv.backBodyId;
                for (var v of gv.playerParents) {
                    v.classList.remove('parentToothbrush');
                    v.classList.remove('absoluteToothbrush');
                }
                gv.player.classList.remove('playerToothbrush');
                if (document.location.hostname == 'www.youtube.com' && gv.ytbStageChange) {
                    document.querySelector('.ytp-size-button').click();
                    gv.ytbStageChange = false;
                }
                if (gv.player.nodeName == 'VIDEO') {
                    gv.player.controls = gv.backControls;
                }
                gv.leftBtn.style.display = '';
                gv.rightBtn.style.display = '';
                gv.controlBtn.style.display = '';
                document.addEventListener('mouseover', handle.getPlayer, false);
                window.dispatchEvent(new Event('resize'));
                gv.isFull = false;
            }
        };

        ggx.maximize=maximize;

        var pictureInPicture = function() {
            if (!document.pictureInPictureElement) {
                if (gv.player) {
                    if (gv.player.nodeName == 'IFRAME') {
                        gv.player.contentWindow.postMessage('iframePicInPic', '*');
                    } else {
                        var videoElm=gv.player.parentNode.querySelector('video');
                        if('requestPictureInPicture' in videoElm) videoElm.requestPictureInPicture(); else gbl.h5Player.tips('PIP is not supported.');
                    }
                } else {
                    var videoElm=document.querySelector('video');
                    if('requestPictureInPicture' in videoElm) videoElm.requestPictureInPicture(); else gbl.h5Player.tips('PIP is not supported.');
                }
            } else {
                document.exitPictureInPicture();
            }
        }
        ggx.pictureInPicture=pictureInPicture;

        var init = function() {
            gv.picinpicBtn = document.createElement('tbdiv');
            gv.picinpicBtn.id = "picinpicBtn";
            gv.picinpicBtn.onclick = function() {
                pictureInPicture();
            };
            document.body.appendChild(gv.picinpicBtn);
            gv.controlBtn = tool.createButton('playerControlBtn');
            gv.leftBtn = tool.createButton('leftFullStackButton');
            gv.rightBtn = tool.createButton('rightFullStackButton');
            if (getComputedStyle(gv.controlBtn).position != 'fixed') {
                tool.addStyle([
                    '#htmlToothbrush #bodyToothbrush .parentToothbrush .bilibili-player-video {margin:0 !important;}',
                    '#htmlToothbrush, #bodyToothbrush {overflow:hidden !important;zoom:100% !important;}',
                    '#htmlToothbrush #bodyToothbrush .parentToothbrush {overflow:visible !important;z-index:auto !important;transform:none !important;-webkit-transform-style:flat !important;transition:none !important;contain:none !important;}',
                    '#htmlToothbrush #bodyToothbrush .absoluteToothbrush {position:absolute !important;}',
                    '#htmlToothbrush #bodyToothbrush .playerToothbrush {position:fixed !important;top:0px !important;left:0px !important;width:100vw !important;height:100vh !important;max-width:none !important;max-height:none !important;min-width:0 !important;min-height:0 !important;margin:0 !important;padding:0 !important;z-index:2147483647 !important;border:none !important;background-color:#000 !important;transform:none !important;}',
                    '#htmlToothbrush #bodyToothbrush .parentToothbrush video {object-fit:contain !important;}',
                    '#htmlToothbrush #bodyToothbrush .parentToothbrush .videoToothbrush {width:100vw !important;height:100vh !important;}',
                    '#playerControlBtn {text-shadow: none;visibility:hidden;opacity:0;display:none;transition: all 0.5s ease;cursor: pointer;font: 12px "微软雅黑";margin:0;width:64px;height:20px;line-height:20px;border:none;text-align: center;position: fixed;z-index:2147483647;background-color: #27A9D8;color: #FFF;} #playerControlBtn:hover {visibility:visible;opacity:1;background-color:#2774D8;}',
                    '#picinpicBtn {text-shadow: none;visibility:hidden;opacity:0;display:none;transition: all 0.5s ease;cursor: pointer;font: 12px "微软雅黑";margin:0;width:53px;height:20px;line-height:20px;border:none;text-align: center;position: fixed;z-index:2147483647;background-color: #27A9D8;color: #FFF;} #picinpicBtn:hover {visibility:visible;opacity:1;background-color:#2774D8;}',
                    '#leftFullStackButton{display:none;position:fixed;width:1px;height:100vh;top:0;left:0;z-index:2147483647;background:#000;}',
                    '#rightFullStackButton{display:none;position:fixed;width:1px;height:100vh;top:0;right:0;z-index:2147483647;background:#000;}'
                ].join('\n'));
            }
        };

        init();

        document.addEventListener('mouseover', handle.getPlayer, false);
        //document.addEventListener('keydown', handle.hotKey, false);
        window.addEventListener('message', handle.receiveMessage, false);

    });




(function () {
    /**
   * 任務配置中心 Task Control Center
   * 用於配置所有無法進行通用處理的任務,如不同網站的FULLSCREEN方式不一樣,必須調用網站本身的FULLSCREEN邏輯,才能確保字幕、彈幕等正常工作
   * */
    const TCC = {
        /**
     * 配置示例
     * 父級鍵名對應的是一級域名,
     * 子級鍵名對應的相關功能名稱,鍵值對應的該功能要觸發的點擊選擇器或者要調用的相關函數
     * 所有子級的鍵值都支持使用選擇器觸發或函數調用
     * 配置了子級的則使用子級配置邏輯進行操作,否則使用默認邏輯
     * 注意:include,exclude這兩個子級鍵名除外,這兩個是用來進行url範圍匹配的
     * */
        'demo.demo': {
            'fullScreen': '.fullscreen-btn',
            'exitFullScreen': '.exit-fullscreen-btn',
            'webFullScreen': function () {},
            'exitWebFullScreen': '.exit-fullscreen-btn',
            'autoPlay': '.player-start-btn',
            'pause': '.player-pause',
            'play': '.player-play',
            'switchPlayStatus': '.player-play',
            'playbackRate': function () {},
            'currentTime': function () {},
            'addCurrentTime': '.add-currenttime',
            'subtractCurrentTime': '.subtract-currenttime',
            // 自定義快捷鍵的執行方式,如果是組合鍵,必須是 ctrl-->shift-->alt 這樣的順序,沒有可以忽略,鍵名必須全小寫
            'shortcuts': {
                /* 註冊要執行自定義回調操作的快捷鍵 */
                register: [
                    'ctrl+shift+alt+c',
                    'ctrl+shift+c',
                    'ctrl+alt+c',
                    'ctrl+c',
                    'c'
                ],
                /* 自定義快捷鍵的回調操作 */
                callback: function (h5Player, taskConf, data) {
                    let { event, player } = data
                    console.log(event, player)
                }
            },
            /* 當前域名下需包含的路徑信息,默認整個域名下所有路徑可用 必須是正則 */
            include: /^.*/,
            /* 當前域名下需排除的路徑信息,默認不排除任何路徑 必須是正則 */
            exclude: /\t/
        },
        'youtube.com': {
            // 'webFullScreen': 'button.ytp-size-button',
            'fullScreen': 'button.ytp-fullscreen-button'
        },
        'netflix.com': {
            'fullScreen': 'button.button-nfplayerFullscreen',
            'addCurrentTime': 'button.button-nfplayerFastForward',
            'subtractCurrentTime': 'button.button-nfplayerBackTen'
        },
        'bilibili.com': {
            'fullScreen': '[data-text="進入FULLSCREEN"]',
            'webFullScreen': '[data-text="網頁FULLSCREEN"]',
            'autoPlay': '.bilibili-player-video-btn-start',
            'switchPlayStatus': '.bilibili-player-video-btn-start'
        },
        'live.bilibili.com': {
            'fullScreen': '.bilibili-live-player-video-controller-fullscreen-btn button',
            'webFullScreen': '.bilibili-live-player-video-controller-web-fullscreen-btn button',
            'switchPlayStatus': '.bilibili-live-player-video-controller-start-btn button'
        },
        'iqiyi.com': {
            'fullScreen': '.iqp-btn-fullscreen',
            'webFullScreen': '.iqp-btn-webscreen',
            'init': function (h5Player, taskConf) {
                // 隱藏水印
                hideDom('.iqp-logo-box')
                // 移除暫停廣告
                GM_addStyle(`
div[templatetype="common_pause"]{ display:none }
`)
            }
        },
        'youku.com': {
            'fullScreen': '.control-fullscreen-icon',
            'init': function (h5Player, taskConf) {
                // 隱藏水印
                hideDom('.youku-layer-logo')
            }
        },
        'ted.com': {
            'fullScreen': 'button.Fullscreen'
        },
        'v.qq.com': {
            'pause': '.container_inner .txp-shadow-mod]',
            'play': '.container_inner .txp-shadow-mod',
            'shortcuts': {
                register: ['c', 'x', 'z'],
                callback: function (h5Player, taskConf, data) {
                    let { event } = data
                    let key = event.key.toLowerCase()
                    let speedItems = document.querySelectorAll('.container_inner txpdiv[data-role="txp-button-speed-list"] .txp_menuitem')

                    /* 利用sessionStorage下的playbackRate進行設置 */
                    if (window.sessionStorage.playbackRate && /(c|x|z)/.test(key)) {
                        let curSpeed = Number(window.sessionStorage.playbackRate)
                        let perSpeed = curSpeed - 0.1 >= 0 ? curSpeed - 0.1 : 0.1
                        let nextSpeed = curSpeed + 0.1 <= 4 ? curSpeed + 0.1 : 4
                        let targetSpeed = curSpeed
                        switch (key) {
                            case 'z' :
                                targetSpeed = 1
                                break
                            case 'c' :
                                targetSpeed = nextSpeed
                                break
                            case 'x' :
                                targetSpeed = perSpeed
                                break
                        }
                        window.sessionStorage.playbackRate = targetSpeed
                        h5Player.setCurrentTime(0.1, true)
                        h5Player.setPlaybackRate(targetSpeed, true)
                        return true
                    }

                    /* 模擬點擊觸發 */
                    if (speedItems.length >= 3 && /(c|x|z)/.test(key)) {
                        let curIndex = 1
                        speedItems.forEach((item, index) => {
                            if (item.classList.contains('txp_current')) {
                                curIndex = index
                            }
                        })
                        let perIndex = curIndex - 1 >= 0 ? curIndex - 1 : 0
                        let nextIndex = curIndex + 1 < speedItems.length ? curIndex + 1 : speedItems.length - 1

                        let target = speedItems[1]
                        switch (key) {
                            case 'z' :
                                target = speedItems[1]
                                break
                            case 'c' :
                                target = speedItems[nextIndex]
                                break
                            case 'x' :
                                target = speedItems[perIndex]
                                break
                        }

                        target.click()
                        let speedNum = Number(target.innerHTML.replace('x'))
                        h5Player.setPlaybackRate(speedNum)
                    }
                }
            },
            'init': function (h5Player, taskConf) {
                // 隱藏水印
                hideDom('.txp-watermark')
            }
        },
        'pan.baidu.com': {
            'fullScreen': function (h5Player, taskConf) {
                h5Player.playerInstance.parentNode.querySelector('.vjs-fullscreen-control').click()
            }
        },

        /**
     * 獲取域名 , 目前實現方式不好,需改造,對地區性域名(如com.cn)、三級及以上域名支持不好
     * */
        getDomain: function () {
            let host = window.location.host
            let domain = host
            let tmpArr = host.split('.')
            if (tmpArr.length > 2) {
                tmpArr.shift()
                domain = tmpArr.join('.')
            }
            return domain
        },
        /**
     * 格式化配置任務
     * @param isAll { boolean } -可選 默認只格式當前域名或host下的配置任務,傳入true則將所有域名下的任務配置都進行格式化
     */
        formatTCC: function (isAll) {
            let t = this
            let keys = Object.keys(t)
            let domain = t.getDomain()
            let host = window.location.host

            function formatter (item) {
                let defObj = {
                    include: /^.*/,
                    exclude: /\t/
                }
                item.include = item.include || defObj.include
                item.exclude = item.exclude || defObj.exclude
                return item
            }

            let result = {}
            keys.forEach(function (key) {
                let item = t[key]
                if (isObj(item)) {
                    if (isAll) {
                        item = formatter(item)
                        result[key] = item
                    } else {
                        if (key === host || key === domain) {
                            item = formatter(item)
                            result[key] = item
                        }
                    }
                }
            })
            return result
        },
        /* 判斷所提供的配置任務是否適用於當前URL */
        isMatch: function (taskConf) {
            let url = window.location.href
            let isMatch = false
            if (taskConf.include.test(url)) {
                isMatch = true
            }
            if (taskConf.exclude.test(url)) {
                isMatch = false
            }
            return isMatch
        },
        /**
     * 獲取任務配置,只能獲取到當前域名下的任務配置信息
     * @param taskName {string} -可選 指定具體任務,默認返回所有類型的任務配置
     */
        getTaskConfig: function () {
            let t = this
            if (!t._hasFormatTCC_) {
                t.formatTCC()
                t._hasFormatTCC_ = true
            }
            let domain = t.getDomain()
            let taskConf = t[window.location.host] || t[domain]

            if (taskConf && t.isMatch(taskConf)) {
                return taskConf
            }

            return {}
        },
        /**
     * 執行當前頁面下的相應任務
     * @param taskName {object|string} -必選,可直接傳入任務配置對象,也可用是任務名稱的字符串信息,自己去查找是否有任務需要執行
     * @param data {object} -可選,傳給回調函數的數據
     */
        doTask: function (taskName, data) {
            let t = this
            let isDo = false
            if (!taskName) return isDo
            let taskConf = isObj(taskName) ? taskName : t.getTaskConfig()

            if (!isObj(taskConf) || !taskConf[taskName]) return isDo

            let task = taskConf[taskName]

            let wrapDom = h5Player.getPlayerWrapDom()

            if (taskName === 'shortcuts') {
                if (isObj(task) && getType(task.callback) === 'function') {
                    task.callback(h5Player, taskConf, data)
                    isDo = true
                }
            } else if (getType(task) === 'function') {
                task(h5Player, taskConf, data)
                isDo = true
            } else {
                /* 觸發選擇器上的點擊事件 */
                if (wrapDom && wrapDom.querySelector(task)) {
                    // 在video的父元素裡查找,是為了盡可能相容多實例下的邏輯
                    wrapDom.querySelector(task).click()
                    isDo = true
                } else if (document.querySelector(task)) {
                    document.querySelector(task).click()
                    isDo = true
                }
            }
            return isDo
        }
    }

    /**
   * 元素監聽器
   * @param selector -必選
   * @param fn -必選,元素存在時的回調
   * @param shadowRoot -可選 指定監聽某個shadowRoot下面的DOM元素
   * 參考:https://javascript.ruanyifeng.com/dom/mutationobserver.html
   */
    function ready (selector, fn, shadowRoot) {
        let listeners = []
        let win = window
        let doc = shadowRoot || win.document
        let MutationObserver = win.MutationObserver || win.WebKitMutationObserver
        let observer

        function $ready (selector, fn) {
            // 儲存選擇器和回調函數
            listeners.push({
                selector: selector,
                fn: fn
            })
            if (!observer) {
                // 監聽document變化
                observer = new MutationObserver(check)
                observer.observe(shadowRoot || doc.documentElement, {
                    childList: true,
                    subtree: true
                })
            }
            // 檢查該節點是否已經在DOM中
            check()
        }

        function check () {
            for (let i = 0; i < listeners.length; i++) {
                var listener = listeners[i]
                var elements = doc.querySelectorAll(listener.selector)
                for (let j = 0; j < elements.length; j++) {
                    var element = elements[j]
                    if (!element._isMutationReady_) {
                        element._isMutationReady_ = true
                        listener.fn.call(element, element)
                    }
                }
            }
        }

        $ready(selector, fn)
    }

    function hideDom (selector, delay) {
        setTimeout(function () {
            const dom = document.querySelector(selector)
            if (dom) {
                dom.style.opacity = 0
            }
        }, delay || 1000 * 3)
    }

    /**
   * 某些網頁用了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
   */
    function hackAttachShadow () {
        if (window._hasHackAttachShadow_) return
        try {
            window._shadowDomList_ = []
            window.Element.prototype._attachShadow = window.Element.prototype.attachShadow
            window.Element.prototype.attachShadow = function () {
                let arg = arguments
                if (arg[0] && arg[0]['mode']) {
                    // 強制使用 open mode
                    arg[0]['mode'] = 'open'
                }
                let shadowRoot = this._attachShadow.apply(this, arg)
                // 存一份shadowDomList
                window._shadowDomList_.push(shadowRoot)

                // 在document下面添加 addShadowRoot 自定義事件
                let shadowEvent = new window.CustomEvent('addShadowRoot', {
                    shadowRoot,
                    detail: {
                        shadowRoot,
                        message: 'addShadowRoot',
                        time: new Date()
                    },
                    bubbles: true,
                    cancelable: true
                })
                document.dispatchEvent(shadowEvent)

                return shadowRoot
            }
            window._hasHackAttachShadow_ = true
        } catch (e) {
            console.error('hackAttachShadow error by h5player plug-in')
        }
    }
    hackAttachShadow()

    /* 事件偵聽hack */
    function hackEventListener () {
        const EVENT = window.EventTarget.prototype
        if (EVENT._addEventListener) return
        EVENT._addEventListener = EVENT.addEventListener
        EVENT._removeEventListener = EVENT.removeEventListener
        window._listenerList_ = []

        // hack addEventListener
        EVENT.addEventListener = function () {
            let arg = arguments
            let type = arg[0]
            let listener = arg[1]
            this._addEventListener.apply(this, arg)
            this._listeners = this._listeners || {}
            this._listeners[type] = this._listeners[type] || []
            let listenerObj = {
                target: this,
                type,
                listener,
                options: arg[2],
                addTime: new Date().getTime()
            }
            window._listenerList_.push(listenerObj)
            this._listeners[type].push(listenerObj)
        }

        // hack removeEventListener
        EVENT.removeEventListener = function () {
            let arg = arguments
            let type = arg[0]
            let listener = arg[1]
            this._removeEventListener.apply(this, arg)
            this._listeners = this._listeners || {}
            this._listeners[type] = this._listeners[type] || []

            let result = []
            this._listeners[type].forEach(function (listenerObj) {
                if (listenerObj.listener !== listener) {
                    result.push(listenerObj)
                }
            })
            this._listeners[type] = result
        }
    }
    hackEventListener()

    let quickSort = function (arr) {
        if (arr.length <= 1) { return arr }
        var pivotIndex = Math.floor(arr.length / 2)
        var pivot = arr.splice(pivotIndex, 1)[0]
        var left = []
        var right = []
        for (var i = 0; i < arr.length; i++) {
            if (arr[i] < pivot) {
                left.push(arr[i])
            } else {
                right.push(arr[i])
            }
        }
        return quickSort(left).concat([pivot], quickSort(right))
    }

    /**
   * 向上查找操作
   * @param dom {Element} -必選 初始dom元素
   * @param fn {function} -必選 每一級ParentNode的回調操作
   * 如果函數返回true則表示停止向上查找動作
   */
    function eachParentNode (dom, fn) {
        let parent = dom.parentNode
        while (parent) {
            let isEnd = fn(parent, dom)
            parent = parent.parentNode
            if (isEnd) {
                break
            }
        }
    }

    /**
   * 準確地獲取對象的具體類型
   * @param obj { all } -必選 要判斷的對象
   * @returns {*} 返回判斷的具體類型
   */
    function getType (obj) {
        if (obj == null) {
            return String(obj)
        }
        return typeof obj === 'object' || typeof obj === 'function'
            ? (obj.constructor && obj.constructor.name && obj.constructor.name.toLowerCase()) ||
            /function\s(.+?)\(/.exec(obj.constructor)[1].toLowerCase()
        : typeof obj
    }

    function isObj (obj) {
        return getType(obj) === 'object'
    }

    /**
   * 深度合併兩個可枚舉的對象
   * @param objA {object} -必選 對象A
   * @param objB {object} -必選 對象B
   * @param concatArr {boolean} -可選 合併數組,默認遇到數組的時候,直接以另外一個數組替換當前數組,將此設置true則,遇到數組的時候一律合併,而不是直接替換
   * @returns {*|void}
   */
    function mergeObj (objA, objB, concatArr) {
        function isObj (obj) {
            return Object.prototype.toString.call(obj) === '[object Object]'
        }
        function isArr (arr) {
            return Object.prototype.toString.call(arr) === '[object Array]'
        }
        if (!isObj(objA) || !isObj(objB)) return objA
        function deepMerge (objA, objB) {
            let keys = Object.keys(objB)
            keys.forEach(function (key) {
                let subItemA = objA[key]
                let subItemB = objB[key]
                if (typeof subItemA === 'undefined') {
                    objA[key] = subItemB
                } else {
                    if (isObj(subItemA) && isObj(subItemB)) {
                        /* 進行深層合併 */
                        objA[key] = deepMerge(subItemA, subItemB)
                    } else {
                        if (concatArr && isArr(subItemA) && isArr(subItemB)) {
                            objA[key] = subItemA.concat(subItemB)
                        } else {
                            objA[key] = subItemB
                        }
                    }
                }
            })
            return objA
        }
        return deepMerge(objA, objB)
    }

    /**
   * 多對象深度合併,合併規則基於mergeObj,但不存在concatArr選項
   * @returns {*}
   */
    function merge () {
        let result = arguments[0]
        for (var i = 0; i < arguments.length; i++) {
            if (i) {
                result = mergeObj(result, arguments[i])
            }
        }
        return result
    }

    /* ua信息偽裝 */
    function fakeUA (ua) {
        Object.defineProperty(navigator, 'userAgent', {
            value: ua,
            writable: false,
            configurable: false,
            enumerable: true
        })
    }

    /* ua信息來源:https://developers.whatismybrowser.com */
    const userAgentMap = {
        android: {
            chrome: 'Mozilla/5.0 (Linux; Android 9; SM-G960F Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.157 Mobile Safari/537.36',
            firefox: 'Mozilla/5.0 (Android 7.0; Mobile; rv:57.0) Gecko/57.0 Firefox/57.0'
        },
        iPhone: {
            safari: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Mobile/15E148 Safari/604.1',
            chrome: 'Mozilla/5.0 (iPhone; CPU iPhone OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/74.0.3729.121 Mobile/15E148 Safari/605.1'
        },
        iPad: {
            safari: 'Mozilla/5.0 (iPad; CPU OS 12_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1 Mobile/15E148 Safari/604.1',
            chrome: 'Mozilla/5.0 (iPad; CPU OS 12_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/74.0.3729.155 Mobile/15E148 Safari/605.1'
        }
    }

    const fakeConfig = {
        // 'tv.cctv.com': userAgentMap.iPhone.chrome,
        // 'v.qq.com': userAgentMap.iPad.chrome,
        'open.163.com': userAgentMap.iPhone.chrome,
        'm.open.163.com': userAgentMap.iPhone.chrome
    }

    function debugMsg () {
        let arg = Array.from(arguments)
        arg.unshift('h5player debug message :')
        console.info.apply(console, arg)
    }

    let h5Player = {
        /* 提示文本的字號 */
        fontSize: 16,
        enable: true,
        globalMode: true,
        playerInstance: null,
        scale: 1,
        translate: {
            x: 0,
            y: 0
        },
        playbackRate: 1,
        /* 快進快退步長 */
        skipStep: 5,
        /* 獲取當前播放器的實例 */
        player: function () {
            let t = this
            return t.playerInstance || t.getPlayerList()[0]
        },
        /* 每個網頁可能存在的多個video播放器 */
        getPlayerList: function () {
            let list = []
            function findPlayer (context) {
                context.querySelectorAll('video').forEach(function (player) {
                    list.push(player)
                })
            }
            findPlayer(document)

            // 被封裝在 shadow dom 裡面的video
            if (window._shadowDomList_) {
                window._shadowDomList_.forEach(function (shadowRoot) {
                    findPlayer(shadowRoot)
                })
            }

            return list
        },
        getPlayerWrapDom: function () {
            let t = this
            let player = t.player()
            if (!player) return

            let wrapDom = null
            let playerBox = player.getBoundingClientRect()
            eachParentNode(player, function (parent) {
                if (parent === document || !parent.getBoundingClientRect) return
                let parentBox = parent.getBoundingClientRect()
                if (parentBox.width && parentBox.height) {
                    if (parentBox.width === playerBox.width && parentBox.height === playerBox.height) {
                        wrapDom = parent
                    }
                }
            })
            return wrapDom
        },
        /**
     * 初始化播放器實例
     * @param isSingle 是否為單實例video標籤
     */
        initPlayerInstance: function (isSingle) {
            let t = this
            if (!t.playerInstance) return

            let player = t.playerInstance


            if(!player.hasAttribute('tabindex'))      player.setAttribute('tabindex','-1');
            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']='-webkit-optimize-contrast';
            player.style['image-rendering']='crisp-edges';
            // <video id="viu-player_html5_api" tabindex="-1" class="vjs-tech" autoplay="" playsinline="playsinline" x-webkit-airplay="deny" preload="auto"></video>


            t.filter.reset()
            t.initTips()
            t.initPlaybackRate()
            t.isFoucs()

            setTimeout(function(){
                if(player&&player.parentNode){

                    //if(player.getClientRects()[0].height>0&&player.parentNode.getClientRects()[0].height==0&&player.parentNode.style.display==''&&player.style.display=='absolute')player.parentNode.style.display='contents';
                    /*
          console.log(1212)
          var player_focus_input=document.createElement('a');
          player_focus_input.setAttribute('style',`
position:absolute;
z-index:999;
top:-9934px;
left:-9912px;
opacity:0;
display:inline-block;
width:10px;
height:10px;
`);
          player_focus_input.setAttribute('href','javascript:window.qq=1;void(0);');
          player.parentNode.appendChild(player_focus_input);
          t.player_focus_input=player_focus_input;
*/
                }
            },100);



            /* 增加通用FULLSCREEN,網頁FULLSCREEN-api */
            player._fullScreen_ = new FullScreen(player)
            player._fullPageScreen_ = new FullScreen(player, true)

            if (!player._hasCanplayEvent_) {
                player.addEventListener('canplay', function (event) {
                    t.initAutoPlay(player)
                })
                player._hasCanplayEvent_ = true
            }

            /* 播放的時候進行相關同步操作 */
            if (!player._hasPlayingInitEvent_) {
                let setPlaybackRateOnPlayingCount = 0
                player.addEventListener('playing', function (event) {
                    if (setPlaybackRateOnPlayingCount === 0) {
                        /* 同步之前設定的播放速度 */
                        t.setPlaybackRate()

                        if (isSingle === true) {
                            /* 恢復播放進度和進行進度記錄 */
                            t.setPlayProgress(player)
                            setTimeout(function () {
                                t.playProgressRecorder(player)
                            }, 1000 * 3)
                        }
                    } else {
                        t.setPlaybackRate(null, true)
                    }
                    setPlaybackRateOnPlayingCount += 1
                })
                player._hasPlayingInitEvent_ = true
            }

            /* 進行自定義初始化操作 */
            let taskConf = TCC.getTaskConfig()
            if (taskConf.init) {
                TCC.doTask('init', player)
            }
        },
        initPlaybackRate: function () {
            let t = this
            t.playbackRate = t.getPlaybackRate()
        },
        getPlaybackRate: function () {
            let t = this
            let playbackRate = window.localStorage.getItem('_h5_player_playback_rate_') || t.playbackRate
            return Number(Number(playbackRate).toFixed(1))
        },

        change_playerBox:function (tips){
            let t = this;
            let player = t.player()
            var playerBox=player.parentNode

            while(playerBox&&playerBox.offsetHeight==0) playerBox=playerBox.parentNode;


            while(playerBox&&playerBox.offsetHeight<player.offsetHeight) playerBox=playerBox.parentNode;

            playerBox=playerBox||player.parentNode
            if(playerBox){
                if(!tips.parentNode || tips.parentNode!==playerBox){
                    playerBox.insertBefore(tips,playerBox.firstChild);
                }
            }
        },
        callFullScreenBtn7:function(){

            let t = this;

            let player = t.player()
            if(gv001.player&&gv001.player.contains(player)){}else{
                gv001.player=player;
                ggx.setButton.init();
            }
            ggx.maximize.playerControl();
        },
        callFullScreenBtn8:function(){
            let t = this;

            let player = t.player()
            var doubleClickEvent = document.createEvent('MouseEvents');
            doubleClickEvent.initEvent('dblclick', true, true);
            player.dispatchEvent(doubleClickEvent); // inside method
        },
        callFullScreenBtn0:function(){
            return this.callFullScreenBtn7();
            let t = this;

            let player = t.player()
            if (!TCC.doTask('fullScreen') && player._fullScreen_) player._fullScreen_.toggle();
        },
        callFullScreenBtn5:function(playerBox){

            return this.callFullScreenBtn7();

            let t = this;
            let player = t.player()


            function get_gPlayer(){
                // parent of same size as video player

                var q1=JSON.stringify(player.getClientRects()[0])

                var pPlayer=player
                var qPlayer=playerBox
                for(var q3=0;q3<4;q3++){
                    if(!qPlayer) break;
                    var q2=JSON.stringify(qPlayer.getClientRects()[0])
                    if(q1==q2) {
                        pPlayer=qPlayer
                        qPlayer=qPlayer.parentNode
                    }else{
                        break;
                    }
                }
                var gPlayer=pPlayer
                return gPlayer;
            }
            var gPlayer=get_gPlayer();
            console.log('gPlayer',gPlayer)

            console.log('DOM fullscreen')
            gPlayer.requestFullscreen()
        },

        callFullScreenBtn: function () {
            //return this.callFullScreenBtn7();
            //return this.callFullScreenBtn8();
            let t = this;
            let player = t.player()

            if(!player ||!player.ownerDocument) return this.callFullScreenBtn0();

            let tcn= player.getAttribute('_h5player_tips')||( t.tipsClassName );
            console.log('tcn',tcn)
            let playerBox = player.ownerDocument.querySelector('.' + tcn)

            if(!playerBox)return this.callFullScreenBtn0();

            function checkFullScreen(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;
            }

            let chFull=checkFullScreen(player.ownerDocument);
            if(chFull===null)  return this.callFullScreenBtn0();


            if(chFull ===true){
                player.ownerDocument.exitFullscreen();

                //  var el = [t.player_focus_input]; if (el) {el = el[0].click()}
            }
            else {


                var pPlayer=player
                var qPlayer=playerBox

                var clicked=false;


                // try to find the fullscreen button
                for(var q3=0;q3<4;q3++){//max 4 layers
                    if(!qPlayer) break;

                    var fs1=qPlayer.querySelectorAll('[class*="fullscreen"]')

                    if(fs1.length>0){


                        // -- indiv-elm --

                        var gs1=Array.prototype.map.call(fs1,function(elm){return {elm:elm,visible:null,click:null,childElementCount:null,contains:null}});


                        if(('_listeners' in document)){
                            gs1.forEach(function(elp){
                                var elm=elp.elm;
                                elp.click=elm._listeners && elm._listeners.click && elm._listeners.click.length>0
                            })
                        }


                        if('childElementCount' in player){
                            gs1.forEach(function(elp){
                                var elm=elp.elm;
                                elp.childElementCount=elm.childElementCount;
                            })
                        }



                        if('getBoundingClientRect' in player){
                            gs1.forEach(function(elp){
                                var elm=elp.elm;
                                var rect=elm.getBoundingClientRect();
                                elp.visible=rect.height*rect.width>0
                            })

                        }


                        gs1=gs1.filter(function(elp){return elp.click})
                        //console.log('gs1',gs1)

                        // -- inter-elm --

                        if('contains' in player){
                            gs1.forEach(function(elp){
                                var elm=elp.elm;
                                if(elp.childElementCount===0)return false;
                                elp.contains= gs1.filter(function(elq){ return elm==elq.elm?false:elm.contains(elq.elm) }).length>0;
                            })
                        }

                        var gs2=gs1.filter(function(elp){return !elp.contains&&elp.visible})
                        //console.log('gs2',gs2)




                        if(gs2.length>=1){
                            // pick the last btn if there is more than one
                            gs2[gs2.length-1].elm.click();
                            clicked=true;
                            console.log('original fullscreen')
                            break;

                        }




                    }

                    pPlayer=qPlayer
                    qPlayer=qPlayer.parentNode
                }



                if(!clicked){
                    //cannot find -> default
                    this.callFullScreenBtn5(playerBox);

                }



            }


        },

        /* 設置播放速度 */
        setPlaybackRate: function (num, notips) {
            let taskConf = TCC.getTaskConfig()
            if (taskConf.playbackRate) {
                TCC.doTask('playbackRate')
                return
            }

            let t = this
            let player = t.player()
            let curPlaybackRate
            if (num) {
                num = Number(num)
                if (Number.isNaN(num)) {
                    console.error('h5player: 播放速度轉換出錯')
                    return false
                }
                if (num <= 0) {
                    num = 0.1
                }
                num = Number(num.toFixed(1))
                curPlaybackRate = num
            } else {
                curPlaybackRate = t.getPlaybackRate()
            }

            /* 記錄播放速度的信息 */
            window.localStorage.setItem('_h5_player_playback_rate_', curPlaybackRate)

            t.playbackRate = curPlaybackRate
            player.playbackRate = curPlaybackRate

            /* 本身處於1被播放速度的時候不再提示 */
            if (!num && curPlaybackRate === 1) return
            !notips && t.tips('Playback speed: ' + player.playbackRate + 'x')
        },
        /**
     * 初始化自動播放邏輯
     * 必須是配置了自動播放按鈕選擇器得的才會進行自動播放
     */
        initAutoPlay: function (p) {
            let t = this
            let player = p || t.player()

            // 在輪詢重試的時候,如果實例變了,或處於隱藏頁面中則不進行自動播放操作
            if (!player || (p && p !== t.player()) || document.hidden) return

            let taskConf = TCC.getTaskConfig()
            if (player && taskConf.autoPlay && player.paused) {
                TCC.doTask('autoPlay')
                if (player.paused) {
                    // 輪詢重試
                    if (!player._initAutoPlayCount_) {
                        player._initAutoPlayCount_ = 1
                    }
                    player._initAutoPlayCount_ += 1
                    if (player._initAutoPlayCount_ >= 10) {
                        return false
                    }
                    setTimeout(function () {
                        t.initAutoPlay(player)
                    }, 200)
                }
            }
        },
        setWebFullScreen: function () {
            return this.callFullScreenBtn();
        },
        setCurrentTime: function (num, notips) {
            if (!num) return
            num = Number(num)
            let _num = Math.abs(Number(num.toFixed(1)))

            let t = this
            let player = t.player()
            let taskConf = TCC.getTaskConfig()
            if (taskConf.currentTime) {
                TCC.doTask('currentTime')
                return
            }

            if (num > 0) {
                if (taskConf.addCurrentTime) {
                    TCC.doTask('addCurrentTime')
                } else {
                    player.currentTime += _num
                    !notips && t.tips(_num + ' Sec. Forward')
                }
            } else {
                if (taskConf.subtractCurrentTime) {
                    TCC.doTask('subtractCurrentTime')
                } else {
                    player.currentTime -= _num
                    !notips && t.tips(_num + ' Sec. Backward')
                }
            }
        },
        setVolume: function (num) {
            if (!num) return
            num = Number(num)
            let _num = Math.abs(Number(num.toFixed(2)))

            let t = this
            let player = t.player()
            if (num > 0) {
                if (player.volume < 1) {
                    player.volume += _num
                }
            } else {
                if (player.volume > 0) {
                    player.volume -= _num
                }
            }
            t.tips('Volume: ' + parseInt(player.volume * 100) + '%')
        },
        setFakeUA (ua) {
            ua = ua || userAgentMap.iPhone.safari

            /* 記錄設定的ua信息 */
            window.localStorage.setItem('_h5_player_user_agent_', ua)
            fakeUA(ua)
        },

        /* ua偽裝切換開關 */
        switchFakeUA (ua) {
            let customUA = window.localStorage.getItem('_h5_player_user_agent_')
            if (customUA) {
                window.localStorage.removeItem('_h5_player_user_agent_')
            } else {
                this.setFakeUA(ua)
            }

            debugMsg('ua', navigator.userAgent)
        },

        switchPlayStatus: function () {
            let t = this
            let player = t.player()
            let taskConf = TCC.getTaskConfig()
            if (taskConf.switchPlayStatus) {
                TCC.doTask('switchPlayStatus')
                return
            }

            if (player.paused) {
                if (taskConf.play) {
                    TCC.doTask('play')
                } else {
                    player.play()
                    t.tips('Playback resumed')
                }
            } else {
                if (taskConf.pause) {
                    TCC.doTask('pause')
                } else {
                    player.pause()
                    t.tips('Playback paused')
                }
            }
        },
        tipsClassName: 'html_player_enhance_tips',
        tips: function (str, duration) {
            let t = h5Player
            let player = t.player()
            if (!player) {
                console.log('h5Player Tips:', str)
                return true
            }


            let parentNode = player.parentNode
            /*
      // 修復部分提示按鈕位置異常問題
      let backupStyle = parentNode.getAttribute('style-backup') || ''
      let defStyle = parentNode.getAttribute('style') || ''
      if (backupStyle === null) {
        parentNode.setAttribute('style-backup', defStyle)
        backupStyle = defStyle
      }
      let newStyleArr = backupStyle.split(';')

      let oldPosition = parentNode.getAttribute('def-position') || window.getComputedStyle(parentNode).position
      if (parentNode.getAttribute('def-position') === null) {
        parentNode.setAttribute('def-position', oldPosition || '')
      }
      if (['static', 'inherit', 'initial', 'unset', ''].includes(oldPosition)) {
        newStyleArr.push('position: relative')
      }

      let playerBox = player.getBoundingClientRect()
      newStyleArr.push('min-width:' + playerBox.width + 'px')
      newStyleArr.push('min-height:' + playerBox.height + 'px')
      parentNode.setAttribute('style', newStyleArr.join(';'))
*/

            let tipsSelector = '.' +  (player.getAttribute('_h5player_tips')|| t.tipsClassName)
            let tipsDom = player.ownerDocument.querySelector(tipsSelector)||(t.initTips(),player.ownerDocument.querySelector(tipsSelector))

            if (!tipsDom) {
                console.log('init h5player tips dom error...')
                return false
            }

            t.change_playerBox(tipsDom);


            let style = tipsDom.style



            if(str===false){
                tipsDom.innerText = '';
                clearTimeout(this.on_off[0])
                clearTimeout(this.on_off[1])
                clearTimeout(this.on_off[2])
                style.display = 'none'
                style.opacity = 0
            }else{


                if(duration===undefined) duration=2000

                tipsDom.innerText = str

                for (var i = 0; i < 3; i++) {
                    if (this.on_off[i]) clearTimeout(this.on_off[i])
                }

                clearTimeout(this.on_off[0])
                clearTimeout(this.on_off[1])
                clearTimeout(this.on_off[2])
                //style.display = 'none'
                //style.opacity = 0
                t.on_off[0] = setTimeout(function () {
                    style.display = 'block'
                    style.opacity = 0.9
                }, 15)
                t.on_off[1] = setTimeout(function () {
                    style.display = 0.7
                }, 50)

                if(duration>0){
                    t.on_off[2] = setTimeout(function () {
                        // 隱藏提示框和還原樣式
                        style.display = 'none'
                        style.opacity = 0
                        //parentNode.setAttribute('style', backupStyle)
                    }, duration)
                }

            }

        },
        /* 設置提示DOM的樣式 */
        initTips: function () {
            let t = this
            let player = t.player()
            let parentNode = player.parentNode

            var tcn= player.getAttribute('_h5player_tips')||( t.tipsClassName+'_'+(+new Date));
            player.setAttribute('_h5player_tips',tcn)

            if (player.ownerDocument.querySelector('.' + tcn)) return

            let tipsStyle = `
position: absolute;
z-index: 999;
font-size: ${t.fontSize || 16}px;
padding: 10px;
background: rgba(0,0,0,0.4);
color:white;
top: 50%;
left: 50%;max-width:500px;max-height:50px;
transform: translate(-50%,-50%);
transition: all 500ms ease;
opacity: 0;
border-radius:3px;
display: none;
-webkit-font-smoothing: subpixel-antialiased;
-moz-font-smoothing: subpixel-antialiased;
-ms-font-smoothing: subpixel-antialiased;
font-smoothing: subpixel-antialiased;
font-family: 'microsoft yahei', Verdana, Geneva, sans-serif;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-drag: none;
-khtml-user-select: none;
-moz-user-select: none;
-moz-user-select: -moz-none;
-ms-user-select: none;
pointer-events: none;
user-select: none;
`
            let tips = document.createElement('div')
            tips.setAttribute('style', tipsStyle)
            tips.setAttribute('class', tcn)
            tips.setAttribute('unselectable','on')
            t.change_playerBox(tips);
        },
        on_off: new Array(3),
        rotate: 0,
        fps: 30,
        /* 濾鏡效果 */
        filter: {
            key: {},
            view_units: {'hue-rotate':'deg','blur':'px'},
            setup: function () {
                var view = ''
                for(var view_key in this.key){
                    var view_unit = this.view_units[view_key]||''
                    view += view_key+'('+(+this.key[view_key]||0).toFixed(3)+view_unit+') '
                    this.key[view_key] = Number(+this.key[view_key]||0)
                }
                view += 'url("#unsharpen1")'
                h5Player.player().style.WebkitFilter = view
            },
            reset: function () {
                this.key['brightness'] = 1
                this.key['contrast'] = 1
                this.key['saturate'] = 1
                this.key['hue-rotate'] = 0
                this.key['blur'] = 0
                this.setup()
            }
        },
        _isFoucs: false,

        /* 播放器的聚焦事件 */
        isFoucs: function () {
            let t = h5Player
            let player = t.player()
            if (!player) return
            /*
      if(!document.documentElement.hasAttribute('_h5_player_mousemove')){
        FullScreen._evt_move_target=[];
        document.documentElement.setAttribute('_h5_player_mousemove','')
        document.addEventListener('mousemove', function (e) {
          let w=h5Player.playerInstance;
          let pfocus=(FullScreen._evt_move_target||[]).indexOf(w)>=0
          FullScreen._evt_move_target = e.path||[e.target]
          let cfocus=(FullScreen._evt_move_target||[]).indexOf(w)>=0
          if(pfocus^cfocus) w.focus()
          console.log(pfocus,cfocus)
            window.pp1=h5Player
            window.pp2=player
        },true)
      }*/
            player.addEventListener('mouseenter', function (e) {
                h5Player._isFoucs = true
            })
            player.addEventListener('mouseleave', function (e) {
                h5Player._isFoucs = false
            })

            player.ownerDocument.addEventListener('focusout', function(e){

                if(!this.hasFocus()){
                    setTimeout(function(){
                        h5Player.tips('focus is lost',-1);
                    },1);
                }

            },true)

            player.ownerDocument.addEventListener('focusin', function(e){

                if(this.hasFocus()){
                    setTimeout(function(){
                        h5Player.tips(false);
                    },1);
                }

            },true)

        },

        keyCodeList: [13, 16, 17, 18, 27, 32, 37, 38, 39, 40, 49, 50, 51, 52, 67, 68, 69, 70, 73, 74, 75,78, 79, 80, 81, 82, 83, 84, 85, 87, 88, 89, 90, 97, 98, 99, 100, 220],
        keyList: ['enter', 'shift', 'control', 'alt', 'escape', ' ', 'arrowleft', 'arrowright', 'arrowright', 'arrowup', 'arrowdown',
                  '1', '2', '3', '4', 'c', 'd', 'e', 'f', 'i', 'j', 'k', 'o', 'p', 'q', 'r', 's', 't', 'u', 'w', 'x', 'y', 'z', '\\', '|'],
        keyMap: {
            'enter': 13,
            'shift': 16,
            'ctrl': 17,
            'alt': 18,
            'esc': 27,
            'space': 32,
            '←': 37,
            '↑': 38,
            '→': 39,
            '↓': 40,
            '1': 49,
            '2': 50,
            '3': 51,
            '4': 52,
            'c': 67,
            'd': 68,
            'e': 69,
            'f': 70,
            'i': 73,
            'j': 74,
            'k': 75,
            'n':78,
            'o': 79,
            'p': 80,
            'q': 81,
            'r': 82,
            's': 83,
            't': 84,
            'u': 85,
            'w': 87,
            'x': 88,
            'y': 89,
            'z': 90,
            'pad1': 97,
            'pad2': 98,
            'pad3': 99,
            'pad4': 100,
            '\\': 220,
        },
        trigger_allowKeys: ['x', 'c', 'z', 'arrowright', 'arrowleft', 'arrowup', 'arrowdown'],
        /* 播放器事件響應器 */
        playerTrigger: function (player, event) {
            if (!player || !event) return
            let t = h5Player
            let keyCode = event.keyCode

            if(event.code=="Space"&&keyCode>128) keyCode=32;


            if (event.shiftKey && !event.ctrlKey && !event.altKey) {

                let key = event.key.toLowerCase()
                // 網頁FULLSCREEN
                if (key === 'enter') {
                    t.callFullScreenBtn()
                }

                // 進入或退出畫中畫模式
                else if (key === 'p') {
                    if (window._isPictureInPicture_) {
                        document.exitPictureInPicture().then(() => {
                            window._isPictureInPicture_ = null
                        }).catch(() => {
                            window._isPictureInPicture_ = null
                        })
                    } else {
                        player.requestPictureInPicture && player.requestPictureInPicture().then(() => {
                            window._isPictureInPicture_ = true
                        }).catch(() => {
                            window._isPictureInPicture_ = null
                        })
                    }
                }

                // 視頻畫面縮放相關事件
                else if (t.trigger_allowKeys.includes(key)){

                    t.scale = Number(t.scale)
                    switch (key) {
                            // shift+X:視頻縮小 -0.1
                        case 'x' :
                            t.scale -= 0.1
                            break
                            // shift+C:視頻放大 +0.1
                        case 'c' :
                            t.scale += 0.1
                            break
                            // shift+Z:視頻恢復正常大小
                        case 'z' :
                            t.scale = 1
                            t.translate = { x: 0, y: 0 }
                            break
                        case 'arrowright' :
                            t.translate.x += 10
                            break
                        case 'arrowleft' :
                            t.translate.x -= 10
                            break
                        case 'arrowup' :
                            t.translate.y -= 10
                            break
                        case 'arrowdown' :
                            t.translate.y += 10
                            break
                    }

                    let scale = t.scale = Number(t.scale).toFixed(1)
                    player.style.transform = `scale(${scale}) translate(${t.translate.x}px, ${t.translate.y}px)`
                    let tipsMsg = `視頻縮放率:${scale * 100}%`
                    if (t.translate.x) {
                        tipsMsg += `,水平位移:${t.translate.x}px`
                    }
                    if (t.translate.y) {
                        tipsMsg += `,垂直位移:${t.translate.y}px`
                    }
                    t.tips(tipsMsg)

                    // 阻止事件冒泡
                    event.stopPropagation()
                    event.preventDefault()
                    return true
                }
            }

            // 防止其它無關組合鍵衝突
            if (!event.altKey&&!event.ctrlKey&&!event.shiftKey){


                var kControl = null
                console.log('keycode',keyCode)

                switch(keyCode){


                        // 方向鍵右→:快進3秒
                    case 39:
                        t.setCurrentTime(t.skipStep)
                        break;

                        // 方向鍵左←:後退3秒
                    case 37:
                        t.setCurrentTime(-t.skipStep)
                        break;

                        // 方向鍵上↑:音量升高 1%
                    case 38:
                        t.setVolume(0.01)
                        break;
                        // 方向鍵下↓:音量降低 1%
                    case 40:
                        t.setVolume(-0.01)
                        break;

                        // 空格鍵:暫停/播放
                    case h5Player.keyMap.space:
                        t.switchPlayStatus()
                        break;

                        // 按鍵X:減速播放 -0.1
                    case h5Player.keyMap.x:
                        if (player.playbackRate > 0) {
                            t.setPlaybackRate(player.playbackRate - 0.1)
                        }
                        break;
                        // 按鍵C:加速播放 +0.1
                    case h5Player.keyMap.c:
                        if (player.playbackRate < 16) {
                            t.setPlaybackRate(player.playbackRate + 0.1)
                        }
                        break;
                        // 按鍵Z:正常速度播放
                    case h5Player.keyMap.z:
                        player.playbackRate = 1
                        t.setPlaybackRate(player.playbackRate)
                        break;



                        // 按鍵F:下一幀
                    case h5Player.keyMap.f:
                        if (window.location.hostname === 'www.netflix.com') return /* netflix 的F鍵是FULLSCREEN的意思 */
                        if (!player.paused) player.pause()
                        player.currentTime += Number(1 / t.fps)
                        t.tips('Jump to: Next frame')
                        break;
                        // 按鍵D:上一幀
                    case h5Player.keyMap.d:
                        if (!player.paused) player.pause()
                        player.currentTime -= Number(1 / t.fps)
                        t.tips('Jump to: Previous frame')
                        break;
                        // 按鍵E:亮度增加%
                    case h5Player.keyMap.e:
                        kControl='brightness'
                        t.filter.key[kControl] += 0.1
                        t.filter.key[kControl] = t.filter.key[kControl].toFixed(2)
                        t.filter.setup()
                        t.tips('Brightness: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;
                        // 按鍵W:亮度減少%
                    case h5Player.keyMap.w:
                        kControl='brightness'
                        if (t.filter.key[kControl] > 0) {
                            t.filter.key[kControl] -= 0.1
                            t.filter.key[kControl] = t.filter.key[kControl].toFixed(2)
                            t.filter.setup()
                        }
                        t.tips('Brightness: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;

                        // 按鍵T:對比度增加%
                    case h5Player.keyMap.t:
                        kControl='contrast'
                        t.filter.key[kControl] += 0.1
                        t.filter.key[kControl] = t.filter.key[kControl].toFixed(2)
                        t.filter.setup()
                        t.tips('Contrast: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;
                        // 按鍵R:對比度減少%
                    case h5Player.keyMap.r:
                        kControl='contrast'
                        if (t.filter.key[kControl] > 0) {
                            t.filter.key[kControl] -= 0.1
                            t.filter.key[kControl] = t.filter.key[1].toFixed(2)
                            t.filter.setup()
                        }
                        t.tips('Contrast: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;

                        // 按鍵U:飽和度增加%
                    case h5Player.keyMap.u:
                        kControl='saturate'
                        t.filter.key[kControl] += 0.1
                        t.filter.key[kControl] = t.filter.key[kControl].toFixed(2)
                        t.filter.setup()
                        t.tips('Saturate: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;
                        // 按鍵Y:飽和度減少%
                    case h5Player.keyMap.y:
                        kControl='saturate'
                        if (t.filter.key[kControl] > 0) {
                            t.filter.key[kControl] -= 0.1
                            t.filter.key[kControl] = t.filter.key[kControl].toFixed(2)
                            t.filter.setup()
                        }
                        t.tips('Saturate: ' + parseInt(t.filter.key[kControl] * 100) + '%')
                        break;

                        // 按鍵O:色相增加 1 度
                    case h5Player.keyMap.o:
                        kControl='hue-rotate'
                        t.filter.key['hue-rotate'] += 1
                        t.filter.setup()
                        t.tips('Hue: ' + t.filter.key[kControl] + ' deg')
                        break;
                        // 按鍵I:色相減少 1 度
                    case h5Player.keyMap.i:
                        kControl='hue-rotate'
                        t.filter.key['hue-rotate'] -= 1
                        t.filter.setup()
                        t.tips('Hue: ' + t.filter.key[kControl] + ' deg')
                        break;

                        // 按鍵K:模糊增加 0.1 px
                    case h5Player.keyMap.k:
                        kControl='blur'
                        t.filter.key[kControl] += 0.1
                        t.filter.key[kControl] = (+t.filter.key[kControl]||0).toFixed(1)
                        t.filter.setup()
                        t.tips('Blur: ' + t.filter.key[kControl] + ' px')
                        break;
                        // 按鍵J:模糊減少 0.1 px
                    case h5Player.keyMap.j:
                        kControl='blur'
                        if (t.filter.key[kControl] > 0) {
                            t.filter.key[kControl] -= 0.1
                            t.filter.key[kControl] = (+t.filter.key[kControl]||0).toFixed(1)
                            t.filter.setup()
                        }
                        t.tips('Blur: ' + t.filter.key[kControl] + ' px')
                        break;

                        // 按鍵Q:圖像復位
                    case h5Player.keyMap.q:
                        t.filter.reset()
                        t.tips('Video Filter Reset')
                        break;

                        // 按鍵S:畫面旋轉 90 度
                    case h5Player.keyMap.s:
                        t.rotate += 90
                        if (t.rotate % 360 === 0) t.rotate = 0;
                        player.style.transform = 'rotate(' + t.rotate + 'deg)'
                        t.tips('Rotation:' + t.rotate + ' deg')
                        break;

                        // 按鍵迴車,進入FULLSCREEN
                    case h5Player.keyMap.enter:
                        t.callFullScreenBtn();


                        break;

                    case h5Player.keyMap.n:

                        if(gv001.player&&gv001.player.contains(player)){}else{
                            gv001.player = player;
                            ggx.setButton.init();
                        }
                        ggx.pictureInPicture();

                        break;

                    default:

                        // 按1-4設置播放速度 49-52;97-100
                        if ((keyCode >= 49 && keyCode <= 52) || (keyCode >= 97 && keyCode <= 100)) {
                            player.playbackRate = Number(event.key)
                            t.setPlaybackRate(player.playbackRate)

                        }
                }

                // 阻止事件冒泡
                event.stopPropagation()
                event.preventDefault()
                return true
            }
        },

        /* 運行自定義的快捷鍵操作,如果運行了會返回true */
        runCustomShortcuts: function (player, event) {
            if (!player || !event) return
            let key = event.key.toLowerCase()
            let taskConf = TCC.getTaskConfig()
            let confIsCorrect = isObj(taskConf.shortcuts) &&
                Array.isArray(taskConf.shortcuts.register) &&
                taskConf.shortcuts.callback instanceof Function

            /* 判斷當前觸發的快捷鍵是否已被註冊 */
            function isRegister () {
                let list = taskConf.shortcuts.register

                /* 當前觸發的組合鍵 */
                let combineKey = []
                if (event.ctrlKey) {
                    combineKey.push('ctrl')
                }
                if (event.shiftKey) {
                    combineKey.push('shift')
                }
                if (event.altKey) {
                    combineKey.push('alt')
                }
                combineKey.push(key)

                /* 通過循環判斷當前觸發的組合鍵和已註冊的組合鍵是否完全一致 */
                let hasReg = false
                list.forEach((shortcut) => {
                    let regKey = shortcut.split('+')
                    if (combineKey.length === regKey.length) {
                        let allMatch = true
                        regKey.forEach((key) => {
                            if (!combineKey.includes(key)) {
                                allMatch = false
                            }
                        })
                        if (allMatch) {
                            hasReg = true
                        }
                    }
                })

                return hasReg
            }

            if (confIsCorrect && isRegister()) {
                // 執行自定義快捷鍵操作
                TCC.doTask('shortcuts', {
                    event,
                    player,
                    h5Player
                })

                return true
            } else {
                return false
            }
        },

        /* 判斷焦點是否處於可編輯元素 */
        isEditableTarget: function (target) {
            let isEditable = target.getAttribute && target.getAttribute('contenteditable') === 'true'
            let isInputDom = /INPUT|TEXTAREA|SELECT/.test(target.nodeName)
            return isEditable || isInputDom
        },

        /* 按鍵響應方法 */
        keydownEvent: function (event) {
            let t = h5Player
            let keyCode = event.keyCode
            let key = event.key.toLowerCase()
            let player = t.player()

            if(event.code=="Space"&&keyCode>128) keyCode=32;

            /* 處於可編輯元素中不執行任何快捷鍵 */
            if (t.isEditableTarget(event.target)) return

            /* shift+f 切換UA偽裝 */
            if (event.shiftKey && keyCode === 70) {
                t.switchFakeUA()
            }

            /* 未用到的按鍵不進行任何事件監聽 */
            console.log('-keycode-',keyCode)
            let isInUseCode = t.keyCodeList.includes(keyCode) || t.keyList.includes(key)
            if (!isInUseCode) return

            if (!player) {
                // console.log('無可用的播放,不執行相關操作')
                return
            }

            /* 切換插件的可用狀態 */
            if (event.ctrlKey && keyCode === 32) {
                t.enable = !t.enable
                if (t.enable) {
                    t.tips('啟用h5Player插件')
                } else {
                    t.tips('禁用h5Player插件')
                }
            }

            if (!t.enable) {
                console.log('h5Player 已禁用~')
                return false
            }

            // 按ctrl+\ 鍵進入聚焦或取消聚焦狀態,用於視頻標籤被遮擋的場景
            if (event.ctrlKey && keyCode === 220) {
                t.globalMode = !t.globalMode
                if (t.globalMode) {
                    t.tips('全局模式')
                } else {
                    t.tips('禁用全局模式')
                }
            }



            /* 非全局模式下,不聚焦則不執行快捷鍵的操作 */
            // t._isFoucs=(FullScreen._evt_move_target||[]).indexOf(player)>=0
            if (!t.globalMode && !t._isFoucs) return

            /* 判斷是否執行了自定義快捷鍵操作,如果是則不再響應後面默認定義操作 */
            if (t.runCustomShortcuts(player, event) === true) return

            /* 響應播放器相關操作 */
            t.playerTrigger(player, event)
        },

        /**
     * 獲取播放進度
     * @param player -可選 對應的h5 播放器對象, 如果不傳,則獲取到的是整個播放進度表,傳則獲取當前播放器的播放進度
     */
        getPlayProgress: function (player) {
            let progressMap = window.localStorage.getItem('_h5_player_play_progress_')
            if (!progressMap) {
                progressMap = {}
            } else {
                progressMap = JSON.parse(progressMap)
            }
            if (!player) {
                return progressMap
            } else {
                let keyName = window.location.href || player.src
                if (progressMap[keyName]) {
                    return progressMap[keyName].progress
                } else {
                    return player.currentTime
                }
            }
        },
        /* 播放進度記錄器 */
        playProgressRecorder: function (player) {
            let t = h5Player
            clearTimeout(player._playProgressTimer_)
            function recorder (player) {
                player._playProgressTimer_ = setTimeout(function () {
                    let progressMap = t.getPlayProgress()

                    let keyName = window.location.href || player.src
                    let list = Object.keys(progressMap)

                    /* 只保存最近10個視頻的播放進度 */
                    if (list.length > 10) {
                        /* 根據更新的時間戳,取出最早添加播放進度的記錄項 */
                        let timeList = []
                        list.forEach(function (keyName) {
                            progressMap[keyName] && progressMap[keyName].t && timeList.push(progressMap[keyName].t)
                        })
                        timeList = quickSort(timeList)
                        let timestamp = timeList[0]

                        /* 刪除最早添加的記錄項 */
                        list.forEach(function (keyName) {
                            if (progressMap[keyName].t === timestamp) {
                                delete progressMap[keyName]
                            }
                        })
                    }

                    /* 記錄當前播放進度 */
                    progressMap[keyName] = {
                        progress: player.currentTime,
                        t: new Date().getTime()
                    }

                    /* 存儲播放進度表 */
                    window.localStorage.setItem('_h5_player_play_progress_', JSON.stringify(progressMap))

                    /* 循環偵聽 */
                    recorder(player)
                }, 1000 * 2)
            }
            recorder(player)
        },
        /* 設置播放進度 */
        setPlayProgress: function (player, time) {
            if (!player) return
            let t = h5Player
            let curTime = Number(t.getPlayProgress(player))
            if (!curTime || Number.isNaN(curTime)) return

            player.currentTime = curTime || player.currentTime
            if (curTime > 3) {
                t.tips('- Playback Progress is restored -')
            }
        },
        /**
     * 檢測h5播放器是否存在
     * @param callback
     */
        detecH5Player: function () {
            let t = this
            let playerList = t.getPlayerList()

            if (playerList.length) {
                console.log(' - HTML5 Video is detected -')

                /* 單video實例標籤的情況 */
                if (playerList.length === 1) {
                    t.playerInstance = playerList[0]
                    t.initPlayerInstance(true)
                } else {
                    /* 多video實例標籤的情況 */
                    playerList.forEach(function (player) {
                        /* 鼠標移到其上面的時候重新指定實例 */
                        if (player._hasMouseRedirectEvent_) return
                        player.addEventListener('mouseenter', function (event) {
                            t.playerInstance = event.target
                            t.initPlayerInstance(false)
                        })
                        player._hasMouseRedirectEvent_ = true

                        /* 播放器開始播放的時候重新指向實例 */
                        if (player._hasPlayingRedirectEvent_) return
                        player.addEventListener('playing', function (event) {
                            t.playerInstance = event.target
                            t.initPlayerInstance(false)

                            /* 同步之前設定的播放速度 */
                            t.setPlaybackRate()
                        })
                        player._hasPlayingRedirectEvent_ = true
                    })
                }
            }
        },
        /* 綁定相關事件 */
        bindEvent: function () {
            var t = this
            if (t._hasBindEvent_) return

            document.removeEventListener('keydown', t.keydownEvent)
            document.addEventListener('keydown', t.keydownEvent, true)

            /* 相容iframe操作 */
            if (window.top !== window && window.top.document) {
                window.top.document.removeEventListener('keydown', t.keydownEvent)
                window.top.document.addEventListener('keydown', t.keydownEvent, true)
            }
            t._hasBindEvent_ = true
        },

        init: function (global) {
            var t = this
            if (global) {
                /* 綁定鍵盤事件 */
                t.bindEvent()

                /**
         * 判斷是否需要進行ua偽裝
         * 下面方案暫時不可用
         * 由於部分網站跳轉至移動端後域名不一致,形成跨域問題
         * 導致無法同步偽裝配置而不斷死循環跳轉
         * eg. open.163.com
         * */
                // let customUA = window.localStorage.getItem('_h5_player_user_agent_')
                // debugMsg(customUA, window.location.href, window.navigator.userAgent, document.referrer)
                // if (customUA) {
                //   t.setFakeUA(customUA)
                //   alert(customUA)
                // } else {
                //   alert('ua false')
                // }

                /* 對配置了ua偽裝的域名進行偽裝 */
                let host = window.location.host
                if (fakeConfig[host]) {
                    t.setFakeUA(fakeConfig[host])
                }
            } else {
                /* 檢測是否存在H5播放器 */
                t.detecH5Player()
            }
        },
        load: false
    }
    gbl.h5Player=h5Player;

    try {
        /* 初始化全局所需的相關方法 */
        h5Player.init(true)

        function wVideo(){
            h5Player.init()
var pdd=document.createElement('div')
pdd.id='mysvg'

pdd.innerHTML=`
<svg id='image' version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="sharpen1">
<feConvolveMatrix filterRes="100 100" style="color-interpolation-filters:linearrgb" order="3" kernelMatrix="`+
`
-0.3 -0.3 -0.3
-0.3 3.4 -0.3
-0.3 -0.3 -0.3`+
    `"  preserveAlpha="true"/>
</filter>
<filter id="unsharpen1">
<feConvolveMatrix style="color-interpolation-filters:sRGB;color-rendering: optimizeQuality;color-interpolation: sRGB;" order="5" kernelMatrix="`+
`  -0.00391  -0.01563  -0.02344  -0.01563  -0.00391
  -0.01563  -0.06250  -0.09375  -0.06250  -0.01563
  -0.02344  -0.09375   1.85980  -0.09375  -0.02344
  -0.01563  -0.06250  -0.09375  -0.06250  -0.01563
  -0.00391  -0.01563  -0.02344  -0.01563  -0.00391`.replace(/[\n\r]+/g,'  ').trim()+`"  preserveAlpha="true"/>
</filter>
</defs>
</svg>
`



            video=document.querySelector('video');
            video.parentNode.appendChild(pdd);
            if(video){
            video.addEventListener('loadedmetadata', function() {
                console.log('video size', video.videoWidth + ' x '+video.videoHeight );
            }, true);
            }
        }

        /* 檢測到有視頻標籤就進行初始化 */
        ready('video', function () {


            ggx();
            wVideo();

        })

        /* 檢測shadow dom 下面的video */
        document.addEventListener('addShadowRoot', function (e) {
            let shadowRoot = e.detail.shadowRoot
            ready('video', function (element) {
                wVideo();
            }, shadowRoot)
        })

        window.top._h5PlayerForDebug_ = h5Player
    } catch (e) {
        console.error('h5player:', e)
    }

    // document.addEventListener('visibilitychange', function () {
    //   if (!document.hidden) {
    //     h5Player.initAutoPlay()
    //   }
    // })
})()