app-decorators
Version:
Collection of useful ES7 Decorators, writtin in ES6, that can be used for building webapps
651 lines (543 loc) • 25.8 kB
JavaScript
System.register(['app-decorators/src/libs/element-to-function'], function (_export, _context) {
"use strict";
var _elementToFunc, _slicedToArray, _createClass, Stylesheet;
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
return {
setters: [function (_appDecoratorsSrcLibsElementToFunction) {
_elementToFunc = _appDecoratorsSrcLibsElementToFunction.default;
}],
execute: function () {
_slicedToArray = function () {
function sliceIterator(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"]) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
return function (arr, i) {
if (Array.isArray(arr)) {
return arr;
} else if (Symbol.iterator in Object(arr)) {
return sliceIterator(arr, i);
} else {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
};
}();
_createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
_export('Stylesheet', Stylesheet = function () {
/**
* @Ideas:
* - implement "containment: strict" for avoiding repaint, recalc, etc
* - https://developers.google.com/web/updates/2016/06/css-containment
*/
function Stylesheet() {
var config = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
_classCallCheck(this, Stylesheet);
this._refs = {
appendTo: null,
stylesElement: null,
scope: null
};
this._attachOn = 'load';
this._styles = '';
this._imports = [];
this._type = 'on';
this._removeEvent = true;
this._eventFactory = function (scope) {};
this._onLoadImports = null;
this._order = undefined;
this._fallback = false;
this._attached = false;
this._event = 'attached';
this._eventStatePattern = [/DOMContentLoaded|load/, 'on'];
this._stateSettings = {
DOMContentLoaded: 'interactive',
load: 'complete'
};
this.alreadyDone = false;
this.init(config);
}
/**
* @type {WeakMap}
*/
/**
* e.g. (created, attached, DOMContentLoaded, load)
* @type {string}
* @private
*/
/**
* @type {string}
* @private
*/
/**
* @type {Array}
* @private
*/
/**
* @type {string}
* @private
*/
/**
* will remove event after node appended
* @type {boolean}
* @private
*/
/**
* _eventFactory
* @type {object}
* @private
*/
/**
* @private
*/
/**
* @type {undefined}
* @private
*/
/**
* @type {boolean}
* @private
*/
/**
* @type {boolean}
* @private
*/
/**
* @type {string}
* @private
*/
/**
* @type {Array}
* @private
*/
/**
* @type {{DOMContentLoaded: string, load: string}}
* @private
*/
/**
* Holds the value of whether the registered event (attachOn) is reached or not.
* @type {boolean}
*/
_createClass(Stylesheet, [{
key: 'init',
value: function init() {
var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
appendTo = _ref.appendTo,
styles = _ref.styles,
attachOn = _ref.attachOn,
imports = _ref.imports,
type = _ref.type,
eventFactory = _ref.eventFactory,
removeEvent = _ref.removeEvent,
onLoadImports = _ref.onLoadImports,
order = _ref.order,
alreadyDone = _ref.alreadyDone,
fallback = _ref.fallback;
if (!appendTo) {
throw new Error('Required: appendTo');
}
if (!styles && !imports) {
throw new Error('Required: styles or imports');
}
this.alreadyDone = alreadyDone;
this._checkElement(appendTo);
this._initRefs();
// init props
this._attachOn = attachOn || this._attachOn;
this._appendTo = appendTo;
this._styles = styles;
this._imports = imports || [];
this._type = type || this._type;
this._eventFactory = eventFactory;
this._onLoadImports = onLoadImports || this._onLoadImports;
this._removeEvent = typeof removeEvent === 'boolean' ? removeEvent : this._removeEvent;
this._fallback = typeof fallback === 'boolean' ? fallback : this._fallback;
this._order = order >= 0 ? order : this._order;
this._initScope();
this._eventListener = this._eventFactory(this._scope);
this._run(styles, imports);
}
}, {
key: 'reinit',
value: function reinit(element) {
this._checkElement(element);
this._initRefs();
this._appendTo = element;
this._initScope();
this._eventListener = this._eventFactory(this._scope);
this._run(this._styles, this._imports);
}
}, {
key: 'initialized',
value: function initialized() {
return this._refs.has(this);
}
}, {
key: 'destroy',
value: function destroy() {
if (!this._refs.has(this)) {
return null;
}
this._cleanup();
this._refs.delete(this);
}
}, {
key: 'cleanup',
value: function cleanup() {
this._cleanup();
}
}, {
key: '_checkElement',
value: function _checkElement(element) {
if (!(element instanceof HTMLElement)) {
throw new Error('Passed appendTo element should be instance of HTMLElement');
}
}
}, {
key: '_run',
value: function _run(styles, imports) {
if (this._isAlreadyDone(this._attachOn)) {
this._runProcess(styles, imports);
return;
}
// listen event
this._addEventListener(this._attachOn, styles, imports);
}
}, {
key: '_getEventName',
value: function _getEventName() {
var value = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "";
var collection = value.split(' ');
return collection[0];
}
}, {
key: '_addEventListener',
value: function _addEventListener(attachOn, styles, imports) {
var _this = this;
this._eventListener.on(attachOn, function () {
_this._processListener(styles, imports);
_this.alreadyDone = true;
// event reached
// Remove listener after stylesheet is appended.
// We dont want multiple style elements on multiple events
var event = _this._getEventName(attachOn);
_this._removeEvent && _this._eventListener.off(event);
});
}
}, {
key: '_isAlreadyDone',
value: function _isAlreadyDone(state) {
// when the an event e.g. click, foo, bar is already fulfilled.
if (this.alreadyDone) {
return this.alreadyDone;
}
// fire immediately no matter what status is reached
if (this._attachOn === 'immediately') {
return this.alreadyDone = true;
}
// fire when load (complete) event already reached but we declared as DOMContentLoaded
if (this._getDocumentReadyState() === 'complete' && state === 'DOMContentLoaded') {
return this.alreadyDone = true;
}
return this.alreadyDone = this._getDocumentReadyState() === this._stateSettings[state];
}
}, {
key: '_initScope',
value: function _initScope() {
var _eventStatePattern = _slicedToArray(this._eventStatePattern, 2),
attachOn = _eventStatePattern[0],
type = _eventStatePattern[1];
if (attachOn.test(this._attachOn) && type === this._type) {
this._scope = window;
} else {
this._scope = this._appendTo;
}
}
}, {
key: '_getDocumentReadyState',
value: function _getDocumentReadyState() {
return document.readyState;
}
}, {
key: '_runProcess',
value: function _runProcess(styles) {
var imports = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var element = null;
if (styles) {
this._stylesElement = this._createStylesheetNode(styles);
element = this._insertStylesheetNode(this._appendTo, this._stylesElement);
}
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = imports[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var href = _step.value;
if (this._supportRelPreload()) {
this._stylesElement = this._createLinkRelPreloadNode(href);
} else {
this._stylesElement = this._createLinkRelStylesheetNode(href);
}
element = this._insertStylesheetNode(this._appendTo, this._stylesElement);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
this._trigger(this._event);
return element;
}
}, {
key: '_processListener',
value: function _processListener(styles, imports) {
this._runProcess(styles, imports);
}
}, {
key: '_cleanup',
value: function _cleanup() {
if (!this._processListener) {
return;
}
this._eventListener.off(this._attachOn);
// remove rendered styles and link
var elements = this._appendTo.querySelectorAll('link,style');
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = (elements.length && elements || [])[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var element = _step2.value;
element.parentElement.removeChild(element);
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
}
}, {
key: '_insertStylesheetNode',
value: function _insertStylesheetNode(appendTo, stylesElement) {
this._addOrderClass(this._order);
var _ref2 = [],
node = _ref2[0];
var styleQuery = '.style-order-' + this._order;
var styleOrderNodes = appendTo.querySelectorAll(styleQuery);
if (this._order >= 0 && styleOrderNodes.length) {
node = this._lastItem(styleOrderNodes);
} else {
node = this._lastItem(appendTo.children);
}
if (node) {
if (/style|link/.test(node.localName.toLowerCase())) {
node.after(stylesElement);
} else {
node.before(stylesElement);
}
return appendTo;
}
appendTo.appendChild(stylesElement);
return appendTo;
}
}, {
key: '_createStylesheetNode',
value: function _createStylesheetNode() {
var styles = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var element = document.createElement('style');
// Support for IE
if (element.styleSheet) {
element.styleSheet.cssText = this._styles;
}
// Support for the rest
else {
element.appendChild(document.createTextNode(styles));
}
return element;
}
}, {
key: '_createLinkRelPreloadNode',
value: function _createLinkRelPreloadNode(href) {
var element = this._createElement('div');
element.innerHTML = '<link rel="preload" ' + 'as="style" ' + 'href="' + href + '" ' + 'onload="' + 'this.rel=\'stylesheet\';' + 'this.__event = new CustomEvent(\'load:stylesheet\', { bubbles: true });' + 'this.dispatchEvent(this.__event)"' + '>';
return element.querySelector('link');
}
}, {
key: '_createLinkRelStylesheetNode',
value: function _createLinkRelStylesheetNode(href) {
var attachOn = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'all';
var self = this;
var linkElement = this._createElement('link');
var event = new CustomEvent('load:stylesheet', { bubbles: true, detail: this });
linkElement.rel = 'stylesheet';
linkElement.href = href;
// temporarily set media to something inapplicable to ensure it'll fetch without blocking render
// see: https://github.com/filamentgroup/loadCSS/blob/master/src/loadCSS.js#L25
linkElement.media = "only x";
linkElement.onload = function () {
var self2 = this;
if (!self._onLoadImports) {
self2.media = '' + attachOn;
linkElement.dispatchEvent(event);
return;
}
self._onLoadImports(function call(done) {
self2.media = '' + attachOn;
done ? done() : null;
linkElement.dispatchEvent(event);
}, self2);
};
return linkElement;
}
}, {
key: '_initRefs',
value: function _initRefs() {
this._refs = new WeakMap([[this, new Map([['appendTo', null], ['scope', null], ['eventListener', null]])]]);
}
}, {
key: '_trigger',
value: function _trigger(eventName) {
this._attached = true;
var event = new CustomEvent(eventName, {
bubbles: true,
detail: {
type: 'stylesheet'
}
});
this._appendTo.dispatchEvent(event);
}
}, {
key: '_createElement',
value: function _createElement(type) {
return document.createElement(type);
}
}, {
key: '_lastItem',
value: function _lastItem(items) {
return items[items.length - 1];
}
}, {
key: '_addOrderClass',
value: function _addOrderClass() {
var order = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
if (order >= 0) {
this._stylesElement.classList.add('style-order-' + order);
}
}
}, {
key: 'supportRelPreload',
value: function supportRelPreload() {
return this._supportRelPreload();
}
}, {
key: '_supportRelPreload',
value: function _supportRelPreload() {
if (this._fallback) {
return false;
}
var hasPreload = false;
try {
hasPreload = window.document.createElement("link").relList.supports("preload");
} catch (e) {
hasPreload = false;
}
return hasPreload;
}
}, {
key: '_appendTo',
set: function set(appendTo) {
this._refs.get(this).set('appendTo', appendTo);
},
get: function get() {
var _result = this._refs.get(this);
return _result && this._refs.get(this).get('appendTo');
}
}, {
key: '_stylesElement',
set: function set(stylesElement) {
this._refs.get(this).set('stylesElement', stylesElement);
},
get: function get() {
var _result = this._refs.get(this);
return _result && this._refs.get(this).get('stylesElement');
}
}, {
key: '_scope',
set: function set(element) {
this._refs.get(this).set('scope', element);
},
get: function get() {
var _result = this._refs.get(this);
return _result && this._refs.get(this).get('scope');
}
}, {
key: '_eventListener',
set: function set(listener) {
this._refs.get(this).set('eventListener', listener);
},
get: function get() {
var _result = this._refs.get(this);
return _result && this._refs.get(this).get('eventListener');
}
}]);
return Stylesheet;
}());
_export('Stylesheet', Stylesheet);
}
};
});
//# sourceMappingURL=stylesheet.js.map