From f8a73c0b03eec87b4d3c979f577152b9c8c9d466 Mon Sep 17 00:00:00 2001 From: Ilya Kreymer Date: Tue, 31 Aug 2021 21:17:01 -0700 Subject: [PATCH] initial pass on integrating vue calendar + banner from @vanecat - use rollup to build vue ui, build at vue/vueui.js - calendar page renders via both /*/ and /*? queries - banner support for framed mode only for now --- config.yaml | 2 + pywb/static/calendar-icon.png | Bin 0 -> 20175 bytes pywb/static/js/cdxquery.js | 91 + pywb/static/pywb-logo-sm.png | Bin 0 -> 4612 bytes pywb/static/vue/vueui.js | 13608 ++++++++++++++++++ pywb/templates/banner.html | 21 +- pywb/templates/query.html | 33 +- pywb/vueui/.eslintrc.js | 39 + pywb/vueui/package.json | 21 + pywb/vueui/rollup.config.js | 20 + pywb/vueui/src/App.vue | 172 + pywb/vueui/src/components/CalendarMonth.vue | 151 + pywb/vueui/src/components/CalendarYear.vue | 77 + pywb/vueui/src/components/Summary.vue | 76 + pywb/vueui/src/components/Timeline.vue | 379 + pywb/vueui/src/index.js | 23 + pywb/vueui/src/model.js | 360 + pywb/vueui/yarn.lock | 1810 +++ 18 files changed, 16873 insertions(+), 10 deletions(-) create mode 100644 pywb/static/calendar-icon.png create mode 100644 pywb/static/js/cdxquery.js create mode 100644 pywb/static/pywb-logo-sm.png create mode 100644 pywb/static/vue/vueui.js create mode 100644 pywb/vueui/.eslintrc.js create mode 100644 pywb/vueui/package.json create mode 100644 pywb/vueui/rollup.config.js create mode 100644 pywb/vueui/src/App.vue create mode 100644 pywb/vueui/src/components/CalendarMonth.vue create mode 100644 pywb/vueui/src/components/CalendarYear.vue create mode 100644 pywb/vueui/src/components/Summary.vue create mode 100644 pywb/vueui/src/components/Timeline.vue create mode 100644 pywb/vueui/src/index.js create mode 100644 pywb/vueui/src/model.js create mode 100644 pywb/vueui/yarn.lock diff --git a/config.yaml b/config.yaml index a3b638c0..a93dd839 100644 --- a/config.yaml +++ b/config.yaml @@ -8,6 +8,8 @@ collections: index_paths: ./sample_archive/cdx/ archive_paths: ./sample_archive/warcs/ + ukwa: cdx+https://www.webarchive.org.uk/wayback/archive/cdx + # Settings for each collection use_js_obj_proxy: true diff --git a/pywb/static/calendar-icon.png b/pywb/static/calendar-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..de813c40b8b89a496cb8c662ec90e54578bc1fe7 GIT binary patch literal 20175 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 literal 0 HcmV?d00001 diff --git a/pywb/static/js/cdxquery.js b/pywb/static/js/cdxquery.js new file mode 100644 index 00000000..8fa38a7a --- /dev/null +++ b/pywb/static/js/cdxquery.js @@ -0,0 +1,91 @@ + + +// =========================================================================== +class CDXLoader { + constructor(staticPrefix, url, prefix, timestamp) { + this.opts = {}; + this.prefix = prefix; + + this.isReplay = (timestamp !== undefined); + + if (this.isReplay) { + window.WBBanner = new VueBannerWrapper(this); + } + + this.queryWorker = new Worker(staticPrefix + '/queryWorker.js'); + + // query form *?=url... + if (window.location.href.indexOf("*?") > 0) { + this.queryURL = window.location.href.replace("*?", "cdx?") + "&output=json"; + url = new URL(this.queryURL).searchParams.get("url"); + + // otherwise, traditional calendar form /*/ + } else if (url) { + const params = new URLSearchParams(); + params.set("url", url); + params.set("output", "json"); + this.queryURL = prefix + "cdx?" + params.toString(); + + // otherwise, an error since no URL + } else { + throw new Error("No query URL specified"); + } + + this.opts.initialView = {url, timestamp}; + + // TODO: make configurable + this.opts.logoImg = staticPrefix + "/pywb-logo-sm.png"; + + const cdxList = []; + + this.queryWorker.addEventListener("message", (event) => { + const data = event.data; + switch (data.type) { + case "cdxRecord": + cdxList.push(data.record); + break; + + case "finished": + PywbVue.init(cdxList, this.opts, (snapshot) => this.loadSnapshot(snapshot)); + break; + } + }); + } + + init() { + this.queryWorker.postMessage({ + type: 'query', + queryURL: this.queryURL + }); + } + + loadSnapshot(snapshot) { + if (!isReplay) { + window.location.href = this.prefix + snapshot.id + "/" + snapshot.url; + } + } +} + + +// =========================================================================== +class VueBannerWrapper +{ + constructor() { + this.loading = true; + this.lastUrl = null; + } + + init() {} + + stillIndicatesLoading() { + return this.loading; + } + + updateCaptureInfo(url, ts, is_live) { + this.loading = false; + } + + onMessage(event) { + console.log(event); + } +} diff --git a/pywb/static/pywb-logo-sm.png b/pywb/static/pywb-logo-sm.png new file mode 100644 index 0000000000000000000000000000000000000000..cd7e7e5c5c346d529c43a2580ff6a0ffae02f6a6 GIT binary patch literal 4612 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 { + const snap = new PywbSnapshot(rawSnap, i); + let year, month, day, hour, single; + if (!(year = allTimePeriod.getChildById(snap.year))) { + year = new PywbPeriod({type: PywbPeriod.Type.year, id: snap.year}); + allTimePeriod.addChild(year); + } + if (!(month = year.getChildById(snap.month))) { + month = new PywbPeriod({type: PywbPeriod.Type.month, id: snap.month}); + year.addChild(month); + } + if (!(day = month.getChildById(snap.day))) { + day = new PywbPeriod({type: PywbPeriod.Type.day, id: snap.day}); + month.addChild(day); + } + const hourValue = Math.ceil((snap.hour + .0001) / (24/8)); // divide day in 4 six-hour periods (aka quarters) + //const hourValue = snap.hour; + if (!(hour = day.getChildById(hourValue))) { + hour = new PywbPeriod({type: PywbPeriod.Type.hour, id: hourValue}); + day.addChild(hour); + } + if (!(single = hour.getChildById(snap.id))) { + single = new PywbPeriod({type: PywbPeriod.Type.snapshot, id: snap.id}); + hour.addChild(single); + } + single.setSnapshot(snap); + if (lastSingle) { + lastSingle.setNextSnapshotPeriod(single); + single.setPreviousSnapshotPeriod(lastSingle); + } + lastSingle = single; + + snapshots.push(snap); + }); + + this.timeline = allTimePeriod; + this.snapshots = snapshots; + this.getSnapshot = function(index) { + if (index < 0 || index >= this.snapshots.length) { + return null; + } + return this.snapshots[index]; + }; + this.getPreviousSnapshot = function(snapshot) { + const index = snapshot.index; + return this.getSnapshot(index-1); + }; + this.getNextSnapshot = function(snapshot) { + const index = snapshot.index; + return this.getSnapshot(index+1); + }; + } + /* ---------------- SNAP SHOT object ----------------- */ + class PywbSnapshot { + constructor(init, index) { + this.index = index; + this.year = parseInt(init.timestamp.substr(0, 4)); + this.month = parseInt(init.timestamp.substr(4, 2)); + this.day = parseInt(init.timestamp.substr(6, 2)); + this.hour = parseInt(init.timestamp.substr(8, 2)); + this.minute = parseInt(init.timestamp.substr(10, 2)); + this.second = parseInt(init.timestamp.substr(12, 2)); + this.id = parseInt(init.timestamp); + + this.urlkey = init.urlkey; + this.url = init.url; + this.mime = init.mime; + this.status = init.status; + this.digest = init.digest; + this.redirect = init.redirect; + this.robotflags = init.robotflags; + this.length = init.length; + this.offset = init.offset; + this.filename = init.filename; + this.load_url = init.load_url; + this["source-col"] = init["source-col"]; + this.access = init.access; + } + + getTimeDateFormatted() { + return `${this.year}-${PywbMonthLabels[this.month]}-${this.day} ${this.getTimeFormatted()}`; + } + + getDateFormatted() { + return `${this.year}-${PywbMonthLabels[this.month]}-${this.day}`; + } + + getTimeFormatted() { + return (this.hour < 13 ? this.hour : (this.hour % 12)) + ":" + ((this.minute < 10 ? "0":"")+this.minute) + ":" + ((this.second < 10 ? "0":"")+this.second) + " " + (this.hour < 12 ? "am":"pm"); + } + } + + /* ---------------- PERIOD object ----------------- */ + function PywbPeriod(init) { + this.type = init.type; + this.id = init.id; + + this.childrenIds = {}; // allow for query by ID + this.children = []; // allow for sequentiality / order + + this.maxGrandchildSnapshotCount = 0; + this.snapshotCount = 0; + } + PywbPeriod.Type = {all: 0,year: 1,month: 2,day: 3,hour: 4,snapshot:5}; + PywbPeriod.TypeLabel = ["timeline","year","month","day","hour","snapshot"]; + + PywbPeriod.prototype.getTypeLabel = function() { + return PywbPeriod.TypeLabel[this.type]; + }; + PywbPeriod.GetTypeLabel = function(type) { + return PywbPeriod.TypeLabel[type] ? PywbPeriod.TypeLabel[type] : ""; + }; + + PywbPeriod.prototype.getChildById = function(id) { + return this.children[this.childrenIds[id]]; + }; + + // previous period (ONLY SET at the period level/type: snapshot) + PywbPeriod.prototype.getPreviousSnapshotPeriod = () => {}; + PywbPeriod.prototype.setPreviousSnapshotPeriod = function(period) { + this.getPreviousSnapshotPeriod = () => period; + }; + // next period (ONLY SET at the period level/type: snapshot) + PywbPeriod.prototype.getNextSnapshotPeriod = () => {}; + PywbPeriod.prototype.setNextSnapshotPeriod = function(period) { + this.getNextSnapshotPeriod = () => period; + }; + + PywbPeriod.prototype.getFirstSnapshotPeriod = function() { + return this.getFirstLastSnapshotPeriod_("first"); + }; + PywbPeriod.prototype.getLastSnapshotPeriod = function() { + return this.getFirstLastSnapshotPeriod_("last"); + }; + PywbPeriod.prototype.getFirstLastSnapshotPeriod_ = function(direction) { + let period = this; + let iFailSafe = 100; // in case a parser has a bug and the snapshotCount is not correct; avoid infinite-loop + while (period.snapshotCount && period.type !== PywbPeriod.Type.snapshot) { + let i = 0; + for(i=0; i < period.children.length; i++) { + const ii = direction === "first" ? i : (period.children.length - 1 - i); + if (period.children[ii].snapshotCount) { + period = period.children[ii]; + break; + } + } + if (iFailSafe-- < 0) { + break; + } + } + if (period.type === PywbPeriod.Type.snapshot && period.snapshot) { + return period; + } + return null; + }; + + PywbPeriod.prototype.getPrevious = function() { + const firstSnapshotPeriod = this.getFirstSnapshotPeriod(); + if (!firstSnapshotPeriod) { + return null; + } + const previousSnapshotPeriod = firstSnapshotPeriod.getPreviousSnapshotPeriod(); + if (!previousSnapshotPeriod) { + return null; + } + if (this.type === PywbPeriod.Type.snapshot) { + return previousSnapshotPeriod; + } + let parent = previousSnapshotPeriod.parent; + while(parent) { + if (parent.type === this.type) { + break; + } + parent = parent.parent; + } + return parent; + }; + PywbPeriod.prototype.getNext = function() { + const lastSnapshotPeriod = this.getLastSnapshotPeriod(); + if (!lastSnapshotPeriod) { + return null; + } + const nextSnapshotPeriod = lastSnapshotPeriod.getNextSnapshotPeriod(); + if (!nextSnapshotPeriod) { + return null; + } + if (this.type === PywbPeriod.Type.snapshot) { + return nextSnapshotPeriod; + } + let parent = nextSnapshotPeriod.parent; + while(parent) { + if (parent.type === this.type) { + break; + } + parent = parent.parent; + } + return parent; + }; + + PywbPeriod.prototype.parent = null; + PywbPeriod.prototype.addChild = function(period) { + if (this.getChildById(period.id)) { + return false; + } + period.parent = this; + this.childrenIds[period.id] = this.children.length; + this.children.push(period); + return true; + }; + + PywbPeriod.prototype.getChildrenRange = function() { + switch (this.type) { + case PywbPeriod.Type.all: + // year range: first to last year available + return [this.children[0].id, this.children[this.children.length-1].id]; + case PywbPeriod.Type.year: + // month is simple: 1 to 12 + return [1,12]; + case PywbPeriod.Type.month: + // days in month: 1 to last day in month + const y = this.parent.id; const m = this.id; + const lastDateInMonth = (new Date((new Date(y, m, 1)).getTime() - 1000)).getDate(); // 1 sec earlier + return [1, lastDateInMonth]; + case PywbPeriod.Type.day: + // hours: 0 to 23 + // return [1,4]; + return [1,8]; + } + return null; + }; + PywbPeriod.prototype.fillEmptyGrancChildPeriods = function() { + if (this.hasFilledEmptyGrandchildPeriods) { + return; + } + this.children.forEach(c => { + c.fillEmptyChildPeriods(); + }); + this.hasFilledEmptyGrandchildPeriods = true; + }; + + PywbPeriod.prototype.fillEmptyChildPeriods = function(isFillEmptyGrandChildrenPeriods=false) { + if (this.snapshotCount === 0 || this.type > PywbPeriod.Type.day) { + return; + } + + if (isFillEmptyGrandChildrenPeriods) { + this.fillEmptyGrancChildPeriods(); + } + + if (this.hasFilledEmptyChildPeriods) { + return; + } + this.hasFilledEmptyChildPeriods = true; + + const idRange = this.getChildrenRange(); + if (!idRange) { + return; + } + + let i = 0; + for (let newId = idRange[0]; newId <= idRange[1]; newId++) { + if (i < this.children.length) { + // if existing and new id match, skip, item already in place + // else + if (this.children[i].id !== newId) { + const empty = new PywbPeriod({type: this.type + 1, id: newId}); + if (newId < this.children[i].id) { + // insert new before existing + this.children.splice(i, 0, empty); + } else { + // insert new after existing + this.children.splice(i+1, 0, empty); + } + // manually push children (no need to reverse link parent + //empty.parent = this; + } + i++; + } else { + const empty = new PywbPeriod({type: this.type + 1, id: newId}); + this.children.push(empty); + // manually push children (no need to reverse link parent + //empty.parent = this; + } + } + + // re-calculate indexes + for(let i=0;i p.getReadableId(hasDayCardinalSuffix)).join(" ") + " " + this.getReadableId(hasDayCardinalSuffix); + }; + PywbPeriod.prototype.getReadableId = function(hasDayCardinalSuffix) { + switch (this.type) { + case PywbPeriod.Type.all: + return "All-time"; + case PywbPeriod.Type.year: + return this.id; + case PywbPeriod.Type.month: + return PywbMonthLabels[this.id]; + case PywbPeriod.Type.day: + let suffix = ""; + if (hasDayCardinalSuffix) { + const singleDigit = this.id % 10; + const isTens = Math.floor(this.id / 10) === 1; + const suffixes = {1:"st", 2:"nd",3:"rd"}; + suffix = (isTens || !suffixes[singleDigit]) ? "th" : suffixes[singleDigit]; + } + return this.id + suffix; + case PywbPeriod.Type.hour: + return ({1:"12 am", 2: "3 am", 3: "6 am", 4: "9 am", 5: "noon", 6: "3 pm", 7: "6 pm", 8: "9 pm"})[this.id]; + //return ({1:'midnight', 2: '6 am', 3: 'noon', 4: '6 pm'})[this.id]; + //return (this.id < 13 ? this.id : this.id % 12) + ' ' + (this.id < 12 ? 'am':'pm'); + case PywbPeriod.Type.snapshot: + return this.snapshot.getTimeFormatted(); + } + }; + + // + + var script$4 = { + props: ["period", "highlight"], + data: function() { + return { + // TODO: remove widths (now using flex css for width calculation) + subPeriodBoxWidths: {// in pixels + [PywbPeriod.Type.year]: 80, // year box + [PywbPeriod.Type.month]: 80, // month box in year + [PywbPeriod.Type.day]: 20, // days box in month + [PywbPeriod.Type.hour]: 60, // hour box in day + [PywbPeriod.Type.snapshot]: 10 // snapshot box in hour/day + }, + // TODO: remove widths (now using flex css for width calculation) + emptySubPeriodBoxWidths: {// in pixels + [PywbPeriod.Type.year]: 40, // year box + [PywbPeriod.Type.month]: 40, // month box in year + [PywbPeriod.Type.day]: 20, // days box in month + [PywbPeriod.Type.hour]: 40, // hour box in day + [PywbPeriod.Type.snapshot]: 20 // snapshot box in hour/day + }, + highlightPeriod: null, + previousPeriod: null, + nextPeriod: null, + isScrollZero: true, + isScrollMax: true, + }; + }, + created: function() { + this.addEmptySubPeriods(); + }, + mounted: function() { + this.$refs.periods._computedStyle = window.getComputedStyle(this.$refs.periods); + this.$refs.periodScroll._computedStyle = window.getComputedStyle(this.$refs.periodScroll); + this.$watch("period", this.onPeriodChanged); + // TODO: remove widths (now using flex css for width calculation), so we don't need manual calc + //this.adjustScrollElWidth(); + + this.$refs.periodScroll.addEventListener("scroll", this.updateScrollArrows); + window.addEventListener("resize", this.updateScrollArrows); + this.updateScrollArrows(); + }, + computed: { + subPeriodBoxWidth: function() { + return this.subPeriodBoxWidths[this.period.type+1]; // the type of the period children + }, + // TODO: remove widths (now using flex css for width calculation) + emptySubPeriodBoxWidth: function() { + return this.emptySubPeriodBoxWidths[this.period.type+1]; // the type of the period children + }, + // this determins which the last zoom level is before we go straight to showing snapshot + isLastZoomLevel() { + return this.period.type === PywbPeriod.Type.day; + } + }, + updated() { + // do something on update + }, + methods: { + addEmptySubPeriods() { + this.period.fillEmptyChildPeriods(true); + }, + updateScrollArrows() { + this.period.scroll = this.$refs.periodScroll.scrollLeft; + const maxScroll = parseInt(this.$refs.periods._computedStyle.width) - parseInt(this.$refs.periodScroll._computedStyle.width); + this.isScrollZero = !this.period.scroll; // if 0, then true (we are at scroll zero) + this.isScrollMax = Math.abs(maxScroll - this.period.scroll) < 5; + }, + restoreScroll() { + this.$refs.periodScroll.scrollLeft = this.period.scroll; + }, + scrollNext: function () { + if (this.isScrollMax) { + if (this.nextPeriod) { + this.$emit("goto-period", this.nextPeriod); + } + } else { + this.$refs.periodScroll.scrollLeft += 30; + } + }, + scrollPrev: function () { + if (this.isScrollZero) { + if (this.previousPeriod) { + this.$emit("goto-period", this.previousPeriod); + } + } else { + this.$refs.periodScroll.scrollLeft -= 30; + } + }, + getTimeFormatted: function(date) { + return (date.hour < 13 ? date.hour : (date.hour % 12)) + ":" + ((date.minute < 10 ? "0":"")+date.minute) + " " + (date.hour < 12 ? "am":"pm"); + }, + getHistoLineHeight: function(value) { + const percent = Math.ceil((value/this.period.maxGrandchildSnapshotCount) * 100); + return (percent ? (5 + Math.ceil(percent*.95)) : 0) + "%"; + // return percent + '%'; + }, + changePeriod(period) { + if (period.snapshotCount) { + if (this.isLastZoomLevel) { + if (period.type === PywbPeriod.Type.snapshot) { + this.$emit("goto-period", period); + } + } else { + this.$emit("goto-period", period); + } + } + }, + // special "change period" from histogram lines + changePeriodFromHistogram(period) { + if (this.isLastZoomLevel && period.type === PywbPeriod.Type.snapshot) { + this.$emit("goto-period", period); + } + }, + onPeriodChanged(newPeriod, oldPeriod) { + this.addEmptySubPeriods(); + this.previousPeriod = this.period.getPrevious(); + this.nextPeriod = this.period.getNext(); + + // detect if going up level of period (new period type should be in old period parents) + if (oldPeriod.type - newPeriod.type > 0) { + let highlightPeriod = oldPeriod; + for (let i=oldPeriod.type - newPeriod.type; i > 1; i--) { + highlightPeriod = highlightPeriod.parent; + } + this.highlightPeriod = highlightPeriod; + setTimeout((function() { + this.highlightPeriod = null; + }).bind(this), 2000); + } + setTimeout((function() { + // TODO: remove widths (now using flex css for width calculation), so we don't need manual calc + //this.adjustScrollElWidth(); + this.restoreScroll(); + this.updateScrollArrows(); + }).bind(this), 1); + }, + // TODO: remove widths (now using flex css for width calculation), so we don't need manual calc + adjustScrollElWidth() { + //this.$refs.periodScroll.style.maxWidth = this.$refs.periods._computedStyle.width; + } + } + }; + + function normalizeComponent(template, style, script, scopeId, isFunctionalTemplate, moduleIdentifier /* server only */, shadowMode, createInjector, createInjectorSSR, createInjectorShadow) { + if (typeof shadowMode !== 'boolean') { + createInjectorSSR = createInjector; + createInjector = shadowMode; + shadowMode = false; + } + // Vue.extend constructor export interop. + const options = typeof script === 'function' ? script.options : script; + // render functions + if (template && template.render) { + options.render = template.render; + options.staticRenderFns = template.staticRenderFns; + options._compiled = true; + // functional template + if (isFunctionalTemplate) { + options.functional = true; + } + } + // scopedId + if (scopeId) { + options._scopeId = scopeId; + } + let hook; + if (moduleIdentifier) { + // server build + hook = function (context) { + // 2.3 injection + context = + context || // cached call + (this.$vnode && this.$vnode.ssrContext) || // stateful + (this.parent && this.parent.$vnode && this.parent.$vnode.ssrContext); // functional + // 2.2 with runInNewContext: true + if (!context && typeof __VUE_SSR_CONTEXT__ !== 'undefined') { + context = __VUE_SSR_CONTEXT__; + } + // inject component styles + if (style) { + style.call(this, createInjectorSSR(context)); + } + // register component module identifier for async chunk inference + if (context && context._registeredComponents) { + context._registeredComponents.add(moduleIdentifier); + } + }; + // used by ssr in case component is cached and beforeCreate + // never gets called + options._ssrRegister = hook; + } + else if (style) { + hook = shadowMode + ? function (context) { + style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot)); + } + : function (context) { + style.call(this, createInjector(context)); + }; + } + if (hook) { + if (options.functional) { + // register for functional component in vue file + const originalRender = options.render; + options.render = function renderWithStyleInjection(h, context) { + hook.call(context); + return originalRender(h, context); + }; + } + else { + // inject component registration as beforeCreate hook + const existing = options.beforeCreate; + options.beforeCreate = existing ? [].concat(existing, hook) : [hook]; + } + } + return script; + } + + const isOldIE = typeof navigator !== 'undefined' && + /msie [6-9]\\b/.test(navigator.userAgent.toLowerCase()); + function createInjector(context) { + return (id, style) => addStyle(id, style); + } + let HEAD; + const styles = {}; + function addStyle(id, css) { + const group = isOldIE ? css.media || 'default' : id; + const style = styles[group] || (styles[group] = { ids: new Set(), styles: [] }); + if (!style.ids.has(id)) { + style.ids.add(id); + let code = css.source; + if (css.map) { + // https://developer.chrome.com/devtools/docs/javascript-debugging + // this makes source maps inside style tags work properly in Chrome + code += '\n/*# sourceURL=' + css.map.sources[0] + ' */'; + // http://stackoverflow.com/a/26603875 + code += + '\n/*# sourceMappingURL=data:application/json;base64,' + + btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) + + ' */'; + } + if (!style.element) { + style.element = document.createElement('style'); + style.element.type = 'text/css'; + if (css.media) + style.element.setAttribute('media', css.media); + if (HEAD === undefined) { + HEAD = document.head || document.getElementsByTagName('head')[0]; + } + HEAD.appendChild(style.element); + } + if ('styleSheet' in style.element) { + style.styles.push(code); + style.element.styleSheet.cssText = style.styles + .filter(Boolean) + .join('\n'); + } + else { + const index = style.ids.size - 1; + const textNode = document.createTextNode(code); + const nodes = style.element.childNodes; + if (nodes[index]) + style.element.removeChild(nodes[index]); + if (nodes.length) + style.element.insertBefore(textNode, nodes[index]); + else + style.element.appendChild(textNode); + } + } + } + + /* script */ + const __vue_script__$4 = script$4; + + /* template */ + var __vue_render__$4 = function() { + var _vm = this; + var _h = _vm.$createElement; + var _c = _vm._self._c || _h; + return _c("div", { staticClass: "timeline" }, [ + _c( + "div", + { + staticClass: "arrow previous", + class: { disabled: _vm.isScrollZero && !_vm.previousPeriod }, + on: { + click: _vm.scrollPrev, + dblclick: function($event) { + $event.stopPropagation(); + $event.preventDefault(); + } + } + }, + [_vm._v("◀")] + ), + _vm._v(" "), + _c( + "div", + { + ref: "periodScroll", + staticClass: "scroll", + class: { highlight: _vm.highlight } + }, + [ + _c( + "div", + { ref: "periods", staticClass: "periods" }, + _vm._l(_vm.period.children, function(subPeriod) { + return _c( + "div", + { + key: subPeriod.id, + staticClass: "period", + class: { + empty: !subPeriod.snapshotCount, + highlight: _vm.highlightPeriod === subPeriod, + "last-level": _vm.isLastZoomLevel + }, + on: { + click: function($event) { + return _vm.changePeriod(subPeriod) + } + } + }, + [ + _c( + "div", + { staticClass: "histo" }, + [ + !subPeriod.snapshot && !_vm.isLastZoomLevel + ? _c("div", { staticClass: "count" }, [ + _c("span", { staticClass: "count-inner" }, [ + _vm._v(_vm._s(subPeriod.snapshotCount)) + ]) + ]) + : _vm._e(), + _vm._v(" "), + _vm._l(subPeriod.children, function(histoPeriod) { + return _c( + "div", + { + key: histoPeriod.id, + staticClass: "line", + style: { + height: _vm.getHistoLineHeight( + histoPeriod.snapshotCount + ) + }, + on: { + click: function($event) { + return _vm.changePeriodFromHistogram(histoPeriod) + } + } + }, + [ + _vm.isLastZoomLevel + ? _c("div", { staticClass: "snap-info" }, [ + _vm._v( + _vm._s( + histoPeriod.snapshot.getTimeDateFormatted() + ) + ) + ]) + : _vm._e() + ] + ) + }) + ], + 2 + ), + _vm._v(" "), + _c("div", { staticClass: "inner" }, [ + _c("div", { staticClass: "label" }, [ + _vm._v(_vm._s(subPeriod.getReadableId())) + ]) + ]) + ] + ) + }), + 0 + ) + ] + ), + _vm._v(" "), + _c( + "div", + { + staticClass: "arrow next", + class: { disabled: _vm.isScrollMax && !_vm.nextPeriod }, + on: { + click: _vm.scrollNext, + dblclick: function($event) { + $event.stopPropagation(); + $event.preventDefault(); + } + } + }, + [_vm._v("▶")] + ) + ]) + }; + var __vue_staticRenderFns__$4 = []; + __vue_render__$4._withStripped = true; + + /* style */ + const __vue_inject_styles__$4 = function (inject) { + if (!inject) return + inject("data-v-54dd375d_0", { source: "\n.timeline {\n position: relative;\n display: flex;\n width: auto;\n height: 80px;\n margin: 5px;\n justify-content: left;\n}\n.timeline .id {\n display: inline-block;\n font-size: 30px;\n}\n.timeline .arrow {\n display: inline-block;\n width: 20px;\n font-size: 20px; /* font-size = width of arrow, as it UTF char */\n line-height: 80px;\n vertical-align: top;\n cursor: pointer;\n}\n.timeline .arrow.previous {\n}\n.timeline .arrow.next {\n}\n.timeline .arrow.disabled, .timeline .arrow.disabled:hover {\n color: lightgray;\n background-color: transparent;\n cursor: not-allowed;\n}\n.timeline .arrow:hover {\n background-color: antiquewhite;\n color: firebrick;\n}\n.timeline .scroll {\n position: relative;\n display: inline-block;\n width: 100%; /* */\n height: 100%;\n\n /* maker scrollable horizontally */\n overflow-x: scroll;\n overflow-y: hidden;\n white-space: nowrap;\n scroll-behavior: smooth;\n\n text-align: center;\n\n transition: background-color 500ms ease-in;\n}\n/* hide scroll bar */\n.timeline .scroll::-webkit-scrollbar {\n display: none;\n}\n/* highlight the scroll period: usually triggered from root app */\n.timeline .scroll.highlight {\n background-color: #fff7ce;\n}\n.timeline .scroll .periods {\n display: flex;\n justify-content: space-between;\n height: 100%;\n width: 100%;\n min-width: 600px;\n}\n.timeline .period {\n flex-grow: 1;\n position: relative;\n display: inline-block;\n height: 100%;\n /* line-height: 80px; /* use to center middle vertically */\n white-space: normal;\n vertical-align: top;\n text-align: center;\n /*background-color: #eee;*/\n /*border-right: 1px solid white; !* all other periods have right border, except first period*!*/\n cursor: pointer;\n\n transition: background-color 500ms ease-in-out;\n}\n/* 1st period period child el */\n.timeline .period:nth-child(1) {\n /*border-left: 1px solid white; !* has left border; all other periods have right border *!*/\n}\n.timeline .period:hover {\n background-color: #eeeeee;\n}\n\n/* empty period */\n.timeline .period.empty {\n color: #aaa;\n /*background-color: transparent;*/\n}\n/* highlighted period */\n.timeline .period.highlight {\n background-color: cyan;\n}\n.timeline .period .label {\n font-weight: bold;\n font-size: 14px;\n font-family: Baskerville, sans-serif;\n}\n.timeline .period .count {\n position: absolute;\n top: 0;\n left: 0;\n display: none;\n width: 100%;\n height: 100%;\n padding: 10px 0 0 0;\n text-align: center;\n font-size: 25px;\n}\n.timeline .period .count .count-inner {\n display: inline;\n width: auto;\n background-color: rgba(255,255,255,.85);\n padding: 1px;\n border-radius: 5px;\n}\n.timeline .period:hover .count {\n display: block;\n}\n.timeline .period .inner {\n display: block;\n position: absolute;\n bottom: 0;\n left: 0;\n width: 100%;\n height: 16px;\n background-color: white;\n border-top: 1px solid gray;\n}\n.timeline .period.last-level .inner {\n text-align: left;\n}\n.timeline .period .histo {\n display: flex;\n position: absolute;\n top: 1px;\n left: 0;\n width: 100%;\n height: 60px;\n align-items: flex-end;\n justify-content: space-between;\n text-align: left;\n}\n.timeline .period .histo .line {\n flex-grow: 1;\n display: inline-block;\n background-color: #a6cdf5;\n margin: 0;\n padding: 0;\n}\n\n/* Last level period histogram spaces things evenly */\n.timeline .period.last-level .histo {\n justify-content: space-around;\n}\n\n/* Last level period histogram lines do not grow, but are fixed width/margin */\n.timeline .period.last-level .histo .line {\n flex-grow: unset;\n width: 5px;\n margin-left: 2px;\n\n position: relative;\n}\n\n /* update line color on hover*/\n.timeline .period.last-level .histo .line:hover {\n background-color: #f5a6eb;\n}\n\n /*Last level period histogram line has extra info*/\n.timeline .period.last-level .histo .line .snap-info {\n position: absolute;\n z-index: 10;\n top: 30%;\n left: 120%;\n display: none;\n background-color: white;\n border: 1px solid gray;\n padding: 2px;\n white-space: nowrap; /*no wrapping allowed*/\n}\n /*show on hover*/\n.timeline .period.last-level .histo .line:hover .snap-info {\n display: block;\n}\n\n", map: {"version":3,"sources":["/Users/ilya/core/pywb/pywb/vueui/src/components/Timeline.vue"],"names":[],"mappings":";AAoLA;IACA,kBAAA;IACA,aAAA;IACA,WAAA;IACA,YAAA;IACA,WAAA;IACA,qBAAA;AACA;AAEA;IACA,qBAAA;IACA,eAAA;AACA;AACA;IACA,qBAAA;IACA,WAAA;IACA,eAAA,EAAA,+CAAA;IACA,iBAAA;IACA,mBAAA;IACA,eAAA;AACA;AACA;AACA;AACA;AACA;AACA;IACA,gBAAA;IACA,6BAAA;IACA,mBAAA;AACA;AACA;IACA,8BAAA;IACA,gBAAA;AACA;AAEA;IACA,kBAAA;IACA,qBAAA;IACA,WAAA,EAAA,IAAA;IACA,YAAA;;IAEA,kCAAA;IACA,kBAAA;IACA,kBAAA;IACA,mBAAA;IACA,uBAAA;;IAEA,kBAAA;;IAEA,0CAAA;AACA;AACA,oBAAA;AACA;IACA,aAAA;AACA;AACA,iEAAA;AACA;IACA,yBAAA;AACA;AACA;IACA,aAAA;IACA,8BAAA;IACA,YAAA;IACA,WAAA;IACA,gBAAA;AACA;AAGA;IACA,YAAA;IACA,kBAAA;IACA,qBAAA;IACA,YAAA;IACA,0DAAA;IACA,mBAAA;IACA,mBAAA;IACA,kBAAA;IACA,0BAAA;IACA,+FAAA;IACA,eAAA;;IAEA,8CAAA;AACA;AACA,+BAAA;AACA;IACA,2FAAA;AACA;AAEA;IACA,yBAAA;AACA;;AAEA,iBAAA;AACA;IACA,WAAA;IACA,iCAAA;AACA;AACA,uBAAA;AACA;IACA,sBAAA;AACA;AAEA;IACA,iBAAA;IACA,eAAA;IACA,oCAAA;AACA;AACA;IACA,kBAAA;IACA,MAAA;IACA,OAAA;IACA,aAAA;IACA,WAAA;IACA,YAAA;IACA,mBAAA;IACA,kBAAA;IACA,eAAA;AACA;AACA;IACA,eAAA;IACA,WAAA;IACA,uCAAA;IACA,YAAA;IACA,kBAAA;AACA;AACA;IACA,cAAA;AACA;AACA;IACA,cAAA;IACA,kBAAA;IACA,SAAA;IACA,OAAA;IACA,WAAA;IACA,YAAA;IACA,uBAAA;IACA,0BAAA;AACA;AACA;IACA,gBAAA;AACA;AAEA;IACA,aAAA;IACA,kBAAA;IACA,QAAA;IACA,OAAA;IACA,WAAA;IACA,YAAA;IACA,qBAAA;IACA,8BAAA;IACA,gBAAA;AACA;AAEA;IACA,YAAA;IACA,qBAAA;IACA,yBAAA;IACA,SAAA;IACA,UAAA;AACA;;AAEA,qDAAA;AACA;IACA,6BAAA;AACA;;AAEA,8EAAA;AACA;IACA,gBAAA;IACA,UAAA;IACA,gBAAA;;IAEA,kBAAA;AACA;;IAEA,8BAAA;AACA;QACA,yBAAA;AACA;;IAEA,kDAAA;AACA;QACA,kBAAA;QACA,WAAA;QACA,QAAA;QACA,UAAA;QACA,aAAA;QACA,uBAAA;QACA,sBAAA;QACA,YAAA;QACA,mBAAA,EAAA,sBAAA;AACA;QACA,gBAAA;AACA;YACA,cAAA;AACA","file":"Timeline.vue","sourcesContent":["\n\n\n\n\n\n"]}, media: undefined }); + + }; + /* scoped */ + const __vue_scope_id__$4 = undefined; + /* module identifier */ + const __vue_module_identifier__$4 = undefined; + /* functional template */ + const __vue_is_functional_template__$4 = false; + /* style inject SSR */ + + /* style inject shadow dom */ + + + + const __vue_component__$4 = /*#__PURE__*/normalizeComponent( + { render: __vue_render__$4, staticRenderFns: __vue_staticRenderFns__$4 }, + __vue_inject_styles__$4, + __vue_script__$4, + __vue_scope_id__$4, + __vue_is_functional_template__$4, + __vue_module_identifier__$4, + false, + createInjector, + undefined, + undefined + ); + + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + + var script$3 = { + props: { + period: { + required: true + } + }, + computed: { + parents: function() { + return this.period.getParents(); + } + }, + methods: { + changePeriod(period) { + if (period.snapshotCount) { + this.$emit("goto-period", period); + } + }, + } + }; + + /* script */ + const __vue_script__$3 = script$3; + + /* template */ + var __vue_render__$3 = function() { + var _vm = this; + var _h = _vm.$createElement; + var _c = _vm._self._c || _h; + return _c( + "div", + { staticClass: "summary" }, + [ + _vm.parents.length + ? [ + _c("span", { staticClass: "item" }, [ + _c( + "span", + { + staticClass: "goto", + on: { + click: function($event) { + return _vm.changePeriod(_vm.parents[0]) + } + } + }, + [_vm._v(_vm._s(_vm.parents[0].getReadableId(true)))] + ) + ]), + _vm._v("\n >\n "), + _vm._l(_vm.parents, function(parent, i) { + return i > 0 + ? _c("span", { key: parent.id, staticClass: "item" }, [ + _c( + "span", + { + staticClass: "goto", + on: { + click: function($event) { + return _vm.changePeriod(parent) + } + } + }, + [_vm._v(_vm._s(parent.getReadableId(true)))] + ) + ]) + : _vm._e() + }) + ] + : _vm._e(), + _vm._v(" "), + _c("span", { staticClass: "item" }, [ + _c("span", { staticClass: "current" }, [ + _vm._v(_vm._s(_vm.period.getReadableId(true))) + ]), + _vm._v(" "), + _c("span", { staticClass: "count" }, [ + _vm._v("(" + _vm._s(_vm.period.snapshotCount) + " capture"), + _vm.period.snapshotCount !== 1 ? _c("span", [_vm._v("s")]) : _vm._e(), + _vm._v(")") + ]) + ]) + ], + 2 + ) + }; + var __vue_staticRenderFns__$3 = []; + __vue_render__$3._withStripped = true; + + /* style */ + const __vue_inject_styles__$3 = function (inject) { + if (!inject) return + inject("data-v-05529944_0", { source: "\n.summary {\n display: inline-block;\n}\n.summary .item {\n position: relative;\n display: inline;\n margin: 0 2px 0 0;\n font-size: inherit;\n}\n.summary .count {\n vertical-align: middle;\n font-size: inherit;\n}\n.summary .count .verbose {\n display: none;\n}\n.summary .count:hover .verbose {\n display: inline;\n}\n.summary .item .goto {\n margin: 1px;\n cursor: pointer;\n color: darkslateblue;\n text-decoration: underline;\n}\n.summary .item .goto:hover {\n background-color: #a6cdf5;\n}\n.summary .item.snapshot {\n display: block;\n}\n\n", map: {"version":3,"sources":["/Users/ilya/core/pywb/pywb/vueui/src/components/Summary.vue"],"names":[],"mappings":";AAyCA;IACA,qBAAA;AACA;AAEA;IACA,kBAAA;IACA,eAAA;IACA,iBAAA;IACA,kBAAA;AACA;AACA;IACA,sBAAA;IACA,kBAAA;AACA;AACA;IACA,aAAA;AACA;AACA;IACA,eAAA;AACA;AAEA;IACA,WAAA;IACA,eAAA;IACA,oBAAA;IACA,0BAAA;AACA;AACA;IACA,yBAAA;AACA;AACA;IACA,cAAA;AACA","file":"Summary.vue","sourcesContent":["\n\n\n\n"]}, media: undefined }); + + }; + /* scoped */ + const __vue_scope_id__$3 = undefined; + /* module identifier */ + const __vue_module_identifier__$3 = undefined; + /* functional template */ + const __vue_is_functional_template__$3 = false; + /* style inject SSR */ + + /* style inject shadow dom */ + + + + const __vue_component__$3 = /*#__PURE__*/normalizeComponent( + { render: __vue_render__$3, staticRenderFns: __vue_staticRenderFns__$3 }, + __vue_inject_styles__$3, + __vue_script__$3, + __vue_scope_id__$3, + __vue_is_functional_template__$3, + __vue_module_identifier__$3, + false, + createInjector, + undefined, + undefined + ); + + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + // + + var script$2 = { + props: ["month", "year", "isCurrent"], + data: function() { + return { + maxInDay: 0, + daySize: 30, + }; + }, + computed: { + dayStyle() { + const s = this.daySize; + return `height: ${s}px; width: ${s}px; line-height: ${s}px`; + }, + days() { + if (!this.month || !this.month.snapshotCount) { + return []; + } + const days = []; + // Get days in month, and days in the complete weeks before first day and after last day + const [firstDay, lastDay] = this.month.getChildrenRange(); + const daysBeforeFirst = (new Date(this.year.id, this.month.id-1, firstDay)).getDay(); + const daysAfterLastDay = (6 - (new Date(this.year.id, this.month.id-1, lastDay)).getDay()); + for(let i=0; i h3 {\n margin: 0;\n font-size: 16px;\n}\n.calendar-month > .empty {\n position: absolute;\n top: 45%;\n width: 100%;\n color: gray;\n}\n.calendar-month .day {\n position: relative;\n display: inline-block;\n margin: 0;\n text-align: center;\n}\n.calendar-month .day.empty {\n color: gray;\n}\n.calendar-month .day .count {\n display: none;\n position: absolute;\n bottom: 80%;\n left: 80%;\n line-height: 1; /* reset to normal */\n padding: 3px;\n border-radius: 10px;\n border-bottom-left-radius: 0;\n border: 1px solid gray;\n background-color: white;\n z-index: 30;\n white-space: nowrap;\n}\n.calendar-month .day:hover .count {\n display: block;\n}\n.calendar-month .day .size {\n position: absolute;\n box-sizing: border-box;\n background-color: rgba(166, 205, 245, .85);\n z-index: 10;\n}\n.calendar-month .day .day-id {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 11;\n\n display: inline-block;\n width: 100%;\n text-align: center;\n\n color: black;\n}\n.calendar-month .day:hover .size {\n border: 1px solid black;\n}\n.calendar-month .day:hover {\n cursor: zoom-in;\n}\n", map: {"version":3,"sources":["/Users/ilya/core/pywb/pywb/vueui/src/components/CalendarMonth.vue"],"names":[],"mappings":";AACA;IACA,kBAAA;IACA,qBAAA;IACA,YAAA;IACA,SAAA;IACA,aAAA;IACA,YAAA;IACA,kBAAA;IACA,mBAAA;AACA;AACA;IACA,yBAAA;IACA,mBAAA;AACA;AACA;IACA,yBAAA;IACA,kBAAA;AACA;AACA;IACA,SAAA;IACA,eAAA;AACA;AACA;IACA,kBAAA;IACA,QAAA;IACA,WAAA;IACA,WAAA;AACA;AACA;IACA,kBAAA;IACA,qBAAA;IACA,SAAA;IACA,kBAAA;AACA;AACA;IACA,WAAA;AACA;AACA;IACA,aAAA;IACA,kBAAA;IACA,WAAA;IACA,SAAA;IACA,cAAA,EAAA,oBAAA;IACA,YAAA;IACA,mBAAA;IACA,4BAAA;IACA,sBAAA;IACA,uBAAA;IACA,WAAA;IACA,mBAAA;AACA;AACA;IACA,cAAA;AACA;AACA;IACA,kBAAA;IACA,sBAAA;IACA,0CAAA;IACA,WAAA;AACA;AACA;IACA,kBAAA;IACA,MAAA;IACA,OAAA;IACA,WAAA;;IAEA,qBAAA;IACA,WAAA;IACA,kBAAA;;IAEA,YAAA;AACA;AACA;IACA,uBAAA;AACA;AACA;IACA,eAAA;AACA","file":"CalendarMonth.vue","sourcesContent":["\n\n\n\n\n\n"]}, media: undefined }); + + }; + /* scoped */ + const __vue_scope_id__$2 = undefined; + /* module identifier */ + const __vue_module_identifier__$2 = undefined; + /* functional template */ + const __vue_is_functional_template__$2 = false; + /* style inject SSR */ + + /* style inject shadow dom */ + + + + const __vue_component__$2 = /*#__PURE__*/normalizeComponent( + { render: __vue_render__$2, staticRenderFns: __vue_staticRenderFns__$2 }, + __vue_inject_styles__$2, + __vue_script__$2, + __vue_scope_id__$2, + __vue_is_functional_template__$2, + __vue_module_identifier__$2, + false, + createInjector, + undefined, + undefined + ); + + // + + var script$1 = { + components: {CalendarMonth: __vue_component__$2}, + props: ["period"], + data: function() { + return {}; + }, + computed: { + year() { + let year = null; + if (this.period.type === PywbPeriod.Type.all) { + year = this.period.children[this.period.children.length-1]; + } else if (this.period.type === PywbPeriod.Type.year) { + year = this.period; + } else { + year = this.period.getParents().filter(p => p.type === PywbPeriod.Type.year)[0]; + } + if (year) { + year.fillEmptyChildPeriods(true); + } + return year; + }, + currentMonth() { + let month = null; + if (this.period.type === PywbPeriod.Type.month) { + month = this.period; + } else { + month = this.period.getParents().filter(p => p.type === PywbPeriod.Type.month)[0]; + } + return month; + } + } + }; + + /* script */ + const __vue_script__$1 = script$1; + + /* template */ + var __vue_render__$1 = function() { + var _vm = this; + var _h = _vm.$createElement; + var _c = _vm._self._c || _h; + return _c("div", { staticClass: "full-view" }, [ + _c("h2", [ + _vm._v( + _vm._s(_vm.year.snapshotCount) + " captures in " + _vm._s(_vm.year.id) + ) + ]), + _vm._v(" "), + _c( + "div", + { staticClass: "months" }, + _vm._l(_vm.year.children, function(month) { + return _c("CalendarMonth", { + key: month.id, + attrs: { + month: month, + year: _vm.year, + "is-current": month === _vm.currentMonth + }, + on: { + "goto-period": function($event) { + return _vm.$emit("goto-period", $event) + } + } + }) + }), + 1 + ) + ]) + }; + var __vue_staticRenderFns__$1 = []; + __vue_render__$1._withStripped = true; + + /* style */ + const __vue_inject_styles__$1 = function (inject) { + if (!inject) return + inject("data-v-cecde690_0", { source: "\n.full-view {\n position: absolute;\n top: 130px;\n left: 0;\n height: 80vh;\n width: 100%;\n background-color: white;\n}\n.full-view .months {\n display: flex;\n justify-content: center;\n flex-wrap: wrap;\n align-items: flex-start;\n}\n.full-view h2 {\n margin: 10px 0;\n font-size: 20px;\n text-align: center;\n}\n", map: {"version":3,"sources":["/Users/ilya/core/pywb/pywb/vueui/src/components/CalendarYear.vue"],"names":[],"mappings":";AACA;IACA,kBAAA;IACA,UAAA;IACA,OAAA;IACA,YAAA;IACA,WAAA;IACA,uBAAA;AACA;AACA;IACA,aAAA;IACA,uBAAA;IACA,eAAA;IACA,uBAAA;AACA;AAEA;IACA,cAAA;IACA,eAAA;IACA,kBAAA;AACA","file":"CalendarYear.vue","sourcesContent":["\n\n\n\n\n\n"]}, media: undefined }); + + }; + /* scoped */ + const __vue_scope_id__$1 = undefined; + /* module identifier */ + const __vue_module_identifier__$1 = undefined; + /* functional template */ + const __vue_is_functional_template__$1 = false; + /* style inject SSR */ + + /* style inject shadow dom */ + + + + const __vue_component__$1 = /*#__PURE__*/normalizeComponent( + { render: __vue_render__$1, staticRenderFns: __vue_staticRenderFns__$1 }, + __vue_inject_styles__$1, + __vue_script__$1, + __vue_scope_id__$1, + __vue_is_functional_template__$1, + __vue_module_identifier__$1, + false, + createInjector, + undefined, + undefined + ); + + // + + var script = { + name: "PywbReplayApp", + //el: '[data-app="webrecorder-replay-app"]', + data: function() { + return { + snapshots: [], + currentPeriod: null, + currentSnapshot: null, + msgs: [], + showFullView: false, + config: { + title: "", + initialView: {} + }, + timelineHighlight: false + }; + }, + components: {Timeline: __vue_component__$4, TimelineSummary: __vue_component__$3, CalendarYear: __vue_component__$1}, + mounted: function() { + this.init(); + }, + methods: { + gotoPeriod: function(newPeriod, initiator) { + if (this.timelineHighlight) { + setTimeout((() => { + this.timelineHighlight=false; + }).bind(this), 3000); + } + if (newPeriod.snapshot) { + this.gotoSnapshot(newPeriod.snapshot); + } else { + this.currentPeriod = newPeriod; + } + }, + gotoSnapshot(snapshot) { + this.currentSnapshot = snapshot; + // COMMUNICATE TO ContentFrame + this.$emit("show-snapshot", snapshot); + this.showFullView = false; + }, + init() { + if (!this.config.title) { + this.config.title = this.config.initialView.url; + } + if (this.config.initialView.timestamp === "undefined") { + this.showFullView = true; + } else { + this.showFullView = false; + + // convert to snapshot objec to support proper rendering of time/date + const snapshot = new PywbSnapshot(this.config.initialView, 0); + this.gotoSnapshot(snapshot); + } + } + } + }; + + /* script */ + const __vue_script__ = script; + + /* template */ + var __vue_render__ = function() { + var _vm = this; + var _h = _vm.$createElement; + var _c = _vm._self._c || _h; + return _c( + "div", + { staticClass: "app", attrs: { "data-app": "webrecorder-replay-app" } }, + [ + _c("div", { staticClass: "short-nav" }, [ + _c("div", { staticClass: "first-line" }, [ + _c("div", { staticClass: "logo" }, [ + _c("img", { attrs: { src: _vm.config.logoImg } }) + ]), + _vm._v(" "), + _c( + "div", + { staticClass: "url-and-timeline" }, + [ + _c( + "div", + { staticClass: "url" }, + [ + _c("strong", [_vm._v(_vm._s(_vm.config.title))]), + _vm._v( + " (" + + _vm._s(_vm.snapshots.length) + + " captures: " + + _vm._s(_vm.snapshots[0].getDateFormatted()) + + " - " + + _vm._s( + _vm.snapshots[ + _vm.snapshots.length - 1 + ].getDateFormatted() + ) + + ")" + ), + _c("br"), + _vm._v(" "), + _vm.currentPeriod + ? _c("TimelineSummary", { + attrs: { period: _vm.currentPeriod }, + on: { "goto-period": _vm.gotoPeriod } + }) + : _vm._e(), + _vm._v(" "), + _c( + "span", + { + staticClass: "full-view-toggle", + class: { expanded: _vm.showFullView }, + on: { + click: function($event) { + _vm.showFullView = !_vm.showFullView; + } + } + }, + [ + !_vm.showFullView + ? [ + _c("span", { staticClass: "detail" }, [ + _vm._v("show year calendar") + ]) + ] + : [ + _c("span", { staticClass: "detail" }, [ + _vm._v("hide year calendar") + ]) + ], + _vm._v(" "), + _c("img", { attrs: { src: "/static/calendar-icon.png" } }) + ], + 2 + ) + ], + 1 + ), + _vm._v(" "), + _vm.currentPeriod + ? _c("Timeline", { + attrs: { + period: _vm.currentPeriod, + highlight: _vm.timelineHighlight + }, + on: { "goto-period": _vm.gotoPeriod } + }) + : _vm._e() + ], + 1 + ) + ]), + _vm._v(" "), + _vm.currentSnapshot && !_vm.showFullView + ? _c("div", { staticClass: "second-line" }, [ + _c("h2", [ + _vm._v( + "Showing Capture from " + + _vm._s(_vm.currentSnapshot.getTimeDateFormatted()) + ) + ]) + ]) + : _vm._e() + ]), + _vm._v(" "), + _vm.showFullView + ? _c("CalendarYear", { + attrs: { period: _vm.currentPeriod }, + on: { "goto-period": _vm.gotoPeriod } + }) + : _vm._e() + ], + 1 + ) + }; + var __vue_staticRenderFns__ = []; + __vue_render__._withStripped = true; + + /* style */ + const __vue_inject_styles__ = function (inject) { + if (!inject) return + inject("data-v-8f962408_0", { source: "\n.app {\n border-bottom: 1px solid lightcoral;\n height: 150px;\n width: 100%;\n}\n.iframe iframe {\n width: 100%;\n height: 80vh;\n}\n.logo {\n margin-right: 30px;\n width: 180px;\n}\n.short-nav {\n width: 100%;\n position: relative;\n}\n.short-nav .first-line {\n display: flex;\n justify-content: flex-start;\n}\n.short-nav .second-line {\n display: flex;\n justify-content: flex-start;\n}\n.short-nav .logo {\n flex-shrink: initial;\n}\n.short-nav .url-and-timeline {\n flex-grow: 2;\n overflow-x: hidden;\n}\n.short-nav .url {\n text-align: left;\n margin: 0 25px;\n position: relative;\n}\n.full-view-toggle {\n position: absolute;\n top: 0;\n right: 0;\n border-radius: 5px;\n padding: 4px;\n cursor: zoom-in;\n}\n.full-view-toggle img {\n width: 17px;\n display: inline-block;\n margin-top: 2px;\n}\n.full-view-toggle:hover {\n background-color: #eeeeee;\n}\n.full-view-toggle .detail {\n display: none;\n}\n.full-view-toggle:hover .detail {\n display: inline;\n}\n.full-view-toggle.expanded {\n background-color: #eeeeee;\n cursor: zoom-out;\n}\n", map: {"version":3,"sources":["/Users/ilya/core/pywb/pywb/vueui/src/App.vue"],"names":[],"mappings":";AAwGA;EACA,mCAAA;EACA,aAAA;EACA,WAAA;AACA;AACA;EACA,WAAA;EACA,YAAA;AACA;AACA;EACA,kBAAA;EACA,YAAA;AACA;AACA;EACA,WAAA;EACA,kBAAA;AACA;AACA;EACA,aAAA;EACA,2BAAA;AACA;AACA;EACA,aAAA;EACA,2BAAA;AACA;AAEA;EACA,oBAAA;AACA;AAEA;EACA,YAAA;EACA,kBAAA;AACA;AAEA;EACA,gBAAA;EACA,cAAA;EACA,kBAAA;AACA;AAEA;EACA,kBAAA;EACA,MAAA;EACA,QAAA;EACA,kBAAA;EACA,YAAA;EACA,eAAA;AACA;AACA;EACA,WAAA;EACA,qBAAA;EACA,eAAA;AACA;AACA;EACA,yBAAA;AACA;AACA;EACA,aAAA;AACA;AACA;EACA,eAAA;AACA;AACA;EACA,yBAAA;EACA,gBAAA;AACA","file":"App.vue","sourcesContent":["\n\n\n\n\n"]}, media: undefined }); + + }; + /* scoped */ + const __vue_scope_id__ = undefined; + /* module identifier */ + const __vue_module_identifier__ = undefined; + /* functional template */ + const __vue_is_functional_template__ = false; + /* style inject SSR */ + + /* style inject shadow dom */ + + + + const __vue_component__ = /*#__PURE__*/normalizeComponent( + { render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ }, + __vue_inject_styles__, + __vue_script__, + __vue_scope_id__, + __vue_is_functional_template__, + __vue_module_identifier__, + false, + createInjector, + undefined, + undefined + ); + + /*! + * Vue.js v2.6.14 + * (c) 2014-2021 Evan You + * Released under the MIT License. + */ + /* */ + + const emptyObject = Object.freeze({}); + + // These helpers produce better VM code in JS engines due to their + // explicitness and function inlining. + function isUndef (v) { + return v === undefined || v === null + } + + function isDef (v) { + return v !== undefined && v !== null + } + + function isTrue (v) { + return v === true + } + + function isFalse (v) { + return v === false + } + + /** + * Check if value is primitive. + */ + function isPrimitive (value) { + return ( + typeof value === 'string' || + typeof value === 'number' || + // $flow-disable-line + typeof value === 'symbol' || + typeof value === 'boolean' + ) + } + + /** + * Quick object check - this is primarily used to tell + * Objects from primitive values when we know the value + * is a JSON-compliant type. + */ + function isObject (obj) { + return obj !== null && typeof obj === 'object' + } + + /** + * Get the raw type string of a value, e.g., [object Object]. + */ + const _toString = Object.prototype.toString; + + function toRawType (value) { + return _toString.call(value).slice(8, -1) + } + + /** + * Strict object type check. Only returns true + * for plain JavaScript objects. + */ + function isPlainObject (obj) { + return _toString.call(obj) === '[object Object]' + } + + function isRegExp (v) { + return _toString.call(v) === '[object RegExp]' + } + + /** + * Check if val is a valid array index. + */ + function isValidArrayIndex (val) { + const n = parseFloat(String(val)); + return n >= 0 && Math.floor(n) === n && isFinite(val) + } + + function isPromise (val) { + return ( + isDef(val) && + typeof val.then === 'function' && + typeof val.catch === 'function' + ) + } + + /** + * Convert a value to a string that is actually rendered. + */ + function toString (val) { + return val == null + ? '' + : Array.isArray(val) || (isPlainObject(val) && val.toString === _toString) + ? JSON.stringify(val, null, 2) + : String(val) + } + + /** + * Convert an input value to a number for persistence. + * If the conversion fails, return original string. + */ + function toNumber (val) { + const n = parseFloat(val); + return isNaN(n) ? val : n + } + + /** + * Make a map and return a function for checking if a key + * is in that map. + */ + function makeMap ( + str, + expectsLowerCase + ) { + const map = Object.create(null); + const list = str.split(','); + for (let i = 0; i < list.length; i++) { + map[list[i]] = true; + } + return expectsLowerCase + ? val => map[val.toLowerCase()] + : val => map[val] + } + + /** + * Check if a tag is a built-in tag. + */ + const isBuiltInTag = makeMap('slot,component', true); + + /** + * Check if an attribute is a reserved attribute. + */ + const isReservedAttribute = makeMap('key,ref,slot,slot-scope,is'); + + /** + * Remove an item from an array. + */ + function remove (arr, item) { + if (arr.length) { + const index = arr.indexOf(item); + if (index > -1) { + return arr.splice(index, 1) + } + } + } + + /** + * Check whether an object has the property. + */ + const hasOwnProperty = Object.prototype.hasOwnProperty; + function hasOwn (obj, key) { + return hasOwnProperty.call(obj, key) + } + + /** + * Create a cached version of a pure function. + */ + function cached (fn) { + const cache = Object.create(null); + return (function cachedFn (str) { + const hit = cache[str]; + return hit || (cache[str] = fn(str)) + }) + } + + /** + * Camelize a hyphen-delimited string. + */ + const camelizeRE = /-(\w)/g; + const camelize = cached((str) => { + return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : '') + }); + + /** + * Capitalize a string. + */ + const capitalize = cached((str) => { + return str.charAt(0).toUpperCase() + str.slice(1) + }); + + /** + * Hyphenate a camelCase string. + */ + const hyphenateRE = /\B([A-Z])/g; + const hyphenate = cached((str) => { + return str.replace(hyphenateRE, '-$1').toLowerCase() + }); + + /** + * Simple bind polyfill for environments that do not support it, + * e.g., PhantomJS 1.x. Technically, we don't need this anymore + * since native bind is now performant enough in most browsers. + * But removing it would mean breaking code that was able to run in + * PhantomJS 1.x, so this must be kept for backward compatibility. + */ + + /* istanbul ignore next */ + function polyfillBind (fn, ctx) { + function boundFn (a) { + const l = arguments.length; + return l + ? l > 1 + ? fn.apply(ctx, arguments) + : fn.call(ctx, a) + : fn.call(ctx) + } + + boundFn._length = fn.length; + return boundFn + } + + function nativeBind (fn, ctx) { + return fn.bind(ctx) + } + + const bind = Function.prototype.bind + ? nativeBind + : polyfillBind; + + /** + * Convert an Array-like object to a real Array. + */ + function toArray (list, start) { + start = start || 0; + let i = list.length - start; + const ret = new Array(i); + while (i--) { + ret[i] = list[i + start]; + } + return ret + } + + /** + * Mix properties into target object. + */ + function extend (to, _from) { + for (const key in _from) { + to[key] = _from[key]; + } + return to + } + + /** + * Merge an Array of Objects into a single Object. + */ + function toObject (arr) { + const res = {}; + for (let i = 0; i < arr.length; i++) { + if (arr[i]) { + extend(res, arr[i]); + } + } + return res + } + + /* eslint-disable no-unused-vars */ + + /** + * Perform no operation. + * Stubbing args to make Flow happy without leaving useless transpiled code + * with ...rest (https://flow.org/blog/2017/05/07/Strict-Function-Call-Arity/). + */ + function noop (a, b, c) {} + + /** + * Always return false. + */ + const no = (a, b, c) => false; + + /* eslint-enable no-unused-vars */ + + /** + * Return the same value. + */ + const identity = (_) => _; + + /** + * Generate a string containing static keys from compiler modules. + */ + function genStaticKeys (modules) { + return modules.reduce((keys, m) => { + return keys.concat(m.staticKeys || []) + }, []).join(',') + } + + /** + * Check if two values are loosely equal - that is, + * if they are plain objects, do they have the same shape? + */ + function looseEqual (a, b) { + if (a === b) return true + const isObjectA = isObject(a); + const isObjectB = isObject(b); + if (isObjectA && isObjectB) { + try { + const isArrayA = Array.isArray(a); + const isArrayB = Array.isArray(b); + if (isArrayA && isArrayB) { + return a.length === b.length && a.every((e, i) => { + return looseEqual(e, b[i]) + }) + } else if (a instanceof Date && b instanceof Date) { + return a.getTime() === b.getTime() + } else if (!isArrayA && !isArrayB) { + const keysA = Object.keys(a); + const keysB = Object.keys(b); + return keysA.length === keysB.length && keysA.every(key => { + return looseEqual(a[key], b[key]) + }) + } else { + /* istanbul ignore next */ + return false + } + } catch (e) { + /* istanbul ignore next */ + return false + } + } else if (!isObjectA && !isObjectB) { + return String(a) === String(b) + } else { + return false + } + } + + /** + * Return the first index at which a loosely equal value can be + * found in the array (if value is a plain object, the array must + * contain an object of the same shape), or -1 if it is not present. + */ + function looseIndexOf (arr, val) { + for (let i = 0; i < arr.length; i++) { + if (looseEqual(arr[i], val)) return i + } + return -1 + } + + /** + * Ensure a function is called only once. + */ + function once (fn) { + let called = false; + return function () { + if (!called) { + called = true; + fn.apply(this, arguments); + } + } + } + + const SSR_ATTR = 'data-server-rendered'; + + const ASSET_TYPES = [ + 'component', + 'directive', + 'filter' + ]; + + const LIFECYCLE_HOOKS = [ + 'beforeCreate', + 'created', + 'beforeMount', + 'mounted', + 'beforeUpdate', + 'updated', + 'beforeDestroy', + 'destroyed', + 'activated', + 'deactivated', + 'errorCaptured', + 'serverPrefetch' + ]; + + /* */ + + + + var config = ({ + /** + * Option merge strategies (used in core/util/options) + */ + // $flow-disable-line + optionMergeStrategies: Object.create(null), + + /** + * Whether to suppress warnings. + */ + silent: false, + + /** + * Show production mode tip message on boot? + */ + productionTip: "development" !== 'production', + + /** + * Whether to enable devtools + */ + devtools: "development" !== 'production', + + /** + * Whether to record perf + */ + performance: false, + + /** + * Error handler for watcher errors + */ + errorHandler: null, + + /** + * Warn handler for watcher warns + */ + warnHandler: null, + + /** + * Ignore certain custom elements + */ + ignoredElements: [], + + /** + * Custom user key aliases for v-on + */ + // $flow-disable-line + keyCodes: Object.create(null), + + /** + * Check if a tag is reserved so that it cannot be registered as a + * component. This is platform-dependent and may be overwritten. + */ + isReservedTag: no, + + /** + * Check if an attribute is reserved so that it cannot be used as a component + * prop. This is platform-dependent and may be overwritten. + */ + isReservedAttr: no, + + /** + * Check if a tag is an unknown element. + * Platform-dependent. + */ + isUnknownElement: no, + + /** + * Get the namespace of an element + */ + getTagNamespace: noop, + + /** + * Parse the real tag name for the specific platform. + */ + parsePlatformTagName: identity, + + /** + * Check if an attribute must be bound using property, e.g. value + * Platform-dependent. + */ + mustUseProp: no, + + /** + * Perform updates asynchronously. Intended to be used by Vue Test Utils + * This will significantly reduce performance if set to false. + */ + async: true, + + /** + * Exposed for legacy reasons + */ + _lifecycleHooks: LIFECYCLE_HOOKS + }); + + /* */ + + /** + * unicode letters used for parsing html tags, component names and property paths. + * using https://www.w3.org/TR/html53/semantics-scripting.html#potentialcustomelementname + * skipping \u10000-\uEFFFF due to it freezing up PhantomJS + */ + const unicodeRegExp = /a-zA-Z\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD/; + + /** + * Check if a string starts with $ or _ + */ + function isReserved (str) { + const c = (str + '').charCodeAt(0); + return c === 0x24 || c === 0x5F + } + + /** + * Define a property. + */ + function def (obj, key, val, enumerable) { + Object.defineProperty(obj, key, { + value: val, + enumerable: !!enumerable, + writable: true, + configurable: true + }); + } + + /** + * Parse simple path. + */ + const bailRE = new RegExp(`[^${unicodeRegExp.source}.$_\\d]`); + function parsePath (path) { + if (bailRE.test(path)) { + return + } + const segments = path.split('.'); + return function (obj) { + for (let i = 0; i < segments.length; i++) { + if (!obj) return + obj = obj[segments[i]]; + } + return obj + } + } + + /* */ + + // can we use __proto__? + const hasProto = '__proto__' in {}; + + // Browser environment sniffing + const inBrowser = typeof window !== 'undefined'; + const inWeex = typeof WXEnvironment !== 'undefined' && !!WXEnvironment.platform; + const weexPlatform = inWeex && WXEnvironment.platform.toLowerCase(); + const UA = inBrowser && window.navigator.userAgent.toLowerCase(); + const isIE = UA && /msie|trident/.test(UA); + const isIE9 = UA && UA.indexOf('msie 9.0') > 0; + const isEdge = UA && UA.indexOf('edge/') > 0; + (UA && UA.indexOf('android') > 0) || (weexPlatform === 'android'); + const isIOS = (UA && /iphone|ipad|ipod|ios/.test(UA)) || (weexPlatform === 'ios'); + UA && /chrome\/\d+/.test(UA) && !isEdge; + UA && /phantomjs/.test(UA); + const isFF = UA && UA.match(/firefox\/(\d+)/); + + // Firefox has a "watch" function on Object.prototype... + const nativeWatch = ({}).watch; + + let supportsPassive = false; + if (inBrowser) { + try { + const opts = {}; + Object.defineProperty(opts, 'passive', ({ + get () { + /* istanbul ignore next */ + supportsPassive = true; + } + })); // https://github.com/facebook/flow/issues/285 + window.addEventListener('test-passive', null, opts); + } catch (e) {} + } + + // this needs to be lazy-evaled because vue may be required before + // vue-server-renderer can set VUE_ENV + let _isServer; + const isServerRendering = () => { + if (_isServer === undefined) { + /* istanbul ignore if */ + if (!inBrowser && !inWeex && typeof global !== 'undefined') { + // detect presence of vue-server-renderer and avoid + // Webpack shimming the process + _isServer = global['process'] && global['process'].env.VUE_ENV === 'server'; + } else { + _isServer = false; + } + } + return _isServer + }; + + // detect devtools + const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; + + /* istanbul ignore next */ + function isNative (Ctor) { + return typeof Ctor === 'function' && /native code/.test(Ctor.toString()) + } + + const hasSymbol = + typeof Symbol !== 'undefined' && isNative(Symbol) && + typeof Reflect !== 'undefined' && isNative(Reflect.ownKeys); + + let _Set; + /* istanbul ignore if */ // $flow-disable-line + if (typeof Set !== 'undefined' && isNative(Set)) { + // use native Set when available. + _Set = Set; + } else { + // a non-standard Set polyfill that only works with primitive keys. + _Set = class Set { + + constructor () { + this.set = Object.create(null); + } + has (key) { + return this.set[key] === true + } + add (key) { + this.set[key] = true; + } + clear () { + this.set = Object.create(null); + } + }; + } + + /* */ + + let warn = noop; + let tip = noop; + let generateComponentTrace = (noop); // work around flow check + let formatComponentName = (noop); + + { + const hasConsole = typeof console !== 'undefined'; + const classifyRE = /(?:^|[-_])(\w)/g; + const classify = str => str + .replace(classifyRE, c => c.toUpperCase()) + .replace(/[-_]/g, ''); + + warn = (msg, vm) => { + const trace = vm ? generateComponentTrace(vm) : ''; + + if (config.warnHandler) { + config.warnHandler.call(null, msg, vm, trace); + } else if (hasConsole && (!config.silent)) { + console.error(`[Vue warn]: ${msg}${trace}`); + } + }; + + tip = (msg, vm) => { + if (hasConsole && (!config.silent)) { + console.warn(`[Vue tip]: ${msg}` + ( + vm ? generateComponentTrace(vm) : '' + )); + } + }; + + formatComponentName = (vm, includeFile) => { + if (vm.$root === vm) { + return '' + } + const options = typeof vm === 'function' && vm.cid != null + ? vm.options + : vm._isVue + ? vm.$options || vm.constructor.options + : vm; + let name = options.name || options._componentTag; + const file = options.__file; + if (!name && file) { + const match = file.match(/([^/\\]+)\.vue$/); + name = match && match[1]; + } + + return ( + (name ? `<${classify(name)}>` : ``) + + (file && includeFile !== false ? ` at ${file}` : '') + ) + }; + + const repeat = (str, n) => { + let res = ''; + while (n) { + if (n % 2 === 1) res += str; + if (n > 1) str += str; + n >>= 1; + } + return res + }; + + generateComponentTrace = vm => { + if (vm._isVue && vm.$parent) { + const tree = []; + let currentRecursiveSequence = 0; + while (vm) { + if (tree.length > 0) { + const last = tree[tree.length - 1]; + if (last.constructor === vm.constructor) { + currentRecursiveSequence++; + vm = vm.$parent; + continue + } else if (currentRecursiveSequence > 0) { + tree[tree.length - 1] = [last, currentRecursiveSequence]; + currentRecursiveSequence = 0; + } + } + tree.push(vm); + vm = vm.$parent; + } + return '\n\nfound in\n\n' + tree + .map((vm, i) => `${ + i === 0 ? '---> ' : repeat(' ', 5 + i * 2) + }${ + Array.isArray(vm) + ? `${formatComponentName(vm[0])}... (${vm[1]} recursive calls)` + : formatComponentName(vm) + }`) + .join('\n') + } else { + return `\n\n(found in ${formatComponentName(vm)})` + } + }; + } + + /* */ + + let uid = 0; + + /** + * A dep is an observable that can have multiple + * directives subscribing to it. + */ + class Dep { + + + + + constructor () { + this.id = uid++; + this.subs = []; + } + + addSub (sub) { + this.subs.push(sub); + } + + removeSub (sub) { + remove(this.subs, sub); + } + + depend () { + if (Dep.target) { + Dep.target.addDep(this); + } + } + + notify () { + // stabilize the subscriber list first + const subs = this.subs.slice(); + if (!config.async) { + // subs aren't sorted in scheduler if not running async + // we need to sort them now to make sure they fire in correct + // order + subs.sort((a, b) => a.id - b.id); + } + for (let i = 0, l = subs.length; i < l; i++) { + subs[i].update(); + } + } + } + + // The current target watcher being evaluated. + // This is globally unique because only one watcher + // can be evaluated at a time. + Dep.target = null; + const targetStack = []; + + function pushTarget (target) { + targetStack.push(target); + Dep.target = target; + } + + function popTarget () { + targetStack.pop(); + Dep.target = targetStack[targetStack.length - 1]; + } + + /* */ + + class VNode { + + + + + + + // rendered in this component's scope + + + // component instance + // component placeholder node + + // strictly internal + // contains raw HTML? (server only) + // hoisted static node + // necessary for enter transition check + // empty comment placeholder? + // is a cloned node? + // is a v-once node? + // async component factory function + + + + // real context vm for functional nodes + // for SSR caching + // used to store functional render context for devtools + // functional scope id support + + constructor ( + tag, + data, + children, + text, + elm, + context, + componentOptions, + asyncFactory + ) { + this.tag = tag; + this.data = data; + this.children = children; + this.text = text; + this.elm = elm; + this.ns = undefined; + this.context = context; + this.fnContext = undefined; + this.fnOptions = undefined; + this.fnScopeId = undefined; + this.key = data && data.key; + this.componentOptions = componentOptions; + this.componentInstance = undefined; + this.parent = undefined; + this.raw = false; + this.isStatic = false; + this.isRootInsert = true; + this.isComment = false; + this.isCloned = false; + this.isOnce = false; + this.asyncFactory = asyncFactory; + this.asyncMeta = undefined; + this.isAsyncPlaceholder = false; + } + + // DEPRECATED: alias for componentInstance for backwards compat. + /* istanbul ignore next */ + get child () { + return this.componentInstance + } + } + + const createEmptyVNode = (text = '') => { + const node = new VNode(); + node.text = text; + node.isComment = true; + return node + }; + + function createTextVNode (val) { + return new VNode(undefined, undefined, undefined, String(val)) + } + + // optimized shallow clone + // used for static nodes and slot nodes because they may be reused across + // multiple renders, cloning them avoids errors when DOM manipulations rely + // on their elm reference. + function cloneVNode (vnode) { + const cloned = new VNode( + vnode.tag, + vnode.data, + // #7975 + // clone children array to avoid mutating original in case of cloning + // a child. + vnode.children && vnode.children.slice(), + vnode.text, + vnode.elm, + vnode.context, + vnode.componentOptions, + vnode.asyncFactory + ); + cloned.ns = vnode.ns; + cloned.isStatic = vnode.isStatic; + cloned.key = vnode.key; + cloned.isComment = vnode.isComment; + cloned.fnContext = vnode.fnContext; + cloned.fnOptions = vnode.fnOptions; + cloned.fnScopeId = vnode.fnScopeId; + cloned.asyncMeta = vnode.asyncMeta; + cloned.isCloned = true; + return cloned + } + + /* + * not type checking this file because flow doesn't play well with + * dynamically accessing methods on Array prototype + */ + + const arrayProto = Array.prototype; + const arrayMethods = Object.create(arrayProto); + + const methodsToPatch = [ + 'push', + 'pop', + 'shift', + 'unshift', + 'splice', + 'sort', + 'reverse' + ]; + + /** + * Intercept mutating methods and emit events + */ + methodsToPatch.forEach(function (method) { + // cache original method + const original = arrayProto[method]; + def(arrayMethods, method, function mutator (...args) { + const result = original.apply(this, args); + const ob = this.__ob__; + let inserted; + switch (method) { + case 'push': + case 'unshift': + inserted = args; + break + case 'splice': + inserted = args.slice(2); + break + } + if (inserted) ob.observeArray(inserted); + // notify change + ob.dep.notify(); + return result + }); + }); + + /* */ + + const arrayKeys = Object.getOwnPropertyNames(arrayMethods); + + /** + * In some cases we may want to disable observation inside a component's + * update computation. + */ + let shouldObserve = true; + + function toggleObserving (value) { + shouldObserve = value; + } + + /** + * Observer class that is attached to each observed + * object. Once attached, the observer converts the target + * object's property keys into getter/setters that + * collect dependencies and dispatch updates. + */ + class Observer { + + + // number of vms that have this object as root $data + + constructor (value) { + this.value = value; + this.dep = new Dep(); + this.vmCount = 0; + def(value, '__ob__', this); + if (Array.isArray(value)) { + if (hasProto) { + protoAugment(value, arrayMethods); + } else { + copyAugment(value, arrayMethods, arrayKeys); + } + this.observeArray(value); + } else { + this.walk(value); + } + } + + /** + * Walk through all properties and convert them into + * getter/setters. This method should only be called when + * value type is Object. + */ + walk (obj) { + const keys = Object.keys(obj); + for (let i = 0; i < keys.length; i++) { + defineReactive$$1(obj, keys[i]); + } + } + + /** + * Observe a list of Array items. + */ + observeArray (items) { + for (let i = 0, l = items.length; i < l; i++) { + observe(items[i]); + } + } + } + + // helpers + + /** + * Augment a target Object or Array by intercepting + * the prototype chain using __proto__ + */ + function protoAugment (target, src) { + /* eslint-disable no-proto */ + target.__proto__ = src; + /* eslint-enable no-proto */ + } + + /** + * Augment a target Object or Array by defining + * hidden properties. + */ + /* istanbul ignore next */ + function copyAugment (target, src, keys) { + for (let i = 0, l = keys.length; i < l; i++) { + const key = keys[i]; + def(target, key, src[key]); + } + } + + /** + * Attempt to create an observer instance for a value, + * returns the new observer if successfully observed, + * or the existing observer if the value already has one. + */ + function observe (value, asRootData) { + if (!isObject(value) || value instanceof VNode) { + return + } + let ob; + if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { + ob = value.__ob__; + } else if ( + shouldObserve && + !isServerRendering() && + (Array.isArray(value) || isPlainObject(value)) && + Object.isExtensible(value) && + !value._isVue + ) { + ob = new Observer(value); + } + if (asRootData && ob) { + ob.vmCount++; + } + return ob + } + + /** + * Define a reactive property on an Object. + */ + function defineReactive$$1 ( + obj, + key, + val, + customSetter, + shallow + ) { + const dep = new Dep(); + + const property = Object.getOwnPropertyDescriptor(obj, key); + if (property && property.configurable === false) { + return + } + + // cater for pre-defined getter/setters + const getter = property && property.get; + const setter = property && property.set; + if ((!getter || setter) && arguments.length === 2) { + val = obj[key]; + } + + let childOb = !shallow && observe(val); + Object.defineProperty(obj, key, { + enumerable: true, + configurable: true, + get: function reactiveGetter () { + const value = getter ? getter.call(obj) : val; + if (Dep.target) { + dep.depend(); + if (childOb) { + childOb.dep.depend(); + if (Array.isArray(value)) { + dependArray(value); + } + } + } + return value + }, + set: function reactiveSetter (newVal) { + const value = getter ? getter.call(obj) : val; + /* eslint-disable no-self-compare */ + if (newVal === value || (newVal !== newVal && value !== value)) { + return + } + /* eslint-enable no-self-compare */ + if (customSetter) { + customSetter(); + } + // #7981: for accessor properties without setter + if (getter && !setter) return + if (setter) { + setter.call(obj, newVal); + } else { + val = newVal; + } + childOb = !shallow && observe(newVal); + dep.notify(); + } + }); + } + + /** + * Set a property on an object. Adds the new property and + * triggers change notification if the property doesn't + * already exist. + */ + function set (target, key, val) { + if (isUndef(target) || isPrimitive(target) + ) { + warn(`Cannot set reactive property on undefined, null, or primitive value: ${(target)}`); + } + if (Array.isArray(target) && isValidArrayIndex(key)) { + target.length = Math.max(target.length, key); + target.splice(key, 1, val); + return val + } + if (key in target && !(key in Object.prototype)) { + target[key] = val; + return val + } + const ob = (target).__ob__; + if (target._isVue || (ob && ob.vmCount)) { + warn( + 'Avoid adding reactive properties to a Vue instance or its root $data ' + + 'at runtime - declare it upfront in the data option.' + ); + return val + } + if (!ob) { + target[key] = val; + return val + } + defineReactive$$1(ob.value, key, val); + ob.dep.notify(); + return val + } + + /** + * Delete a property and trigger change if necessary. + */ + function del (target, key) { + if (isUndef(target) || isPrimitive(target) + ) { + warn(`Cannot delete reactive property on undefined, null, or primitive value: ${(target)}`); + } + if (Array.isArray(target) && isValidArrayIndex(key)) { + target.splice(key, 1); + return + } + const ob = (target).__ob__; + if (target._isVue || (ob && ob.vmCount)) { + warn( + 'Avoid deleting properties on a Vue instance or its root $data ' + + '- just set it to null.' + ); + return + } + if (!hasOwn(target, key)) { + return + } + delete target[key]; + if (!ob) { + return + } + ob.dep.notify(); + } + + /** + * Collect dependencies on array elements when the array is touched, since + * we cannot intercept array element access like property getters. + */ + function dependArray (value) { + for (let e, i = 0, l = value.length; i < l; i++) { + e = value[i]; + e && e.__ob__ && e.__ob__.dep.depend(); + if (Array.isArray(e)) { + dependArray(e); + } + } + } + + /* */ + + /** + * Option overwriting strategies are functions that handle + * how to merge a parent option value and a child option + * value into the final value. + */ + const strats = config.optionMergeStrategies; + + /** + * Options with restrictions + */ + { + strats.el = strats.propsData = function (parent, child, vm, key) { + if (!vm) { + warn( + `option "${key}" can only be used during instance ` + + 'creation with the `new` keyword.' + ); + } + return defaultStrat(parent, child) + }; + } + + /** + * Helper that recursively merges two data objects together. + */ + function mergeData (to, from) { + if (!from) return to + let key, toVal, fromVal; + + const keys = hasSymbol + ? Reflect.ownKeys(from) + : Object.keys(from); + + for (let i = 0; i < keys.length; i++) { + key = keys[i]; + // in case the object is already observed... + if (key === '__ob__') continue + toVal = to[key]; + fromVal = from[key]; + if (!hasOwn(to, key)) { + set(to, key, fromVal); + } else if ( + toVal !== fromVal && + isPlainObject(toVal) && + isPlainObject(fromVal) + ) { + mergeData(toVal, fromVal); + } + } + return to + } + + /** + * Data + */ + function mergeDataOrFn ( + parentVal, + childVal, + vm + ) { + if (!vm) { + // in a Vue.extend merge, both should be functions + if (!childVal) { + return parentVal + } + if (!parentVal) { + return childVal + } + // when parentVal & childVal are both present, + // we need to return a function that returns the + // merged result of both functions... no need to + // check if parentVal is a function here because + // it has to be a function to pass previous merges. + return function mergedDataFn () { + return mergeData( + typeof childVal === 'function' ? childVal.call(this, this) : childVal, + typeof parentVal === 'function' ? parentVal.call(this, this) : parentVal + ) + } + } else { + return function mergedInstanceDataFn () { + // instance merge + const instanceData = typeof childVal === 'function' + ? childVal.call(vm, vm) + : childVal; + const defaultData = typeof parentVal === 'function' + ? parentVal.call(vm, vm) + : parentVal; + if (instanceData) { + return mergeData(instanceData, defaultData) + } else { + return defaultData + } + } + } + } + + strats.data = function ( + parentVal, + childVal, + vm + ) { + if (!vm) { + if (childVal && typeof childVal !== 'function') { + warn( + 'The "data" option should be a function ' + + 'that returns a per-instance value in component ' + + 'definitions.', + vm + ); + + return parentVal + } + return mergeDataOrFn(parentVal, childVal) + } + + return mergeDataOrFn(parentVal, childVal, vm) + }; + + /** + * Hooks and props are merged as arrays. + */ + function mergeHook ( + parentVal, + childVal + ) { + const res = childVal + ? parentVal + ? parentVal.concat(childVal) + : Array.isArray(childVal) + ? childVal + : [childVal] + : parentVal; + return res + ? dedupeHooks(res) + : res + } + + function dedupeHooks (hooks) { + const res = []; + for (let i = 0; i < hooks.length; i++) { + if (res.indexOf(hooks[i]) === -1) { + res.push(hooks[i]); + } + } + return res + } + + LIFECYCLE_HOOKS.forEach(hook => { + strats[hook] = mergeHook; + }); + + /** + * Assets + * + * When a vm is present (instance creation), we need to do + * a three-way merge between constructor options, instance + * options and parent options. + */ + function mergeAssets ( + parentVal, + childVal, + vm, + key + ) { + const res = Object.create(parentVal || null); + if (childVal) { + assertObjectType(key, childVal, vm); + return extend(res, childVal) + } else { + return res + } + } + + ASSET_TYPES.forEach(function (type) { + strats[type + 's'] = mergeAssets; + }); + + /** + * Watchers. + * + * Watchers hashes should not overwrite one + * another, so we merge them as arrays. + */ + strats.watch = function ( + parentVal, + childVal, + vm, + key + ) { + // work around Firefox's Object.prototype.watch... + if (parentVal === nativeWatch) parentVal = undefined; + if (childVal === nativeWatch) childVal = undefined; + /* istanbul ignore if */ + if (!childVal) return Object.create(parentVal || null) + { + assertObjectType(key, childVal, vm); + } + if (!parentVal) return childVal + const ret = {}; + extend(ret, parentVal); + for (const key in childVal) { + let parent = ret[key]; + const child = childVal[key]; + if (parent && !Array.isArray(parent)) { + parent = [parent]; + } + ret[key] = parent + ? parent.concat(child) + : Array.isArray(child) ? child : [child]; + } + return ret + }; + + /** + * Other object hashes. + */ + strats.props = + strats.methods = + strats.inject = + strats.computed = function ( + parentVal, + childVal, + vm, + key + ) { + if (childVal && "development" !== 'production') { + assertObjectType(key, childVal, vm); + } + if (!parentVal) return childVal + const ret = Object.create(null); + extend(ret, parentVal); + if (childVal) extend(ret, childVal); + return ret + }; + strats.provide = mergeDataOrFn; + + /** + * Default strategy. + */ + const defaultStrat = function (parentVal, childVal) { + return childVal === undefined + ? parentVal + : childVal + }; + + /** + * Validate component names + */ + function checkComponents (options) { + for (const key in options.components) { + validateComponentName(key); + } + } + + function validateComponentName (name) { + if (!new RegExp(`^[a-zA-Z][\\-\\.0-9_${unicodeRegExp.source}]*$`).test(name)) { + warn( + 'Invalid component name: "' + name + '". Component names ' + + 'should conform to valid custom element name in html5 specification.' + ); + } + if (isBuiltInTag(name) || config.isReservedTag(name)) { + warn( + 'Do not use built-in or reserved HTML elements as component ' + + 'id: ' + name + ); + } + } + + /** + * Ensure all props option syntax are normalized into the + * Object-based format. + */ + function normalizeProps (options, vm) { + const props = options.props; + if (!props) return + const res = {}; + let i, val, name; + if (Array.isArray(props)) { + i = props.length; + while (i--) { + val = props[i]; + if (typeof val === 'string') { + name = camelize(val); + res[name] = { type: null }; + } else { + warn('props must be strings when using array syntax.'); + } + } + } else if (isPlainObject(props)) { + for (const key in props) { + val = props[key]; + name = camelize(key); + res[name] = isPlainObject(val) + ? val + : { type: val }; + } + } else { + warn( + `Invalid value for option "props": expected an Array or an Object, ` + + `but got ${toRawType(props)}.`, + vm + ); + } + options.props = res; + } + + /** + * Normalize all injections into Object-based format + */ + function normalizeInject (options, vm) { + const inject = options.inject; + if (!inject) return + const normalized = options.inject = {}; + if (Array.isArray(inject)) { + for (let i = 0; i < inject.length; i++) { + normalized[inject[i]] = { from: inject[i] }; + } + } else if (isPlainObject(inject)) { + for (const key in inject) { + const val = inject[key]; + normalized[key] = isPlainObject(val) + ? extend({ from: key }, val) + : { from: val }; + } + } else { + warn( + `Invalid value for option "inject": expected an Array or an Object, ` + + `but got ${toRawType(inject)}.`, + vm + ); + } + } + + /** + * Normalize raw function directives into object format. + */ + function normalizeDirectives (options) { + const dirs = options.directives; + if (dirs) { + for (const key in dirs) { + const def$$1 = dirs[key]; + if (typeof def$$1 === 'function') { + dirs[key] = { bind: def$$1, update: def$$1 }; + } + } + } + } + + function assertObjectType (name, value, vm) { + if (!isPlainObject(value)) { + warn( + `Invalid value for option "${name}": expected an Object, ` + + `but got ${toRawType(value)}.`, + vm + ); + } + } + + /** + * Merge two option objects into a new one. + * Core utility used in both instantiation and inheritance. + */ + function mergeOptions ( + parent, + child, + vm + ) { + { + checkComponents(child); + } + + if (typeof child === 'function') { + child = child.options; + } + + normalizeProps(child, vm); + normalizeInject(child, vm); + normalizeDirectives(child); + + // Apply extends and mixins on the child options, + // but only if it is a raw options object that isn't + // the result of another mergeOptions call. + // Only merged options has the _base property. + if (!child._base) { + if (child.extends) { + parent = mergeOptions(parent, child.extends, vm); + } + if (child.mixins) { + for (let i = 0, l = child.mixins.length; i < l; i++) { + parent = mergeOptions(parent, child.mixins[i], vm); + } + } + } + + const options = {}; + let key; + for (key in parent) { + mergeField(key); + } + for (key in child) { + if (!hasOwn(parent, key)) { + mergeField(key); + } + } + function mergeField (key) { + const strat = strats[key] || defaultStrat; + options[key] = strat(parent[key], child[key], vm, key); + } + return options + } + + /** + * Resolve an asset. + * This function is used because child instances need access + * to assets defined in its ancestor chain. + */ + function resolveAsset ( + options, + type, + id, + warnMissing + ) { + /* istanbul ignore if */ + if (typeof id !== 'string') { + return + } + const assets = options[type]; + // check local registration variations first + if (hasOwn(assets, id)) return assets[id] + const camelizedId = camelize(id); + if (hasOwn(assets, camelizedId)) return assets[camelizedId] + const PascalCaseId = capitalize(camelizedId); + if (hasOwn(assets, PascalCaseId)) return assets[PascalCaseId] + // fallback to prototype chain + const res = assets[id] || assets[camelizedId] || assets[PascalCaseId]; + if (warnMissing && !res) { + warn( + 'Failed to resolve ' + type.slice(0, -1) + ': ' + id, + options + ); + } + return res + } + + /* */ + + + + function validateProp ( + key, + propOptions, + propsData, + vm + ) { + const prop = propOptions[key]; + const absent = !hasOwn(propsData, key); + let value = propsData[key]; + // boolean casting + const booleanIndex = getTypeIndex(Boolean, prop.type); + if (booleanIndex > -1) { + if (absent && !hasOwn(prop, 'default')) { + value = false; + } else if (value === '' || value === hyphenate(key)) { + // only cast empty string / same name to boolean if + // boolean has higher priority + const stringIndex = getTypeIndex(String, prop.type); + if (stringIndex < 0 || booleanIndex < stringIndex) { + value = true; + } + } + } + // check default value + if (value === undefined) { + value = getPropDefaultValue(vm, prop, key); + // since the default value is a fresh copy, + // make sure to observe it. + const prevShouldObserve = shouldObserve; + toggleObserving(true); + observe(value); + toggleObserving(prevShouldObserve); + } + { + assertProp(prop, key, value, vm, absent); + } + return value + } + + /** + * Get the default value of a prop. + */ + function getPropDefaultValue (vm, prop, key) { + // no default, return undefined + if (!hasOwn(prop, 'default')) { + return undefined + } + const def = prop.default; + // warn against non-factory defaults for Object & Array + if (isObject(def)) { + warn( + 'Invalid default value for prop "' + key + '": ' + + 'Props with type Object/Array must use a factory function ' + + 'to return the default value.', + vm + ); + } + // the raw prop value was also undefined from previous render, + // return previous default value to avoid unnecessary watcher trigger + if (vm && vm.$options.propsData && + vm.$options.propsData[key] === undefined && + vm._props[key] !== undefined + ) { + return vm._props[key] + } + // call factory function for non-Function types + // a value is Function if its prototype is function even across different execution context + return typeof def === 'function' && getType(prop.type) !== 'Function' + ? def.call(vm) + : def + } + + /** + * Assert whether a prop is valid. + */ + function assertProp ( + prop, + name, + value, + vm, + absent + ) { + if (prop.required && absent) { + warn( + 'Missing required prop: "' + name + '"', + vm + ); + return + } + if (value == null && !prop.required) { + return + } + let type = prop.type; + let valid = !type || type === true; + const expectedTypes = []; + if (type) { + if (!Array.isArray(type)) { + type = [type]; + } + for (let i = 0; i < type.length && !valid; i++) { + const assertedType = assertType(value, type[i], vm); + expectedTypes.push(assertedType.expectedType || ''); + valid = assertedType.valid; + } + } + + const haveExpectedTypes = expectedTypes.some(t => t); + if (!valid && haveExpectedTypes) { + warn( + getInvalidTypeMessage(name, value, expectedTypes), + vm + ); + return + } + const validator = prop.validator; + if (validator) { + if (!validator(value)) { + warn( + 'Invalid prop: custom validator check failed for prop "' + name + '".', + vm + ); + } + } + } + + const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol|BigInt)$/; + + function assertType (value, type, vm) { + let valid; + const expectedType = getType(type); + if (simpleCheckRE.test(expectedType)) { + const t = typeof value; + valid = t === expectedType.toLowerCase(); + // for primitive wrapper objects + if (!valid && t === 'object') { + valid = value instanceof type; + } + } else if (expectedType === 'Object') { + valid = isPlainObject(value); + } else if (expectedType === 'Array') { + valid = Array.isArray(value); + } else { + try { + valid = value instanceof type; + } catch (e) { + warn('Invalid prop type: "' + String(type) + '" is not a constructor', vm); + valid = false; + } + } + return { + valid, + expectedType + } + } + + const functionTypeCheckRE = /^\s*function (\w+)/; + + /** + * Use function string name to check built-in types, + * because a simple equality check will fail when running + * across different vms / iframes. + */ + function getType (fn) { + const match = fn && fn.toString().match(functionTypeCheckRE); + return match ? match[1] : '' + } + + function isSameType (a, b) { + return getType(a) === getType(b) + } + + function getTypeIndex (type, expectedTypes) { + if (!Array.isArray(expectedTypes)) { + return isSameType(expectedTypes, type) ? 0 : -1 + } + for (let i = 0, len = expectedTypes.length; i < len; i++) { + if (isSameType(expectedTypes[i], type)) { + return i + } + } + return -1 + } + + function getInvalidTypeMessage (name, value, expectedTypes) { + let message = `Invalid prop: type check failed for prop "${name}".` + + ` Expected ${expectedTypes.map(capitalize).join(', ')}`; + const expectedType = expectedTypes[0]; + const receivedType = toRawType(value); + // check if we need to specify expected value + if ( + expectedTypes.length === 1 && + isExplicable(expectedType) && + isExplicable(typeof value) && + !isBoolean(expectedType, receivedType) + ) { + message += ` with value ${styleValue(value, expectedType)}`; + } + message += `, got ${receivedType} `; + // check if we need to specify received value + if (isExplicable(receivedType)) { + message += `with value ${styleValue(value, receivedType)}.`; + } + return message + } + + function styleValue (value, type) { + if (type === 'String') { + return `"${value}"` + } else if (type === 'Number') { + return `${Number(value)}` + } else { + return `${value}` + } + } + + const EXPLICABLE_TYPES = ['string', 'number', 'boolean']; + function isExplicable (value) { + return EXPLICABLE_TYPES.some(elem => value.toLowerCase() === elem) + } + + function isBoolean (...args) { + return args.some(elem => elem.toLowerCase() === 'boolean') + } + + /* */ + + function handleError (err, vm, info) { + // Deactivate deps tracking while processing error handler to avoid possible infinite rendering. + // See: https://github.com/vuejs/vuex/issues/1505 + pushTarget(); + try { + if (vm) { + let cur = vm; + while ((cur = cur.$parent)) { + const hooks = cur.$options.errorCaptured; + if (hooks) { + for (let i = 0; i < hooks.length; i++) { + try { + const capture = hooks[i].call(cur, err, vm, info) === false; + if (capture) return + } catch (e) { + globalHandleError(e, cur, 'errorCaptured hook'); + } + } + } + } + } + globalHandleError(err, vm, info); + } finally { + popTarget(); + } + } + + function invokeWithErrorHandling ( + handler, + context, + args, + vm, + info + ) { + let res; + try { + res = args ? handler.apply(context, args) : handler.call(context); + if (res && !res._isVue && isPromise(res) && !res._handled) { + res.catch(e => handleError(e, vm, info + ` (Promise/async)`)); + // issue #9511 + // avoid catch triggering multiple times when nested calls + res._handled = true; + } + } catch (e) { + handleError(e, vm, info); + } + return res + } + + function globalHandleError (err, vm, info) { + if (config.errorHandler) { + try { + return config.errorHandler.call(null, err, vm, info) + } catch (e) { + // if the user intentionally throws the original error in the handler, + // do not log it twice + if (e !== err) { + logError(e, null, 'config.errorHandler'); + } + } + } + logError(err, vm, info); + } + + function logError (err, vm, info) { + { + warn(`Error in ${info}: "${err.toString()}"`, vm); + } + /* istanbul ignore else */ + if ((inBrowser || inWeex) && typeof console !== 'undefined') { + console.error(err); + } else { + throw err + } + } + + /* */ + + let isUsingMicroTask = false; + + const callbacks = []; + let pending = false; + + function flushCallbacks () { + pending = false; + const copies = callbacks.slice(0); + callbacks.length = 0; + for (let i = 0; i < copies.length; i++) { + copies[i](); + } + } + + // Here we have async deferring wrappers using microtasks. + // In 2.5 we used (macro) tasks (in combination with microtasks). + // However, it has subtle problems when state is changed right before repaint + // (e.g. #6813, out-in transitions). + // Also, using (macro) tasks in event handler would cause some weird behaviors + // that cannot be circumvented (e.g. #7109, #7153, #7546, #7834, #8109). + // So we now use microtasks everywhere, again. + // A major drawback of this tradeoff is that there are some scenarios + // where microtasks have too high a priority and fire in between supposedly + // sequential events (e.g. #4521, #6690, which have workarounds) + // or even between bubbling of the same event (#6566). + let timerFunc; + + // The nextTick behavior leverages the microtask queue, which can be accessed + // via either native Promise.then or MutationObserver. + // MutationObserver has wider support, however it is seriously bugged in + // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It + // completely stops working after triggering a few times... so, if native + // Promise is available, we will use it: + /* istanbul ignore next, $flow-disable-line */ + if (typeof Promise !== 'undefined' && isNative(Promise)) { + const p = Promise.resolve(); + timerFunc = () => { + p.then(flushCallbacks); + // In problematic UIWebViews, Promise.then doesn't completely break, but + // it can get stuck in a weird state where callbacks are pushed into the + // microtask queue but the queue isn't being flushed, until the browser + // needs to do some other work, e.g. handle a timer. Therefore we can + // "force" the microtask queue to be flushed by adding an empty timer. + if (isIOS) setTimeout(noop); + }; + isUsingMicroTask = true; + } else if (!isIE && typeof MutationObserver !== 'undefined' && ( + isNative(MutationObserver) || + // PhantomJS and iOS 7.x + MutationObserver.toString() === '[object MutationObserverConstructor]' + )) { + // Use MutationObserver where native Promise is not available, + // e.g. PhantomJS, iOS7, Android 4.4 + // (#6466 MutationObserver is unreliable in IE11) + let counter = 1; + const observer = new MutationObserver(flushCallbacks); + const textNode = document.createTextNode(String(counter)); + observer.observe(textNode, { + characterData: true + }); + timerFunc = () => { + counter = (counter + 1) % 2; + textNode.data = String(counter); + }; + isUsingMicroTask = true; + } else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) { + // Fallback to setImmediate. + // Technically it leverages the (macro) task queue, + // but it is still a better choice than setTimeout. + timerFunc = () => { + setImmediate(flushCallbacks); + }; + } else { + // Fallback to setTimeout. + timerFunc = () => { + setTimeout(flushCallbacks, 0); + }; + } + + function nextTick (cb, ctx) { + let _resolve; + callbacks.push(() => { + if (cb) { + try { + cb.call(ctx); + } catch (e) { + handleError(e, ctx, 'nextTick'); + } + } else if (_resolve) { + _resolve(ctx); + } + }); + if (!pending) { + pending = true; + timerFunc(); + } + // $flow-disable-line + if (!cb && typeof Promise !== 'undefined') { + return new Promise(resolve => { + _resolve = resolve; + }) + } + } + + /* */ + + let mark; + let measure; + + { + const perf = inBrowser && window.performance; + /* istanbul ignore if */ + if ( + perf && + perf.mark && + perf.measure && + perf.clearMarks && + perf.clearMeasures + ) { + mark = tag => perf.mark(tag); + measure = (name, startTag, endTag) => { + perf.measure(name, startTag, endTag); + perf.clearMarks(startTag); + perf.clearMarks(endTag); + // perf.clearMeasures(name) + }; + } + } + + /* not type checking this file because flow doesn't play well with Proxy */ + + let initProxy; + + { + const allowedGlobals = makeMap( + 'Infinity,undefined,NaN,isFinite,isNaN,' + + 'parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,' + + 'Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,' + + 'require' // for Webpack/Browserify + ); + + const warnNonPresent = (target, key) => { + warn( + `Property or method "${key}" is not defined on the instance but ` + + 'referenced during render. Make sure that this property is reactive, ' + + 'either in the data option, or for class-based components, by ' + + 'initializing the property. ' + + 'See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.', + target + ); + }; + + const warnReservedPrefix = (target, key) => { + warn( + `Property "${key}" must be accessed with "$data.${key}" because ` + + 'properties starting with "$" or "_" are not proxied in the Vue instance to ' + + 'prevent conflicts with Vue internals. ' + + 'See: https://vuejs.org/v2/api/#data', + target + ); + }; + + const hasProxy = + typeof Proxy !== 'undefined' && isNative(Proxy); + + if (hasProxy) { + const isBuiltInModifier = makeMap('stop,prevent,self,ctrl,shift,alt,meta,exact'); + config.keyCodes = new Proxy(config.keyCodes, { + set (target, key, value) { + if (isBuiltInModifier(key)) { + warn(`Avoid overwriting built-in modifier in config.keyCodes: .${key}`); + return false + } else { + target[key] = value; + return true + } + } + }); + } + + const hasHandler = { + has (target, key) { + const has = key in target; + const isAllowed = allowedGlobals(key) || + (typeof key === 'string' && key.charAt(0) === '_' && !(key in target.$data)); + if (!has && !isAllowed) { + if (key in target.$data) warnReservedPrefix(target, key); + else warnNonPresent(target, key); + } + return has || !isAllowed + } + }; + + const getHandler = { + get (target, key) { + if (typeof key === 'string' && !(key in target)) { + if (key in target.$data) warnReservedPrefix(target, key); + else warnNonPresent(target, key); + } + return target[key] + } + }; + + initProxy = function initProxy (vm) { + if (hasProxy) { + // determine which proxy handler to use + const options = vm.$options; + const handlers = options.render && options.render._withStripped + ? getHandler + : hasHandler; + vm._renderProxy = new Proxy(vm, handlers); + } else { + vm._renderProxy = vm; + } + }; + } + + /* */ + + const seenObjects = new _Set(); + + /** + * Recursively traverse an object to evoke all converted + * getters, so that every nested property inside the object + * is collected as a "deep" dependency. + */ + function traverse (val) { + _traverse(val, seenObjects); + seenObjects.clear(); + } + + function _traverse (val, seen) { + let i, keys; + const isA = Array.isArray(val); + if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) { + return + } + if (val.__ob__) { + const depId = val.__ob__.dep.id; + if (seen.has(depId)) { + return + } + seen.add(depId); + } + if (isA) { + i = val.length; + while (i--) _traverse(val[i], seen); + } else { + keys = Object.keys(val); + i = keys.length; + while (i--) _traverse(val[keys[i]], seen); + } + } + + /* */ + + const normalizeEvent = cached((name) => { + const passive = name.charAt(0) === '&'; + name = passive ? name.slice(1) : name; + const once$$1 = name.charAt(0) === '~'; // Prefixed last, checked first + name = once$$1 ? name.slice(1) : name; + const capture = name.charAt(0) === '!'; + name = capture ? name.slice(1) : name; + return { + name, + once: once$$1, + capture, + passive + } + }); + + function createFnInvoker (fns, vm) { + function invoker () { + const fns = invoker.fns; + if (Array.isArray(fns)) { + const cloned = fns.slice(); + for (let i = 0; i < cloned.length; i++) { + invokeWithErrorHandling(cloned[i], null, arguments, vm, `v-on handler`); + } + } else { + // return handler return value for single handlers + return invokeWithErrorHandling(fns, null, arguments, vm, `v-on handler`) + } + } + invoker.fns = fns; + return invoker + } + + function updateListeners ( + on, + oldOn, + add, + remove$$1, + createOnceHandler, + vm + ) { + let name, cur, old, event; + for (name in on) { + cur = on[name]; + old = oldOn[name]; + event = normalizeEvent(name); + if (isUndef(cur)) { + warn( + `Invalid handler for event "${event.name}": got ` + String(cur), + vm + ); + } else if (isUndef(old)) { + if (isUndef(cur.fns)) { + cur = on[name] = createFnInvoker(cur, vm); + } + if (isTrue(event.once)) { + cur = on[name] = createOnceHandler(event.name, cur, event.capture); + } + add(event.name, cur, event.capture, event.passive, event.params); + } else if (cur !== old) { + old.fns = cur; + on[name] = old; + } + } + for (name in oldOn) { + if (isUndef(on[name])) { + event = normalizeEvent(name); + remove$$1(event.name, oldOn[name], event.capture); + } + } + } + + /* */ + + function mergeVNodeHook (def, hookKey, hook) { + if (def instanceof VNode) { + def = def.data.hook || (def.data.hook = {}); + } + let invoker; + const oldHook = def[hookKey]; + + function wrappedHook () { + hook.apply(this, arguments); + // important: remove merged hook to ensure it's called only once + // and prevent memory leak + remove(invoker.fns, wrappedHook); + } + + if (isUndef(oldHook)) { + // no existing hook + invoker = createFnInvoker([wrappedHook]); + } else { + /* istanbul ignore if */ + if (isDef(oldHook.fns) && isTrue(oldHook.merged)) { + // already a merged invoker + invoker = oldHook; + invoker.fns.push(wrappedHook); + } else { + // existing plain hook + invoker = createFnInvoker([oldHook, wrappedHook]); + } + } + + invoker.merged = true; + def[hookKey] = invoker; + } + + /* */ + + function extractPropsFromVNodeData ( + data, + Ctor, + tag + ) { + // we are only extracting raw values here. + // validation and default values are handled in the child + // component itself. + const propOptions = Ctor.options.props; + if (isUndef(propOptions)) { + return + } + const res = {}; + const { attrs, props } = data; + if (isDef(attrs) || isDef(props)) { + for (const key in propOptions) { + const altKey = hyphenate(key); + { + const keyInLowerCase = key.toLowerCase(); + if ( + key !== keyInLowerCase && + attrs && hasOwn(attrs, keyInLowerCase) + ) { + tip( + `Prop "${keyInLowerCase}" is passed to component ` + + `${formatComponentName(tag || Ctor)}, but the declared prop name is` + + ` "${key}". ` + + `Note that HTML attributes are case-insensitive and camelCased ` + + `props need to use their kebab-case equivalents when using in-DOM ` + + `templates. You should probably use "${altKey}" instead of "${key}".` + ); + } + } + checkProp(res, props, key, altKey, true) || + checkProp(res, attrs, key, altKey, false); + } + } + return res + } + + function checkProp ( + res, + hash, + key, + altKey, + preserve + ) { + if (isDef(hash)) { + if (hasOwn(hash, key)) { + res[key] = hash[key]; + if (!preserve) { + delete hash[key]; + } + return true + } else if (hasOwn(hash, altKey)) { + res[key] = hash[altKey]; + if (!preserve) { + delete hash[altKey]; + } + return true + } + } + return false + } + + /* */ + + // The template compiler attempts to minimize the need for normalization by + // statically analyzing the template at compile time. + // + // For plain HTML markup, normalization can be completely skipped because the + // generated render function is guaranteed to return Array. There are + // two cases where extra normalization is needed: + + // 1. When the children contains components - because a functional component + // may return an Array instead of a single root. In this case, just a simple + // normalization is needed - if any child is an Array, we flatten the whole + // thing with Array.prototype.concat. It is guaranteed to be only 1-level deep + // because functional components already normalize their own children. + function simpleNormalizeChildren (children) { + for (let i = 0; i < children.length; i++) { + if (Array.isArray(children[i])) { + return Array.prototype.concat.apply([], children) + } + } + return children + } + + // 2. When the children contains constructs that always generated nested Arrays, + // e.g.