UNPKG

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
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