You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
480 lines
14 KiB
480 lines
14 KiB
|
3 years ago
|
(function umd(root, factory) {
|
||
|
|
if (typeof module === 'object' && typeof exports === 'object')
|
||
|
|
module.exports = factory()
|
||
|
|
else if (typeof define === 'function' && define.amd)
|
||
|
|
define([], factory)
|
||
|
|
else
|
||
|
|
root.httpVueLoader = factory()
|
||
|
|
})(this, function factory() {
|
||
|
|
'use strict';
|
||
|
|
|
||
|
|
var scopeIndex = 0;
|
||
|
|
|
||
|
|
StyleContext.prototype = {
|
||
|
|
|
||
|
|
withBase: function (callback) {
|
||
|
|
|
||
|
|
var tmpBaseElt;
|
||
|
|
if (this.component.baseURI) {
|
||
|
|
|
||
|
|
// firefox and chrome need the <base> to be set while inserting or modifying <style> in a document.
|
||
|
|
tmpBaseElt = document.createElement('base');
|
||
|
|
tmpBaseElt.href = this.component.baseURI;
|
||
|
|
|
||
|
|
var headElt = this.component.getHead();
|
||
|
|
headElt.insertBefore(tmpBaseElt, headElt.firstChild);
|
||
|
|
}
|
||
|
|
|
||
|
|
callback.call(this);
|
||
|
|
|
||
|
|
if (tmpBaseElt)
|
||
|
|
this.component.getHead().removeChild(tmpBaseElt);
|
||
|
|
},
|
||
|
|
|
||
|
|
scopeStyles: function (styleElt, scopeName) {
|
||
|
|
|
||
|
|
function process() {
|
||
|
|
|
||
|
|
var sheet = styleElt.sheet;
|
||
|
|
var rules = sheet.cssRules;
|
||
|
|
|
||
|
|
for (var i = 0; i < rules.length; ++i) {
|
||
|
|
|
||
|
|
var rule = rules[i];
|
||
|
|
if (rule.type !== 1)
|
||
|
|
continue;
|
||
|
|
|
||
|
|
var scopedSelectors = [];
|
||
|
|
|
||
|
|
rule.selectorText.split(/\s*,\s*/).forEach(function (sel) {
|
||
|
|
|
||
|
|
scopedSelectors.push(scopeName + ' ' + sel);
|
||
|
|
var segments = sel.match(/([^ :]+)(.+)?/);
|
||
|
|
scopedSelectors.push(segments[1] + scopeName + (segments[2] || ''));
|
||
|
|
});
|
||
|
|
|
||
|
|
var scopedRule = scopedSelectors.join(',') + rule.cssText.substr(rule.selectorText.length);
|
||
|
|
sheet.deleteRule(i);
|
||
|
|
sheet.insertRule(scopedRule, i);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
// firefox may fail sheet.cssRules with InvalidAccessError
|
||
|
|
process();
|
||
|
|
} catch (ex) {
|
||
|
|
|
||
|
|
if (ex instanceof DOMException && ex.code === DOMException.INVALID_ACCESS_ERR) {
|
||
|
|
|
||
|
|
styleElt.sheet.disabled = true;
|
||
|
|
styleElt.addEventListener('load', function onStyleLoaded() {
|
||
|
|
|
||
|
|
styleElt.removeEventListener('load', onStyleLoaded);
|
||
|
|
|
||
|
|
// firefox need this timeout otherwise we have to use document.importNode(style, true)
|
||
|
|
setTimeout(function () {
|
||
|
|
|
||
|
|
process();
|
||
|
|
styleElt.sheet.disabled = false;
|
||
|
|
});
|
||
|
|
});
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
throw ex;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
compile: function () {
|
||
|
|
|
||
|
|
var hasTemplate = this.template !== null;
|
||
|
|
|
||
|
|
var scoped = this.elt.hasAttribute('scoped');
|
||
|
|
|
||
|
|
if (scoped) {
|
||
|
|
|
||
|
|
// no template, no scopable style needed
|
||
|
|
if (!hasTemplate)
|
||
|
|
return;
|
||
|
|
|
||
|
|
// firefox does not tolerate this attribute
|
||
|
|
this.elt.removeAttribute('scoped');
|
||
|
|
}
|
||
|
|
|
||
|
|
this.withBase(function () {
|
||
|
|
|
||
|
|
this.component.getHead().appendChild(this.elt);
|
||
|
|
});
|
||
|
|
|
||
|
|
if (scoped)
|
||
|
|
this.scopeStyles(this.elt, '[' + this.component.getScopeId() + ']');
|
||
|
|
|
||
|
|
return Promise.resolve();
|
||
|
|
},
|
||
|
|
|
||
|
|
getContent: function () {
|
||
|
|
|
||
|
|
return this.elt.textContent;
|
||
|
|
},
|
||
|
|
|
||
|
|
setContent: function (content) {
|
||
|
|
|
||
|
|
this.withBase(function () {
|
||
|
|
|
||
|
|
this.elt.textContent = content;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
function StyleContext(component, elt) {
|
||
|
|
|
||
|
|
this.component = component;
|
||
|
|
this.elt = elt;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
ScriptContext.prototype = {
|
||
|
|
|
||
|
|
getContent: function () {
|
||
|
|
|
||
|
|
return this.elt.textContent;
|
||
|
|
},
|
||
|
|
|
||
|
|
setContent: function (content) {
|
||
|
|
|
||
|
|
this.elt.textContent = content;
|
||
|
|
},
|
||
|
|
|
||
|
|
compile: function (module) {
|
||
|
|
|
||
|
|
var childModuleRequire = function (childURL) {
|
||
|
|
|
||
|
|
return httpVueLoader.require(resolveURL(this.component.baseURI, childURL));
|
||
|
|
}.bind(this);
|
||
|
|
|
||
|
|
var childLoader = function (childURL, childName) {
|
||
|
|
|
||
|
|
return httpVueLoader(resolveURL(this.component.baseURI, childURL), childName);
|
||
|
|
}.bind(this);
|
||
|
|
|
||
|
|
try {
|
||
|
|
Function('exports', 'require', 'httpVueLoader', 'module', this.getContent()).call(this.module.exports, this.module.exports, childModuleRequire, childLoader, this.module);
|
||
|
|
} catch (ex) {
|
||
|
|
|
||
|
|
if (!('lineNumber' in ex)) {
|
||
|
|
|
||
|
|
return Promise.reject(ex);
|
||
|
|
}
|
||
|
|
var vueFileData = responseText.replace(/\r?\n/g, '\n');
|
||
|
|
var lineNumber = vueFileData.substr(0, vueFileData.indexOf(script)).split('\n').length + ex.lineNumber - 1;
|
||
|
|
throw new (ex.constructor)(ex.message, url, lineNumber);
|
||
|
|
}
|
||
|
|
|
||
|
|
return Promise.resolve(this.module.exports)
|
||
|
|
.then(httpVueLoader.scriptExportsHandler.bind(this))
|
||
|
|
.then(function (exports) {
|
||
|
|
|
||
|
|
this.module.exports = exports;
|
||
|
|
}.bind(this));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
function ScriptContext(component, elt) {
|
||
|
|
|
||
|
|
this.component = component;
|
||
|
|
this.elt = elt;
|
||
|
|
this.module = {exports: {}};
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
TemplateContext.prototype = {
|
||
|
|
|
||
|
|
getContent: function () {
|
||
|
|
|
||
|
|
return this.elt.innerHTML;
|
||
|
|
},
|
||
|
|
|
||
|
|
setContent: function (content) {
|
||
|
|
|
||
|
|
this.elt.innerHTML = content;
|
||
|
|
},
|
||
|
|
|
||
|
|
getRootElt: function () {
|
||
|
|
|
||
|
|
var tplElt = this.elt.content || this.elt;
|
||
|
|
|
||
|
|
if ('firstElementChild' in tplElt)
|
||
|
|
return tplElt.firstElementChild;
|
||
|
|
|
||
|
|
for (tplElt = tplElt.firstChild; tplElt !== null; tplElt = tplElt.nextSibling)
|
||
|
|
if (tplElt.nodeType === Node.ELEMENT_NODE)
|
||
|
|
return tplElt;
|
||
|
|
|
||
|
|
return null;
|
||
|
|
},
|
||
|
|
|
||
|
|
compile: function () {
|
||
|
|
|
||
|
|
return Promise.resolve();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
function TemplateContext(component, elt) {
|
||
|
|
|
||
|
|
this.component = component;
|
||
|
|
this.elt = elt;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
Component.prototype = {
|
||
|
|
|
||
|
|
getHead: function () {
|
||
|
|
|
||
|
|
return document.head || document.getElementsByTagName('head')[0];
|
||
|
|
},
|
||
|
|
|
||
|
|
getScopeId: function () {
|
||
|
|
|
||
|
|
if (this._scopeId === '') {
|
||
|
|
|
||
|
|
this._scopeId = 'data-s-' + (scopeIndex++).toString(36);
|
||
|
|
this.template.getRootElt().setAttribute(this._scopeId, '');
|
||
|
|
}
|
||
|
|
return this._scopeId;
|
||
|
|
},
|
||
|
|
|
||
|
|
load: function (componentURL) {
|
||
|
|
|
||
|
|
return httpVueLoader.httpRequest(componentURL)
|
||
|
|
.then(function (responseText) {
|
||
|
|
|
||
|
|
this.baseURI = componentURL.substr(0, componentURL.lastIndexOf('/') + 1);
|
||
|
|
var doc = document.implementation.createHTMLDocument('');
|
||
|
|
|
||
|
|
// IE requires the <base> to come with <style>
|
||
|
|
doc.body.innerHTML = (this.baseURI ? '<base href="' + this.baseURI + '">' : '') + responseText;
|
||
|
|
|
||
|
|
for (var it = doc.body.firstChild; it; it = it.nextSibling) {
|
||
|
|
|
||
|
|
switch (it.nodeName) {
|
||
|
|
case 'TEMPLATE':
|
||
|
|
this.template = new TemplateContext(this, it);
|
||
|
|
break;
|
||
|
|
case 'SCRIPT':
|
||
|
|
this.script = new ScriptContext(this, it);
|
||
|
|
break;
|
||
|
|
case 'STYLE':
|
||
|
|
this.styles.push(new StyleContext(this, it));
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return this;
|
||
|
|
}.bind(this));
|
||
|
|
},
|
||
|
|
|
||
|
|
_normalizeSection: function (eltCx) {
|
||
|
|
|
||
|
|
var p;
|
||
|
|
|
||
|
|
if (eltCx === null || !eltCx.elt.hasAttribute('src')) {
|
||
|
|
|
||
|
|
p = Promise.resolve(null);
|
||
|
|
} else {
|
||
|
|
|
||
|
|
p = httpVueLoader.httpRequest(eltCx.elt.getAttribute('src'))
|
||
|
|
.then(function (content) {
|
||
|
|
|
||
|
|
eltCx.elt.removeAttribute('src');
|
||
|
|
return content;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
return p
|
||
|
|
.then(function (content) {
|
||
|
|
|
||
|
|
if (eltCx !== null && eltCx.elt.hasAttribute('lang')) {
|
||
|
|
|
||
|
|
var lang = eltCx.elt.getAttribute('lang');
|
||
|
|
eltCx.elt.removeAttribute('lang');
|
||
|
|
return httpVueLoader.langProcessor[lang.toLowerCase()].call(this, content === null ? eltCx.getContent() : content);
|
||
|
|
}
|
||
|
|
return content;
|
||
|
|
}.bind(this))
|
||
|
|
.then(function (content) {
|
||
|
|
|
||
|
|
if (content !== null)
|
||
|
|
eltCx.setContent(content);
|
||
|
|
});
|
||
|
|
},
|
||
|
|
|
||
|
|
normalize: function () {
|
||
|
|
|
||
|
|
return Promise.all(Array.prototype.concat(
|
||
|
|
this._normalizeSection(this.template),
|
||
|
|
this._normalizeSection(this.script),
|
||
|
|
this.styles.map(this._normalizeSection)
|
||
|
|
))
|
||
|
|
.then(function () {
|
||
|
|
|
||
|
|
return this;
|
||
|
|
}.bind(this));
|
||
|
|
},
|
||
|
|
|
||
|
|
compile: function () {
|
||
|
|
|
||
|
|
return Promise.all(Array.prototype.concat(
|
||
|
|
this.template && this.template.compile(),
|
||
|
|
this.script && this.script.compile(),
|
||
|
|
this.styles.map(function (style) {
|
||
|
|
return style.compile();
|
||
|
|
})
|
||
|
|
))
|
||
|
|
.then(function () {
|
||
|
|
|
||
|
|
return this;
|
||
|
|
}.bind(this));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
function Component(name) {
|
||
|
|
|
||
|
|
this.name = name;
|
||
|
|
this.template = null;
|
||
|
|
this.script = null;
|
||
|
|
this.styles = [];
|
||
|
|
this._scopeId = '';
|
||
|
|
}
|
||
|
|
|
||
|
|
function identity(value) {
|
||
|
|
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
function parseComponentURL(url) {
|
||
|
|
|
||
|
|
var comp = url.match(/(.*?)([^/]+?)\/?(\.vue)?(\?.*|#.*|$)/);
|
||
|
|
return {
|
||
|
|
name: comp[2],
|
||
|
|
url: comp[1] + comp[2] + (comp[3] === undefined ? '/index.vue' : comp[3]) + comp[4]
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function resolveURL(baseURL, url) {
|
||
|
|
|
||
|
|
if (url.substr(0, 2) === './' || url.substr(0, 3) === '../') {
|
||
|
|
return baseURL + url;
|
||
|
|
}
|
||
|
|
return url;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
httpVueLoader.load = function (url, name) {
|
||
|
|
|
||
|
|
return function () {
|
||
|
|
|
||
|
|
return new Component(name).load(url)
|
||
|
|
.then(function (component) {
|
||
|
|
|
||
|
|
return component.normalize();
|
||
|
|
})
|
||
|
|
.then(function (component) {
|
||
|
|
|
||
|
|
return component.compile();
|
||
|
|
})
|
||
|
|
.then(function (component) {
|
||
|
|
|
||
|
|
var exports = component.script !== null ? component.script.module.exports : {};
|
||
|
|
|
||
|
|
if (component.template !== null)
|
||
|
|
exports.template = component.template.getContent();
|
||
|
|
|
||
|
|
if (exports.name === undefined)
|
||
|
|
if (component.name !== undefined)
|
||
|
|
exports.name = component.name;
|
||
|
|
|
||
|
|
exports._baseURI = component.baseURI;
|
||
|
|
|
||
|
|
return exports;
|
||
|
|
});
|
||
|
|
};
|
||
|
|
};
|
||
|
|
|
||
|
|
|
||
|
|
httpVueLoader.register = function (Vue, url) {
|
||
|
|
|
||
|
|
var comp = parseComponentURL(url);
|
||
|
|
Vue.component(comp.name, httpVueLoader.load(comp.url));
|
||
|
|
};
|
||
|
|
|
||
|
|
httpVueLoader.install = function (Vue) {
|
||
|
|
|
||
|
|
Vue.mixin({
|
||
|
|
|
||
|
|
beforeCreate: function () {
|
||
|
|
|
||
|
|
var components = this.$options.components;
|
||
|
|
|
||
|
|
for (var componentName in components) {
|
||
|
|
|
||
|
|
if (typeof (components[componentName]) === 'string' && components[componentName].substr(0, 4) === 'url:') {
|
||
|
|
|
||
|
|
var comp = parseComponentURL(components[componentName].substr(4));
|
||
|
|
|
||
|
|
var componentURL = ('_baseURI' in this.$options) ? resolveURL(this.$options._baseURI, comp.url) : comp.url;
|
||
|
|
|
||
|
|
if (isNaN(componentName))
|
||
|
|
components[componentName] = httpVueLoader.load(componentURL, componentName);
|
||
|
|
else
|
||
|
|
components[componentName] = Vue.component(comp.name, httpVueLoader.load(componentURL, comp.name));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
httpVueLoader.require = function (moduleName) {
|
||
|
|
|
||
|
|
return window[moduleName];
|
||
|
|
};
|
||
|
|
|
||
|
|
httpVueLoader.httpRequest = function (url) {
|
||
|
|
|
||
|
|
return new Promise(function (resolve, reject) {
|
||
|
|
|
||
|
|
var xhr = new XMLHttpRequest();
|
||
|
|
xhr.open('GET', url);
|
||
|
|
xhr.responseType = 'text';
|
||
|
|
|
||
|
|
xhr.onreadystatechange = function () {
|
||
|
|
|
||
|
|
if (xhr.readyState === 4) {
|
||
|
|
|
||
|
|
if (xhr.status >= 200 && xhr.status < 300)
|
||
|
|
resolve(xhr.responseText);
|
||
|
|
else
|
||
|
|
reject(xhr.status);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
xhr.send(null);
|
||
|
|
});
|
||
|
|
};
|
||
|
|
|
||
|
|
httpVueLoader.langProcessor = {
|
||
|
|
html: identity,
|
||
|
|
js: identity,
|
||
|
|
css: identity
|
||
|
|
};
|
||
|
|
|
||
|
|
httpVueLoader.scriptExportsHandler = identity;
|
||
|
|
|
||
|
|
function httpVueLoader(url, name) {
|
||
|
|
|
||
|
|
var comp = parseComponentURL(url);
|
||
|
|
return httpVueLoader.load(comp.url, name);
|
||
|
|
}
|
||
|
|
|
||
|
|
return httpVueLoader;
|
||
|
|
});
|