diff --git a/docs/assets/css/style.css b/docs/assets/css/style.css index 6acef6a99..1eac8c7b1 100644 --- a/docs/assets/css/style.css +++ b/docs/assets/css/style.css @@ -189,6 +189,7 @@ img { height: 70%; align-items: center; justify-content: center; + flex-direction: column; border-bottom: 1px solid rgba(255, 255, 255, 0.05); } @@ -199,6 +200,7 @@ img { font-size: 20px; width: 90%; height: 65%; + margin-bottom: 10px; background-color: rgb(0 0 0 / 20%); border: 1px dashed rgba(255, 255, 255, 0.1); } diff --git a/docs/assets/example/danmuku.js b/docs/assets/example/danmuku.js index 21c64a683..48fe4da62 100644 --- a/docs/assets/example/danmuku.js +++ b/docs/assets/example/danmuku.js @@ -1,50 +1,27 @@ -var art = new Artplayer({ - container: '.artplayer-app', - url: '/assets/sample/video.mp4', - autoSize: true, - fullscreen: true, - fullscreenWeb: true, - plugins: [ - artplayerPluginDanmuku({ - danmuku: '/assets/sample/danmuku-v2.xml', - speed: 5, - opacity: 1, - fontSize: 20, - maxlength: 100, - margin: ['10%', 60], - antiOverlap: true, - useWorker: true, - synchronousPlayback: false, - filter: (danmu) => danmu.text.length < 50, - }), - ], - controls: [ - { - position: 'right', - html: '隐藏', - click: function (_, event) { - if (art.plugins.artplayerPluginDanmuku.isHide) { - art.plugins.artplayerPluginDanmuku.show(); - event.target.innerText = '隐藏'; - } else { - art.plugins.artplayerPluginDanmuku.hide(); - event.target.innerText = '显示'; - } - }, - }, - { - position: 'right', - html: '发送', - click: function () { - var text = prompt('请输入弹幕文本', '弹幕测试文本'); - if (!text || !text.trim()) return; - var color = '#' + Math.floor(Math.random() * 0xffffff).toString(16); - art.plugins.artplayerPluginDanmuku.emit({ - text: text, - color: color, - border: true, - }); - }, - }, - ], -}); +var art = new Artplayer( + { + container: '.artplayer-app', + url: '/assets/sample/video.mp4', + autoSize: true, + fullscreen: true, + fullscreenWeb: true, + plugins: [ + artplayerPluginDanmuku({ + danmuku: '/assets/sample/danmuku-v2.xml', + speed: 5, + opacity: 1, + fontSize: 20, + maxlength: 100, + margin: ['10%', 60], + antiOverlap: true, + useWorker: true, + synchronousPlayback: false, + filter: (danmu) => danmu.text.length < 50, + }), + ], + }, + function () { + const danmuku = document.querySelector('.danmuku'); + art.plugins.artplayerPluginDanmuku.mount(danmuku); + }, +); diff --git a/docs/uncompiled/artplayer-plugin-danmuku/index.js b/docs/uncompiled/artplayer-plugin-danmuku/index.js index b6b86074d..2f0515c02 100644 --- a/docs/uncompiled/artplayer-plugin-danmuku/index.js +++ b/docs/uncompiled/artplayer-plugin-danmuku/index.js @@ -147,15 +147,20 @@ var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js"); parcelHelpers.defineInteropFlag(exports); var _danmuku = require("./danmuku"); var _danmukuDefault = parcelHelpers.interopDefault(_danmuku); +var _control = require("./control"); +var _controlDefault = parcelHelpers.interopDefault(_control); function artplayerPluginDanmuku(option) { return (art)=>{ const danmuku = new _danmukuDefault.default(art, option); + const control = new _controlDefault.default(art, danmuku); return { name: 'artplayerPluginDanmuku', emit: danmuku.emit.bind(danmuku), config: danmuku.config.bind(danmuku), hide: danmuku.hide.bind(danmuku), show: danmuku.show.bind(danmuku), + mount: control.mount.bind(control), + unmount: control.unmount.bind(control), get isHide () { return danmuku.isHide; } @@ -165,7 +170,7 @@ function artplayerPluginDanmuku(option) { exports.default = artplayerPluginDanmuku; window['artplayerPluginDanmuku'] = artplayerPluginDanmuku; -},{"./danmuku":"igPca","@parcel/transformer-js/src/esmodule-helpers.js":"6SDkN"}],"igPca":[function(require,module,exports) { +},{"./danmuku":"igPca","./control":"nK2ZQ","@parcel/transformer-js/src/esmodule-helpers.js":"8MjWm"}],"igPca":[function(require,module,exports) { var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js"); parcelHelpers.defineInteropFlag(exports); var _bilibili = require("./bilibili"); @@ -187,17 +192,21 @@ class Danmuku { this.timer = null; this.config(option); if (this.option.useWorker) try { - this.worker = new Worker(require("85d40535eae5f839")); + this.worker = new Worker(require("27c3930ee158179a")); } catch (error) { // } - art.on('video:play', this.start.bind(this)); - art.on('video:playing', this.start.bind(this)); - art.on('video:pause', this.stop.bind(this)); - art.on('video:waiting', this.stop.bind(this)); - art.on('fullscreen', this.reset.bind(this)); - art.on('fullscreenWeb', this.reset.bind(this)); - art.on('destroy', this.stop.bind(this)); + this.start = this.start.bind(this); + this.stop = this.stop.bind(this); + this.reset = this.reset.bind(this); + this.destroy = this.destroy.bind(this); + art.on('video:play', this.start); + art.on('video:playing', this.start); + art.on('video:pause', this.stop); + art.on('video:waiting', this.stop); + art.on('fullscreen', this.reset); + art.on('fullscreenWeb', this.reset); + art.on('destroy', this.destroy); this.load(); } static get option() { @@ -235,23 +244,23 @@ class Danmuku { } get marginTop() { const { clamp } = this.utils; - const { $player } = this.art.template; const value = this.option.margin[0]; - if (typeof value === 'number') return clamp(value, 0, $player.clientHeight); + const { clientHeight } = this.$player; + if (typeof value === 'number') return clamp(value, 0, clientHeight); if (typeof value === 'string' && value.endsWith('%')) { const ratio = parseFloat(value) / 100; - return clamp($player.clientHeight * ratio, 0, $player.clientHeight); + return clamp(clientHeight * ratio, 0, clientHeight); } return Danmuku.option.margin[0]; } get marginBottom() { const { clamp } = this.utils; - const { $player } = this.art.template; const value = this.option.margin[1]; - if (typeof value === 'number') return clamp(value, 0, $player.clientHeight); + const { clientHeight } = this.$player; + if (typeof value === 'number') return clamp(value, 0, clientHeight); if (typeof value === 'string' && value.endsWith('%')) { const ratio = parseFloat(value) / 100; - return clamp($player.clientHeight * ratio, 0, $player.clientHeight); + return clamp(clientHeight * ratio, 0, clientHeight); } return Danmuku.option.margin[1]; } @@ -413,9 +422,9 @@ class Danmuku { reset() { this.queue.forEach((danmu)=>this.makeWait(danmu) ); + return this; } update() { - const { clientWidth } = this.$player; this.timer = window.requestAnimationFrame(async ()=>{ if (this.art.playing && !this.isHide) { this.filter('emit', (danmu)=>{ @@ -425,6 +434,7 @@ class Danmuku { if (danmu.$restTime <= 0) this.makeWait(danmu); }); const readys = this.getReady(); + const { clientWidth , clientHeight } = this.$player; for(let index = 0; index < readys.length; index++){ const danmu = readys[index]; danmu.$ref = this.getRef(); @@ -447,8 +457,8 @@ class Danmuku { target, emits: this.getEmits(), antiOverlap: this.option.antiOverlap, - clientWidth: this.$player.clientWidth, - clientHeight: this.$player.clientHeight, + clientWidth: clientWidth, + clientHeight: clientHeight, marginBottom: this.marginBottom, marginTop: this.marginTop }); @@ -537,10 +547,22 @@ class Danmuku { }); return this; } + destroy() { + this.stop(); + if (this.worker && this.worker.terminate) this.worker.terminate(); + this.art.off('video:play', this.start); + this.art.off('video:playing', this.start); + this.art.off('video:pause', this.stop); + this.art.off('video:waiting', this.stop); + this.art.off('fullscreen', this.reset); + this.art.off('fullscreenWeb', this.reset); + this.art.off('destroy', this.destroy); + this.art.emit('artplayerPluginDanmuku:destroy'); + } } exports.default = Danmuku; -},{"./bilibili":"6a8GK","./getDanmuTop":"eLxSm","85d40535eae5f839":"cDlY2","@parcel/transformer-js/src/esmodule-helpers.js":"6SDkN"}],"6a8GK":[function(require,module,exports) { +},{"./bilibili":"6a8GK","./getDanmuTop":"eLxSm","27c3930ee158179a":"7RMxU","@parcel/transformer-js/src/esmodule-helpers.js":"8MjWm"}],"6a8GK":[function(require,module,exports) { var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js"); parcelHelpers.defineInteropFlag(exports); parcelHelpers.export(exports, "getMode", ()=>getMode @@ -587,7 +609,7 @@ function bilibiliDanmuParseFromUrl(url) { ); } -},{"@parcel/transformer-js/src/esmodule-helpers.js":"6SDkN"}],"6SDkN":[function(require,module,exports) { +},{"@parcel/transformer-js/src/esmodule-helpers.js":"8MjWm"}],"8MjWm":[function(require,module,exports) { exports.interopDefault = function(a) { return a && a.__esModule ? a : { default: a @@ -710,9 +732,9231 @@ function getDanmuTop({ target , emits , clientWidth , clientHeight , marginBotto } exports.default = getDanmuTop; -},{"@parcel/transformer-js/src/esmodule-helpers.js":"6SDkN"}],"cDlY2":[function(require,module,exports) { +},{"@parcel/transformer-js/src/esmodule-helpers.js":"8MjWm"}],"7RMxU":[function(require,module,exports) { module.exports = "data:application/javascript,function%20getDanmuTop%28%7B%20target%20%2C%20emits%20%2C%20clientWidth%20%2C%20clientHeight%20%2C%20marginBottom%20%2C%20marginTop%20%2C%20antiOverlap%20%20%7D%29%20%7B%0A%20%20%20%20const%20danmus%20%3D%20emits.filter%28%28item%29%3D%3Eitem.mode%20%3D%3D%3D%20target.mode%20%26%26%20item.top%20%3C%3D%20clientHeight%20-%20marginBottom%0A%20%20%20%20%29.sort%28%28prev%2C%20next%29%3D%3Eprev.top%20-%20next.top%0A%20%20%20%20%29%3B%0A%20%20%20%20if%20%28danmus.length%20%3D%3D%3D%200%29%20return%20marginTop%3B%0A%20%20%20%20danmus.unshift%28%7B%0A%20%20%20%20%20%20%20%20top%3A%200%2C%0A%20%20%20%20%20%20%20%20left%3A%200%2C%0A%20%20%20%20%20%20%20%20right%3A%200%2C%0A%20%20%20%20%20%20%20%20height%3A%20marginTop%2C%0A%20%20%20%20%20%20%20%20width%3A%20clientWidth%2C%0A%20%20%20%20%20%20%20%20speed%3A%200%2C%0A%20%20%20%20%20%20%20%20distance%3A%20clientWidth%0A%20%20%20%20%7D%29%3B%0A%20%20%20%20danmus.push%28%7B%0A%20%20%20%20%20%20%20%20top%3A%20clientHeight%20-%20marginBottom%2C%0A%20%20%20%20%20%20%20%20left%3A%200%2C%0A%20%20%20%20%20%20%20%20right%3A%200%2C%0A%20%20%20%20%20%20%20%20height%3A%20marginBottom%2C%0A%20%20%20%20%20%20%20%20width%3A%20clientWidth%2C%0A%20%20%20%20%20%20%20%20speed%3A%200%2C%0A%20%20%20%20%20%20%20%20distance%3A%20clientWidth%0A%20%20%20%20%7D%29%3B%0A%20%20%20%20for%28let%20index%20%3D%201%3B%20index%20%3C%20danmus.length%3B%20index%20%2B%3D%201%29%7B%0A%20%20%20%20%20%20%20%20const%20item%20%3D%20danmus%5Bindex%5D%3B%0A%20%20%20%20%20%20%20%20const%20prev%20%3D%20danmus%5Bindex%20-%201%5D%3B%0A%20%20%20%20%20%20%20%20const%20prevBottom%20%3D%20prev.top%20%2B%20prev.height%3B%0A%20%20%20%20%20%20%20%20const%20diff%20%3D%20item.top%20-%20prevBottom%3B%0A%20%20%20%20%20%20%20%20if%20%28diff%20%3E%3D%20target.height%29%20return%20prevBottom%3B%0A%20%20%20%20%7D%0A%20%20%20%20const%20topMap%20%3D%20%5B%5D%3B%0A%20%20%20%20for%28let%20index1%20%3D%201%3B%20index1%20%3C%20danmus.length%20-%201%3B%20index1%20%2B%3D%201%29%7B%0A%20%20%20%20%20%20%20%20const%20item%20%3D%20danmus%5Bindex1%5D%3B%0A%20%20%20%20%20%20%20%20if%20%28topMap.length%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20const%20last%20%3D%20topMap%5BtopMap.length%20-%201%5D%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20%28last%5B0%5D.top%20%3D%3D%3D%20item.top%29%20last.push%28item%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20else%20topMap.push%28%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20item%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%29%3B%0A%20%20%20%20%20%20%20%20%7D%20else%20topMap.push%28%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20item%0A%20%20%20%20%20%20%20%20%5D%29%3B%0A%20%20%20%20%7D%0A%20%20%20%20if%20%28antiOverlap%29%20switch%28target.mode%29%7B%0A%20%20%20%20%20%20%20%20case%200%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20result%20%3D%20topMap.find%28%28list%29%3D%3E%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20list.every%28%28danmu%29%3D%3E%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%28clientWidth%20%3C%20danmu.distance%29%20return%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%28target.speed%20%3C%20danmu.speed%29%20return%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20overlapTime%20%3D%20danmu.right%20%2F%20%28target.speed%20-%20danmu.speed%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20%28overlapTime%20%3E%20danmu.time%29%20return%20true%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20false%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20result%20%26%26%20result%5B0%5D%20%3F%20result%5B0%5D.top%20%3A%20undefined%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20case%201%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20undefined%3B%0A%20%20%20%20%20%20%20%20default%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%7D%0A%20%20%20%20else%20%7B%0A%20%20%20%20%20%20%20%20switch%28target.mode%29%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20case%200%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20topMap.sort%28%28prev%2C%20next%29%3D%3E%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20nextMinRight%20%3D%20Math.min%28...next.map%28%28item%29%3D%3Eitem.right%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20prevMinRight%20%3D%20Math.min%28...prev.map%28%28item%29%3D%3Eitem.right%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20nextMinRight%20%2a%20next.length%20-%20prevMinRight%20%2a%20prev.length%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20case%201%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20topMap.sort%28%28prev%2C%20next%29%3D%3E%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20nextMaxWidth%20%3D%20Math.max%28...next.map%28%28item%29%3D%3Eitem.width%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20const%20prevMaxWidth%20%3D%20Math.max%28...prev.map%28%28item%29%3D%3Eitem.width%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%29%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20prevMaxWidth%20%2a%20prev.length%20-%20nextMaxWidth%20%2a%20next.length%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7D%29%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20default%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%3B%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20topMap%5B0%5D%5B0%5D.top%3B%0A%20%20%20%20%7D%0A%7D%0Aonmessage%20%3D%20%28event%29%3D%3E%7B%0A%20%20%20%20const%20%7B%20data%20%20%7D%20%3D%20event%3B%0A%20%20%20%20const%20top%20%3D%20getDanmuTop%28data%29%3B%0A%20%20%20%20self.postMessage%28%7B%0A%20%20%20%20%20%20%20%20top%2C%0A%20%20%20%20%20%20%20%20id%3A%20data.id%0A%20%20%20%20%7D%29%3B%0A%7D%3B%0A%0A"; -},{}]},["gEVO5"], "gEVO5", "parcelRequire93cf") +},{}],"nK2ZQ":[function(require,module,exports) { +var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js"); +parcelHelpers.defineInteropFlag(exports); +var _vue = require("vue"); +var _appVue = require("./App.vue"); +var _appVueDefault = parcelHelpers.interopDefault(_appVue); +class Control { + constructor(art, danmuku){ + this.art = art; + this.danmuku = danmuku; + this.apps = new Map(); + } + mount($el) { + const that = this; + const app1 = _vue.createApp(_appVueDefault.default); + app1.use({ + install (app) { + app.config.globalProperties.$art = that.art; + app.config.globalProperties.$danmuku = that.danmuku; + } + }); + app1.mount($el); + this.apps.set($el, app1); + return app1; + } + unmount($el) { + const app = this.apps.get($el); + if (app) { + app.unmount(); + this.apps.delete($el); + } + } +} +exports.default = Control; + +},{"vue":"5Gshn","./App.vue":"b6WPi","@parcel/transformer-js/src/esmodule-helpers.js":"8MjWm"}],"5Gshn":[function(require,module,exports) { +var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js"); +parcelHelpers.defineInteropFlag(exports); +parcelHelpers.export(exports, "compile", ()=>compile +); +var _runtimeDom = require("@vue/runtime-dom"); +parcelHelpers.exportAll(_runtimeDom, exports); +function initDev() { + _runtimeDom.initCustomFormatter(); +} +initDev(); +const compile = ()=>{ + _runtimeDom.warn(`Runtime compilation is not supported in this build of Vue.` + ` Configure your bundler to alias "vue" to "vue/dist/vue.esm-bundler.js".`); +}; + +},{"@vue/runtime-dom":"hgqy6","@parcel/transformer-js/src/esmodule-helpers.js":"8MjWm"}],"hgqy6":[function(require,module,exports) { +var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js"); +parcelHelpers.defineInteropFlag(exports); +parcelHelpers.export(exports, "Transition", ()=>Transition +); +parcelHelpers.export(exports, "TransitionGroup", ()=>TransitionGroup +); +parcelHelpers.export(exports, "VueElement", ()=>VueElement +); +parcelHelpers.export(exports, "createApp", ()=>createApp +); +parcelHelpers.export(exports, "createSSRApp", ()=>createSSRApp +); +parcelHelpers.export(exports, "defineCustomElement", ()=>defineCustomElement +); +parcelHelpers.export(exports, "defineSSRCustomElement", ()=>defineSSRCustomElement +); +parcelHelpers.export(exports, "hydrate", ()=>hydrate +); +parcelHelpers.export(exports, "initDirectivesForSSR", ()=>initDirectivesForSSR +); +parcelHelpers.export(exports, "render", ()=>render +); +parcelHelpers.export(exports, "useCssModule", ()=>useCssModule +); +parcelHelpers.export(exports, "useCssVars", ()=>useCssVars +); +parcelHelpers.export(exports, "vModelCheckbox", ()=>vModelCheckbox +); +parcelHelpers.export(exports, "vModelDynamic", ()=>vModelDynamic +); +parcelHelpers.export(exports, "vModelRadio", ()=>vModelRadio +); +parcelHelpers.export(exports, "vModelSelect", ()=>vModelSelect +); +parcelHelpers.export(exports, "vModelText", ()=>vModelText +); +parcelHelpers.export(exports, "vShow", ()=>vShow +); +parcelHelpers.export(exports, "withKeys", ()=>withKeys +); +parcelHelpers.export(exports, "withModifiers", ()=>withModifiers +); +var _runtimeCore = require("@vue/runtime-core"); +var _shared = require("@vue/shared"); +parcelHelpers.exportAll(_runtimeCore, exports); +const svgNS = 'http://www.w3.org/2000/svg'; +const doc = typeof document !== 'undefined' ? document : null; +const templateContainer = doc && /*#__PURE__*/ doc.createElement('template'); +const nodeOps = { + insert: (child, parent, anchor)=>{ + parent.insertBefore(child, anchor || null); + }, + remove: (child)=>{ + const parent = child.parentNode; + if (parent) parent.removeChild(child); + }, + createElement: (tag, isSVG, is, props)=>{ + const el = isSVG ? doc.createElementNS(svgNS, tag) : doc.createElement(tag, is ? { + is + } : undefined); + if (tag === 'select' && props && props.multiple != null) el.setAttribute('multiple', props.multiple); + return el; + }, + createText: (text)=>doc.createTextNode(text) + , + createComment: (text)=>doc.createComment(text) + , + setText: (node, text)=>{ + node.nodeValue = text; + }, + setElementText: (el, text)=>{ + el.textContent = text; + }, + parentNode: (node)=>node.parentNode + , + nextSibling: (node)=>node.nextSibling + , + querySelector: (selector)=>doc.querySelector(selector) + , + setScopeId (el, id) { + el.setAttribute(id, ''); + }, + cloneNode (el) { + const cloned = el.cloneNode(true); + // #3072 + // - in `patchDOMProp`, we store the actual value in the `el._value` property. + // - normally, elements using `:value` bindings will not be hoisted, but if + // the bound value is a constant, e.g. `:value="true"` - they do get + // hoisted. + // - in production, hoisted nodes are cloned when subsequent inserts, but + // cloneNode() does not copy the custom property we attached. + // - This may need to account for other custom DOM properties we attach to + // elements in addition to `_value` in the future. + if (`_value` in el) cloned._value = el._value; + return cloned; + }, + // __UNSAFE__ + // Reason: innerHTML. + // Static content here can only come from compiled templates. + // As long as the user only uses trusted templates, this is safe. + insertStaticContent (content, parent, anchor, isSVG, start, end) { + // before | first ... last | anchor + const before = anchor ? anchor.previousSibling : parent.lastChild; + // #5308 can only take cached path if: + // - has a single root node + // - nextSibling info is still available + if (start && (start === end || start.nextSibling)) // cached + while(true){ + parent.insertBefore(start.cloneNode(true), anchor); + if (start === end || !(start = start.nextSibling)) break; + } + else { + // fresh insert + templateContainer.innerHTML = isSVG ? `${content}` : content; + const template = templateContainer.content; + if (isSVG) { + // remove outer svg wrapper + const wrapper = template.firstChild; + while(wrapper.firstChild)template.appendChild(wrapper.firstChild); + template.removeChild(wrapper); + } + parent.insertBefore(template, anchor); + } + return [ + // first + before ? before.nextSibling : parent.firstChild, + // last + anchor ? anchor.previousSibling : parent.lastChild + ]; + } +}; +// compiler should normalize class + :class bindings on the same element +// into a single binding ['staticClass', dynamic] +function patchClass(el, value, isSVG) { + // directly setting className should be faster than setAttribute in theory + // if this is an element during a transition, take the temporary transition + // classes into account. + const transitionClasses = el._vtc; + if (transitionClasses) value = (value ? [ + value, + ...transitionClasses + ] : [ + ...transitionClasses + ]).join(' '); + if (value == null) el.removeAttribute('class'); + else if (isSVG) el.setAttribute('class', value); + else el.className = value; +} +function patchStyle(el, prev, next) { + const style = el.style; + const isCssString = _shared.isString(next); + if (next && !isCssString) { + for(const key in next)setStyle(style, key, next[key]); + if (prev && !_shared.isString(prev)) { + for(const key in prev)if (next[key] == null) setStyle(style, key, ''); + } + } else { + const currentDisplay = style.display; + if (isCssString) { + if (prev !== next) style.cssText = next; + } else if (prev) el.removeAttribute('style'); + // indicates that the `display` of the element is controlled by `v-show`, + // so we always keep the current `display` value regardless of the `style` + // value, thus handing over control to `v-show`. + if ('_vod' in el) style.display = currentDisplay; + } +} +const importantRE = /\s*!important$/; +function setStyle(style, name, val) { + if (_shared.isArray(val)) val.forEach((v)=>setStyle(style, name, v) + ); + else { + if (val == null) val = ''; + if (name.startsWith('--')) // custom property definition + style.setProperty(name, val); + else { + const prefixed = autoPrefix(style, name); + if (importantRE.test(val)) // !important + style.setProperty(_shared.hyphenate(prefixed), val.replace(importantRE, ''), 'important'); + else style[prefixed] = val; + } + } +} +const prefixes = [ + 'Webkit', + 'Moz', + 'ms' +]; +const prefixCache = {}; +function autoPrefix(style, rawName) { + const cached = prefixCache[rawName]; + if (cached) return cached; + let name = _runtimeCore.camelize(rawName); + if (name !== 'filter' && name in style) return prefixCache[rawName] = name; + name = _shared.capitalize(name); + for(let i = 0; i < prefixes.length; i++){ + const prefixed = prefixes[i] + name; + if (prefixed in style) return prefixCache[rawName] = prefixed; + } + return rawName; +} +const xlinkNS = 'http://www.w3.org/1999/xlink'; +function patchAttr(el, key, value, isSVG, instance) { + if (isSVG && key.startsWith('xlink:')) { + if (value == null) el.removeAttributeNS(xlinkNS, key.slice(6, key.length)); + else el.setAttributeNS(xlinkNS, key, value); + } else { + // note we are only checking boolean attributes that don't have a + // corresponding dom prop of the same name here. + const isBoolean = _shared.isSpecialBooleanAttr(key); + if (value == null || isBoolean && !_shared.includeBooleanAttr(value)) el.removeAttribute(key); + else el.setAttribute(key, isBoolean ? '' : value); + } +} +// __UNSAFE__ +// functions. The user is responsible for using them with only trusted content. +function patchDOMProp(el, key, value, // the following args are passed only due to potential innerHTML/textContent +// overriding existing VNodes, in which case the old tree must be properly +// unmounted. +prevChildren, parentComponent, parentSuspense, unmountChildren) { + if (key === 'innerHTML' || key === 'textContent') { + if (prevChildren) unmountChildren(prevChildren, parentComponent, parentSuspense); + el[key] = value == null ? '' : value; + return; + } + if (key === 'value' && el.tagName !== 'PROGRESS' && // custom elements may use _value internally + !el.tagName.includes('-')) { + // store value as _value as well since + // non-string values will be stringified. + el._value = value; + const newValue = value == null ? '' : value; + if (el.value !== newValue || // #4956: always set for OPTION elements because its value falls back to + // textContent if no value attribute is present. And setting .value for + // OPTION has no side effect + el.tagName === 'OPTION') el.value = newValue; + if (value == null) el.removeAttribute(key); + return; + } + let needRemove = false; + if (value === '' || value == null) { + const type = typeof el[key]; + if (type === 'boolean') // e.g. + try { + el[key] = value; + } catch (e) { + _runtimeCore.warn(`Failed setting prop "${key}" on <${el.tagName.toLowerCase()}>: ` + `value ${value} is invalid.`, e); + } + needRemove && el.removeAttribute(key); +} +// Async edge case fix requires storing an event listener's attach timestamp. +const [_getNow, skipTimestampCheck] = /*#__PURE__*/ (()=>{ + let _getNow1 = Date.now; + let skipTimestampCheck1 = false; + if (typeof window !== 'undefined') { + // Determine what event timestamp the browser is using. Annoyingly, the + // timestamp can either be hi-res (relative to page load) or low-res + // (relative to UNIX epoch), so in order to compare time we have to use the + // same timestamp type when saving the flush timestamp. + if (Date.now() > document.createEvent('Event').timeStamp) // if the low-res timestamp which is bigger than the event timestamp + // (which is evaluated AFTER) it means the event is using a hi-res timestamp, + // and we need to use the hi-res version for event listeners as well. + _getNow1 = ()=>performance.now() + ; + // #3485: Firefox <= 53 has incorrect Event.timeStamp implementation + // and does not fire microtasks in between event propagation, so safe to exclude. + const ffMatch = navigator.userAgent.match(/firefox\/(\d+)/i); + skipTimestampCheck1 = !!(ffMatch && Number(ffMatch[1]) <= 53); + } + return [ + _getNow1, + skipTimestampCheck1 + ]; +})(); +// To avoid the overhead of repeatedly calling performance.now(), we cache +// and use the same timestamp for all event listeners attached in the same tick. +let cachedNow = 0; +const p = /*#__PURE__*/ Promise.resolve(); +const reset = ()=>{ + cachedNow = 0; +}; +const getNow = ()=>cachedNow || (p.then(reset), cachedNow = _getNow()) +; +function addEventListener(el, event, handler, options) { + el.addEventListener(event, handler, options); +} +function removeEventListener(el, event, handler, options) { + el.removeEventListener(event, handler, options); +} +function patchEvent(el, rawName, prevValue, nextValue, instance = null) { + // vei = vue event invokers + const invokers = el._vei || (el._vei = {}); + const existingInvoker = invokers[rawName]; + if (nextValue && existingInvoker) // patch + existingInvoker.value = nextValue; + else { + const [name, options] = parseName(rawName); + if (nextValue) { + // add + const invoker = invokers[rawName] = createInvoker(nextValue, instance); + addEventListener(el, name, invoker, options); + } else if (existingInvoker) { + // remove + removeEventListener(el, name, existingInvoker, options); + invokers[rawName] = undefined; + } + } +} +const optionsModifierRE = /(?:Once|Passive|Capture)$/; +function parseName(name) { + let options; + if (optionsModifierRE.test(name)) { + options = {}; + let m; + while(m = name.match(optionsModifierRE)){ + name = name.slice(0, name.length - m[0].length); + options[m[0].toLowerCase()] = true; + } + } + return [ + _shared.hyphenate(name.slice(2)), + options + ]; +} +function createInvoker(initialValue, instance) { + const invoker = (e)=>{ + // async edge case #6566: inner click event triggers patch, event handler + // attached to outer element during patch, and triggered again. This + // happens because browsers fire microtask ticks between event propagation. + // the solution is simple: we save the timestamp when a handler is attached, + // and the handler would only fire if the event passed to it was fired + // AFTER it was attached. + const timeStamp = e.timeStamp || _getNow(); + if (skipTimestampCheck || timeStamp >= invoker.attached - 1) _runtimeCore.callWithAsyncErrorHandling(patchStopImmediatePropagation(e, invoker.value), instance, 5 /* NATIVE_EVENT_HANDLER */ , [ + e + ]); + }; + invoker.value = initialValue; + invoker.attached = getNow(); + return invoker; +} +function patchStopImmediatePropagation(e1, value) { + if (_shared.isArray(value)) { + const originalStop = e1.stopImmediatePropagation; + e1.stopImmediatePropagation = ()=>{ + originalStop.call(e1); + e1._stopped = true; + }; + return value.map((fn)=>(e)=>!e._stopped && fn && fn(e) + ); + } else return value; +} +const nativeOnRE = /^on[a-z]/; +const patchProp = (el, key, prevValue, nextValue, isSVG = false, prevChildren, parentComponent, parentSuspense, unmountChildren)=>{ + if (key === 'class') patchClass(el, nextValue, isSVG); + else if (key === 'style') patchStyle(el, prevValue, nextValue); + else if (_shared.isOn(key)) // ignore v-model listeners + { + if (!_shared.isModelListener(key)) patchEvent(el, key, prevValue, nextValue, parentComponent); + } else if (key[0] === '.' ? (key = key.slice(1), true) : key[0] === '^' ? (key = key.slice(1), false) : shouldSetAsProp(el, key, nextValue, isSVG)) patchDOMProp(el, key, nextValue, prevChildren, parentComponent, parentSuspense, unmountChildren); + else { + // special case for with + // :true-value & :false-value + // store value as dom properties since non-string values will be + // stringified. + if (key === 'true-value') el._trueValue = nextValue; + else if (key === 'false-value') el._falseValue = nextValue; + patchAttr(el, key, nextValue, isSVG); + } +}; +function shouldSetAsProp(el, key, value, isSVG) { + if (isSVG) { + // most keys must be set as attribute on svg elements to work + // ...except innerHTML & textContent + if (key === 'innerHTML' || key === 'textContent') return true; + // or native onclick with function values + if (key in el && nativeOnRE.test(key) && _shared.isFunction(value)) return true; + return false; + } + // these are enumerated attrs, however their corresponding DOM properties + // are actually booleans - this leads to setting it with a string "false" + // value leading it to be coerced to `true`, so we need to always treat + // them as attributes. + // Note that `contentEditable` doesn't have this problem: its DOM + // property is also enumerated string values. + if (key === 'spellcheck' || key === 'draggable' || key === 'translate') return false; + // #1787, #2840 form property on form elements is readonly and must be set as + // attribute. + if (key === 'form') return false; + // #1526 must be set as attribute + if (key === 'list' && el.tagName === 'INPUT') return false; + // #2766