// ==UserScript== // @name 视频站启用html5播放器 // @description 三大功能 。启用html5播放器;万能网页全屏;添加快捷键:快进、快退、暂停/播放、音量、下一集、切换(网页)全屏、上下帧、播放速度。支持视频站点:油管、TED、优.土、QQ、B站、芒果TV、新浪、微博、网易[娱乐、云课堂、新闻]、搜狐、乐视、风行、百度云视频等;直播:斗鱼、熊猫、YY、虎牙、龙珠、战旗。可增加自定义站点 // @version 1.2.9 // @homepage http://bbs.kafan.cn/thread-2093014-1-1.html // @include *://v.qq.com/* // @include *://v.sports.qq.com/* // @include https://y.qq.com/*/mv/v/* // @include *://film.qq.com/* // @include *://view.inews.qq.com/* // @include *://news.qq.com/* // @include https://www.weiyun.com/video_* // @include *://www.youku.com/ // @include *://v.youku.com/v_show/id_* // @include *://vku.youku.com/live/* // @include *://*.tudou.com/v/* // @include *://www.bilibili.com/* // include http://v.pptv.com/show/* // @include https://tv.sohu.com/* // @include https://film.sohu.com/album/* // @include https://www.mgtv.com/* // @include *://www.fun.tv/vplay/* // @include *://m.fun.tv/* // @include http://*.mtime.com/* // @include *://www.miaopai.com/* // @include *://www.le.com/ptv/vplay/* // @include *://v.163.com/*.html* // @include *://ent.163.com/*.html* // @include *://news.163.com/*.html* // @include *://news.163.com/special/* // @include *://study.163.com/course/*.htm?courseId=* // @include *://www.icourse163.org/learn/* // @include *://news.sina.com.cn/* // @include *://video.sina.com.cn/* // @include *://video.sina.cn/* // @include *://weibo.com/* // @include *://*.weibo.com/* // @include *://pan.baidu.com/* // @include *://yun.baidu.com/* // @include *://v.yinyuetai.com/video/h5/* // @include *://v.yinyuetai.com/playlist/h5/* // @include *://www.365yg.com/* // @include *://v.ifeng.com/*video_* // @GM_info // @include https://www.youtube.com/watch?v=* // @include https://www.ted.com/talks/* // @noframes // @include *://www.yy.com/* // @include *://v.huya.com/play/* // @include *://www.huya.com/* // @include https://v.douyu.com/show/* // @include https://www.douyu.com/* // @include https://www.panda.tv/* // @include *://star.longzhu.com/* // @include https://www.zhanqi.tv/* // @grant unsafeWindow // @grant GM_addStyle // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @run-at document-start // @namespace https://greasyfork.org/users/7036 // @downloadURL none // ==/UserScript== 'use strict'; if (!NodeList.prototype[Symbol.iterator]) NodeList.prototype[Symbol.iterator] = HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator]; const isEdge = navigator.userAgent.includes('Edge'), w = isEdge ? window : unsafeWindow, observeOpt = {childList : true, subtree : true}, q = css => document.querySelector(css), delElem = e => e.remove(), $$ = (c, cb = delElem, doc = document) => { if (!c || !c.length) return; if (typeof c === 'string') c = doc.querySelectorAll(c); if (cb) for (let e of c) { if (e && cb(e)===false) break; } return c; }, gmFuncOfCheckMenu = function(name, val) { GM_setValue(name, val); w.location.reload(); }, r1 = (regp, s) => regp.test(s) && RegExp.$1, log = console.log.bind(console,`%c脚本[${GM_info.script.name}]`,'color:#74C;font-size:1.2em'), sleep = ms => new Promise(resolve => { setTimeout(resolve, ms); }), getStyle = (o, s) => { if (o.style[s]) return o.style[s]; if (getComputedStyle) {//DOM var x = getComputedStyle(o, ''); //s = s.replace(/([A-Z])/g,'-$1').toLowerCase(); return x && x.getPropertyValue(s); } }, throttle = function(fn, delay = 100){ //函数节流 let timer = null, me = this; return function(...args) { timer && clearTimeout(timer); timer = setTimeout(() => { fn.apply(me, args); timer = null; }, delay); }; }, doClick = e => { if (typeof e === 'string') e = q(e); if (e) { e.click ? e.click() : e.dispatchEvent(new MouseEvent('click')) }; }, firefoxVer = r1(/Firefox\/(\d+)/, navigator.userAgent), fakeUA = ua => Object.defineProperty(navigator, 'userAgent', { value: ua, writable: false, configurable: false, enumerable: true }), getMainDomain = host => { let a = host.split('.'), i = a.length -2; if (['com','tv','net','org','gov','edu'].includes(a[i])) i--; return a[i]; }, ua_chrome = 'Mozilla/5.0 (Windows NT 10.0; WOW64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.9', ua_ipad2 = 'Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3'; class FullScreen { constructor(video) { this._video = video; const d = document; this._exitFn = d.exitFullscreen || d.webkitExitFullscreen || d.mozCancelFullScreen || d.msExitFullscreen; const e = video; this._enterFn = e.requestFullscreen || e.webkitRequestFullScreen || e.mozRequestFullScreen || e.msRequestFullScreen; } static isFull() { const d = document; return !!(d.fullscreen || d.webkitIsFullScreen || d.mozFullScreen || d.fullscreenElement || d.webkitFullscreenElement || d.mozFullScreenElement); } enter() { this._enterFn && this._enterFn.call(this._video); } exit() { this._exitFn && this._exitFn.call(document); } toggle() { FullScreen.isFull() ? this.exit() : this.enter(); } } //万能网页全屏,代码参考了:https://github.com/gooyie/ykh5p class FullPage { constructor(video, isFixView, onSwitch) { this._video = video; this._isFixView = isFixView; this._isFull = !1; this._onSwitch = onSwitch; this._rects = new Map(); this._checkContainer(); GM_addStyle(` .z-top { position: relative !important; z-index: 23333333 !important; } .webfullscreen { display: block !important; position: fixed !important; width: 100% !important; height: 100% !important; top: 0 !important; left: 0 !important; background: #000 !important; z-index: 23333333 !important; } `); } getPlayerContainer(video) { let d = document.body, e = video, p = e.parentNode; if (p === d) return e; const wid = p.clientWidth, h = p.clientHeight; do { this._rects.set(e, { width: e.clientWidth + 'px', height: e.clientHeight + 'px' }); e = p; p = e.parentNode; } while (p !== d && p.clientWidth - wid < 5 && p.clientHeight - h < 5); this._rects.delete(e); return e; } _checkContainer() { const e = this._video; if (!this._container || this._container === e) this._container = this.getPlayerContainer(e); } get container() { this._checkContainer(); return this._container; } fixView() { if (!this._isFixView && !this._isFull) return; if (this._video === this._container) return; for(let [e, v] of this._rects) if (this._isFull) e.style.width = e.style.height = '100%'; else { e.style.width = v.width; e.style.height = v.height; } } static isFull(video) { return window.innerWidth -video.clientWidth < 5 && window.innerHeight - video.clientHeight < 5; } toggle() { const cb = this._onSwitch; if (!this._isFull && cb) cb(true); const d = document.body; d.style.overflow = this._isFull ? '' : 'hidden'; this.container.classList.toggle('webfullscreen'); let p = this.container.parentNode; while (p !== d) { p.classList.toggle('z-top'); p = p.parentNode; } this._isFull = !this._isFull; setTimeout(this.fixView.bind(this), 9); if (!this._isFull && cb) setTimeout(cb, 199, !1); } } let v, _fp, _fs; const { host, pathname: path } = location, u = getMainDomain(host),//主域名 //容器,登记事件处理方法中的回调 events = { on(name, fn) { this[name] = fn; } }, app = { isLive: !1, disableSpace: !1, multipleV: !1, //单页面多视频 isFixFPView: !1, //退出网页全屏时是否修正DOM视图 getVideos() { if (v.offsetWidth>1) return; for (let e of this.vList) if (e.offsetWidth>1) { e.playbackRate = v.playbackRate; e.volume = v.volume; v = e; break; } }, _convertView(btn) { (!btn.nextSibling || btn.clientWidth >1 || getStyle(btn, 'display') !== 'none') ? doClick(btn) : doClick(btn.nextSibling); }, hotKey(e) { //判断ctrl,alt,shift三键状态,防止浏览器快捷键被占用 if (e.ctrlKey || e.altKey || e.target.contentEditable=='true' || /INPUT|TEXTAREA|SELECT/.test(e.target.nodeName)) return; if (e.shiftKey && ![13,37,39].includes(e.keyCode)) return; if (this.isLive && [37,39,78,88,67,90].includes(e.keyCode)) return; this.getVideos(); this.checkUI(); if (events.keydown && events.keydown(e)) return; let n; switch (e.keyCode) { case 32: //space if (this.disableSpace) return; if (this.btnPlay) this.play(); else v.paused ? v.play() : v.pause(); e.preventDefault(); break; case 37: n = e.shiftKey ? -27 : -5; //left 快退5秒,shift加速 case 39: //right n = n || (e.shiftKey ? 27 : 5); //快进5秒,shift加速 v.currentTime += n; break; //case 80: // P 上一首 case 78: // N 下一首 this.btnNext && getStyle(this.btnNext, 'display') !== 'none' && doClick(this.btnNext); break; case 38: n = 0.1; //加音量 case 40: //降音量 n = n || -0.1; n += v.volume; if (0 <= n && n <= 1) v.volume = n; e.preventDefault(); e.stopPropagation(); break; case 13: //回车键。 全屏 if (e.shiftKey) { _fp ? _fp.toggle() : this.fullPage(); } else { _fs ? _fs.toggle() : this.fullScreen(); } break; case 27: //esc if (FullScreen.isFull()) { _fs ? _fs.exit() : this.fullScreen(); } else if (FullPage.isFull(v)) { _fp ? _fp.toggle() : this.fullPage(); } break; case 67: n = 0.1; //按键C:加速播放 +0.1 case 88: //按键X:减速播放 -0.1 n = n || -0.1; n += v.playbackRate; if (0 < n && n <= 16) v.playbackRate = n; break; case 90: //按键Z:正常速度播放 v.playbackRate = 1; break; case 70: n = 0.03; //按键F:下一帧 case 68: //按键D:上一帧 n = n || -0.03; if (!v.paused) v.pause(); v.currentTime += n; } }, checkUI() { if (this.webfullCSS && !this.btnWFS) this.btnWFS = q(this.webfullCSS); if (this.btnWFS) { this.fullPage = () => this._convertView(this.btnWFS); if (_fp) _fp = null; } else { _fp = _fp || new FullPage(v, this.isFixFPView, this.switchFP); } if (this.fullCSS && !this.btnFS) this.btnFS = q(this.fullCSS); if (!this.btnFS) { _fs = _fs || new FullScreen(v); } else { this.fullScreen = () => this._convertView(this.btnFS); if (_fs) _fs = null; } if (this.nextCSS && !this.btnNext) this.btnNext = q(this.nextCSS); if (this.playCSS && !this.btnPlay) this.btnPlay = q(this.playCSS); if (this.btnPlay) this.play = () => this._convertView(this.btnPlay); }, switchFP(toFull) { //if (!this.viewObserver) return; if (toFull) { for (let e of this.vSet) this.viewObserver.unobserve(e); } else { for (let e of this.vList) this.viewObserver.observe(e); } }, onGrowVList() { if (this.vList.length > this.vCount) { if (this.viewObserver) { for (let e of this.vList) if (!this.vSet.has(e)) { this.viewObserver.observe(e); } } else { const config = { rootMargin: '0px', threshold: 0.9 }; this.viewObserver = new IntersectionObserver(this.onIntersection.bind(this), config); for (let e of this.vList) this.viewObserver.observe(e); } this.vSet = new Set(this.vList); this.vCount = this.vList.length; } }, onIntersection(entries) { for (let entry of entries) { if (entry.isIntersecting && v != entry.target) {//intersectionRatio v = entry.target; _fs = new FullScreen(v); _fp = new FullPage(v, this.isFixFPView, this.switchFP); events.switchMV && events.switchMV(); break; } } }, bindEvent() { log('bind event\n', v); const fn = ev => { events.canplay && events.canplay(); v.removeEventListener('canplaythrough', fn); }; if (v.readyState > 2) fn(); else v.addEventListener('canplaythrough', fn); document.addEventListener('keydown', this.hotKey.bind(this)); this.checkUI(); events.foundMV && events.foundMV(); if (this.multipleV) new MutationObserver(this.onGrowVList.bind(this)) .observe(document.body, observeOpt); this.vCount = 1; }, findMV() { if (!this.cssMV) return this.vList[0]; for (let e of this.vList) if (e.matches(this.cssMV)) return e; }, init() { this.switchFP = this.multipleV ? this.switchFP.bind(this) : null;//多视频页面 this.vList = document.getElementsByTagName('video'); document.addEventListener('DOMContentLoaded', async e => { events.DOMReady && events.DOMReady(); do { await sleep(190); $$(this.adsCSS); v = this.findMV(); } while (!v); this.bindEvent(); }); } }; let router = { youtube() { app.fullCSS = '.ytp-fullscreen-button'; app.disableSpace = true; app.nextCSS = '.ytp-next-button'; }, ted() { app.fullCSS = 'button[title="Enter Fullscreen"]'; app.playCSS = 'button[title="play video"]'; const forceHD = GM_getValue('ted_forceHD', true); let title = 'TED强制高清'; if (forceHD) title += ' √'; GM_registerMenuCommand(title, gmFuncOfCheckMenu.bind(null, 'ted_forceHD', !forceHD)); const getHDSource = async () => { const pn = r1(/^(\/talks\/\w+)/, location.pathname); const resp = await fetch(`https://www.ted.com${pn}/metadata.json`); const data = await resp.json(); return data.talks[0].downloads.nativeDownloads.high; }, check = async (rs) => { if (!v.src || v.src.startsWith('http')) return; $$(app.vList, e => { e.removeAttribute('src') }); // 取消多余的媒体资源请求 try { v.src = await getHDSource(); } catch(ex) { console.error(ex); } }; events.on('foundMV',() => { if (forceHD) new MutationObserver(check).observe(v, { attributes: true, attributeFilter: ['src'] }); check(); }); }, qq() { Object.assign(app, { disableSpace: true, nextCSS: '.txp_btn_next', webfullCSS: '.txp_btn_fake', fullCSS: '.txp_btn_fullscreen', }); fakeUA('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:48.0) Gecko/20100101 Firefox/48.0'); const isVip = GM_getValue('qqVip', !1); let title = '腾讯VIP用户'; if (isVip) title += ' √'; else { const fn = () => localStorage.clear(); events.on('DOMReady', fn); const ps = history.pushState; history.pushState = function() { fn(); ps.apply(this, arguments); }; w.addEventListener('popstate', fn); document.addEventListener('visibilitychange', ev => !document.hidden && fn()); } GM_registerMenuCommand(title, gmFuncOfCheckMenu.bind(null, 'qqVip', !isVip)); }, youku() { if (host.startsWith('vku.')) { events.on('canplay', () => { app.isLive = !q('.spv_progress'); }); app.fullCSS = '.live_icon_full'; } else { events.on('foundMV', () => { if (!app.btnFS) {//使用了优酷播放器YAPfY扩展 app.webfullCSS = '.ABP-Web-FullScreen'; app.fullCSS = '.ABP-FullScreen'; } else { w.$('.settings-item.disable').replaceWith('
1080p
'); // $$('.youku-layer-logo');//去水印。 破解1080P } }); app.fullCSS = '.control-fullscreen-icon'; app.nextCSS = '.control-next-video'; } }, bilibili() { const autoPlay = GM_getValue('bili_autoPlay', true); let title = '自动播放'; if (autoPlay) title += ' √'; GM_registerMenuCommand(title, gmFuncOfCheckMenu.bind(null, 'bili_autoPlay', !autoPlay)); const danmu = GM_getValue('bili_danmu', true); title = '弹幕'; if (danmu) title += ' √'; GM_registerMenuCommand(title, gmFuncOfCheckMenu.bind(null, 'bili_danmu', !danmu)); let newPlayer, x = localStorage.bilibili_player_settings; if (x) { x = JSON.parse(x); x.video_status.highquality = true; x.video_status.iswidescreen = true; localStorage.bilibili_player_settings = JSON.stringify(x); } else //defquality 选择清晰度,720P:64 1080P:80 localStorage.bilibili_player_settings = `{"setting_config":{"type":"div","opacity":"1.00","fontfamily":"SimHei, 'Microsoft JhengHei'","fontfamilycustom":"","bold":false,"preventshade":false,"fontborder":0,"speedplus":"1.0","speedsync":false,"fontsize":"1.0","fullscreensync":false,"danmakunumber":50,"fullscreensend":false,"defquality":"80","sameaspanel":false},"video_status":{"autopart":1,"highquality":true,"widescreensave":true,"iswidescreen":true,"videomirror":false,"videospeed":1,"volume":1},"block":{"status":true,"type_scroll":true,"type_top":true,"type_bottom":true,"type_reverse":true,"type_guest":true,"type_color":true,"function_normal":true,"function_subtitle":true,"function_special":true,"cloud_level":2,"cloud_source_video":true,"cloud_source_partition":true,"cloud_source_all":true,"size":0,"regexp":false,"list":[]},"message":{"system":false,"bangumi":false,"news":false}}`; app.nextCSS = '.bilibili-player-video-btn-next'; app.playCSS = 'button[title="play video"]'; events.on('foundMV', () => { newPlayer = !!q('#entryOld'); app.webfullCSS = newPlayer ? '.bilibili-player-video-web-fullscreen': 'i[name="web_fullscreen"]'; app.fullCSS = newPlayer ? '.bilibili-player-video-btn-fullscreen' : 'i[name="browser_fullscreen"]'; }); const _setPlayer = () => { v = q('#bofqi video[src]'); if (!v) { setTimeout(_setPlayer, 300); return; } app.btnNext = app.btnWFS = app.btnFS = null; doClick('i.bilibili-player-iconfont-repeat.icon-24repeaton'); //关循环播放 const css = newPlayer ? '.bilibili-player-video-danmaku-switch input' : 'i[name=ctlbar_danmuku_close]'; !danmu && setTimeout(doClick, 90, css);//关弹幕 if (autoPlay) { if (v.readyState === 4) v.play(); else v.oncanplaythrough = ev => { v.play() }; } !newPlayer && v.scrollIntoView(); }; const fn = history.pushState; history.pushState = function() { fn.apply(this, arguments); setTimeout(_setPlayer, 500); }; const doLater = () => { setTimeout(_setPlayer, 500) }; w.addEventListener('popstate', doLater); events.on('canplay', doLater); }, sina() { fakeUA(ua_ipad2); }, weibo() { app.isFixFPView = true; app.multipleV = path.startsWith('/u/'); }, miaopai() { app.multipleV = path.startsWith('/u/'); }, baidu() { if (path.startsWith('/play/')) events.on('keydown', e => { let n, p = w.videojs.getPlayers("video-player"); switch (e.keyCode) { case 67: n = 0.1; //按键C:加速播放 +0.1 case 88: //按键X:减速播放 -0.1 n = n || -0.1; n += p.html5player.tech_.playbackRate(); if (0 < n && n <= 16) p.html5player.tech_.setPlaybackRate(n); return true; case 90: //按键Z:正常速度播放 p.html5player.tech_.setPlaybackRate(1); return true; default: return !1; } }); }, mgtv() { app.disableSpace = true; app.nextCSS = 'mango-control-playnext-btn'; app.webfullCSS = 'mango-webscreen'; app.fullCSS = 'mango-screen'; }, le() { fakeUA('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.75.14 Version/7.0.3 Safari/7046A194A'); app.nextCSS = 'div.hv_ico_next'; app.webfullCSS = 'span.hv_ico_webfullscreen'; app.fullCSS = 'span.hv_ico_screen'; }, sohu() { if (!path.endsWith('html')) return true; app.nextCSS = 'li.on[data-vid]+li a'; app.fullCSS = '.x-fullscreen-btn';//x-fs-btn }, fun() { if (host.startsWith('m.')) { if (!path.includes('play')) return true;//非播放页,不执行init() /^\/[mv]/.test(path) && location.assign(path.replace('/', '/i') + location.search); app.nextCSS = 'a.btn.next-btn'; app.fullCSS = 'a.btn.full-btn'; GM_addStyle('div.p-ip-wrap{overflow-y: auto !important;}'); return; } let vid = r1(/\bv-(\d+)/, path); let mid = r1(/\bg-(\d+)/, path); //剧集path: /implay/,单视频path: /ivplay/ if (vid) { mid && location.assign(`//m.fun.tv/implay/?mid=${mid}&vid=${vid}`); location.assign('//m.fun.tv/ivplay/?vid='+vid); } mid && setTimeout(() => { vid = w.vplay.videoid; vid && location.assign(`//m.fun.tv/implay/?mid=${mid}&vid=${vid}`); }, 99); return true; } }; router.mtime = router.pptv = router.sina; if (!router[u]) { //直播站点 router = { douyu() { if (isEdge) fakeUA(ua_chrome); let n = 0, inRoom = /^\/(t\/)?\w+$/.test(path), //w.$ROOM?.room_id cleanAds = () => { $$(app.adsCSS); $$('i.sign-spec', e=>e.parentNode.remove()); v = app.findMV(); inRoom && v && $$(v.parentNode.parentNode.children, e => { !e.hidden && e.matches('div:not([class]):not([style])') && (e.hidden = true); }); }, swapH5 = () => { const p = w.__player; if (p && !v && !p.isSwitched) { p.switchPlayer('h5'); p.isSwitched = true; } }, timer = setInterval(() => { if (document.readyState=="interactive" || n > 9) { cleanAds(); swapH5(); } if (document.readyState=="complete" && n > 30) clearInterval(timer); n++; }, 300); app.cssMV = '[src^=blob]'; app.webfullCSS = inRoom ? 'div[title="网页全屏"]' : 'input[title="进入网页全屏"]'; app.fullCSS = inRoom ? 'div[title="窗口全屏"]' : 'input[title="进入全屏"]'; app.adsCSS = '.layout-Player~*,[data-dysign],a[href*="wan.douyu.com"]'; }, panda() { if (isEdge) fakeUA(ua_chrome); app.webfullCSS = '.h5player-control-bar-fullscreen'; app.fullCSS = '.h5player-control-bar-allfullscreen'; app.adsCSS = '.act-zhuxianmarch-container, #liveos-container, .ad-container, .room-banner-images'; setTimeout($$, 1900, app.adsCSS); }, yy() { if (!w.chrome) fakeUA(ua_chrome); app.fullCSS = '.liveplayerToolBar-fullScreenBtn'; app.webfullCSS = '.liveplayerToolbar-cinema'; }, huya() { if (firefoxVer && firefoxVer < 57) return true; if (!w.chrome) fakeUA(ua_chrome); app.webfullCSS = '.player-fullpage-btn'; app.fullCSS = '.player-fullscreen-btn'; app.playCSS = '#player-btn'; app.adsCSS = '#player-subscribe-wap,#wrap-income';// 清爽界面,.room-footer,#J_spbg,.room-core-r,.room-hd-r events.on('canplay', function() { setTimeout($$, 900, app.adsCSS); if (!w.TT_ROOM_DATA) return; const $ = w.$; const onBitrate = function(e) { const li = $(this); if (li.hasClass('on')) return; const rate = li.attr("ibitrate"); w.vplayer.vcore.reqBitRate(rate, w.TT_ROOM_DATA.channel); li.siblings('.on').removeClass('on'); li.addClass('on').parent().parent().hide(); $('.player-videotype-cur').text(li.text()); }; const mo = new MutationObserver(rs => { const el = q('#player-login-tip-wrap'); if (!el) return; mo.disconnect(); el.remove(); $(".player-videotype-list li").unbind('click').click(onBitrate); }); mo.observe(v.closest('#player-wrap'), {childList : true}); }); }, longzhu() { if (!w.chrome) fakeUA(ua_chrome); app.fullCSS = 'a.ya-screen-btn'; }, zhanqi() { if (isEdge) fakeUA(ua_chrome); localStorage.lastPlayer = 'h5'; app.fullCSS = '.video-fullscreen'; } }; app.isLive = router[u] && !host.startsWith('v.'); } Object.defineProperty(navigator, 'plugins', { get() { return { length: 0 }; } }); if (!router[u] || !router[u]()) app.init();