UNPKG

@roderickhsiao/react-i13n

Version:

[Experiment] React I13n provides a performant and scalable solution to application instrumentation.

394 lines (305 loc) 10.8 kB
"use strict"; exports.__esModule = true; exports["default"] = void 0; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /** * Copyright 2015 - Present, Yahoo Inc. * Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var TAG_PATTERN = /<[^>]*>/g; var getDOMText = function getDOMText(DOMNode) { if (DOMNode === void 0) { DOMNode = {}; } var _ref = DOMNode || {}, value = _ref.value, innerText = _ref.innerText, textContent = _ref.textContent, innerHTML = _ref.innerHTML; return value || innerText || textContent || innerHTML; }; /** * I13nNode the virtual DOM Node used to build a I13n Tree for instrumentation * @class I13nNode * @param {Object} parentNode parent node * @param {Object|Function} model custom model values * @param {Boolean} isLeafNode indicate if it's a link node * @param {Boolean} isViewportEnabled indicate if viewport check enable * @constructor */ var I13nNode = function I13nNode(parentNode, model, isLeafNode, isViewportEnabled) { this._parentNode = parentNode; if (this._parentNode) { this._parentNode.appendChildNode(this); } // we allow users to pass in a function which generate the dynamic model data if (typeof model === 'function') { this._model = model; } else { this._model = _objectSpread({}, model); } this._childrenNodes = []; // children nodes this._DOMNode = null; // DOM node of the i13n node, will use setDOMNode to set it in componentDidMount // any custom value want to set in the i13n node, can used to save some status in the handler functions this._customAttributes = {}; // _isLeafNode indicate if it's a leaf node or not, // e.g., an anchor or button. it's used to help users to know if they want to handle it for some cases, // e.g., when we want to track links this._isLeafNode = isLeafNode || false; // _isOrderDirty used to check if we need to sort children nodes before we get position of one of it child // will set to true if we already sort them // set to false once we add/remove a child this._isOrderDirty = false; // _isInViewport save the status if node is already shown in the viewport, // if viewport check isn't enabled, then always set to true this._isInViewport = !isViewportEnabled; }; /** * Append a child node * @method appendChildNode * @param {Object} childNode the child node */ I13nNode.prototype.appendChildNode = function appendChildNode(childNode) { this._childrenNodes.push(childNode); this._isOrderDirty = true; }; /** * Get the children array * @method getChildrenNodes * @return {Array} the children array */ I13nNode.prototype.getChildrenNodes = function getChildrenNodes() { return this._childrenNodes; }; /** * Get custom attribute value * @method getCustomAttribute * @param {String} name attribute name * @return {Boolean|Number|String|Array|Object} the attribute value */ I13nNode.prototype.getCustomAttribute = function getCustomAttribute(name) { return this._customAttributes[name]; }; /** * Get the react component * @method getReactComponent * @return {Object} the react component */ I13nNode.prototype.getReactComponent = function getReactComponent() { return this._component; }; /** * Get the dom node * @method getDOMNode * @return {Object} the DOMNode */ I13nNode.prototype.getDOMNode = function getDOMNode() { return this._DOMNode; }; /** * Get merged model which is traced to the root * @method getMergedModel * @param {Boolean} debugMode indicate it's debug mode, will return additional information for debug tool * @return {Object} the merged model */ I13nNode.prototype.getMergedModel = function getMergedModel(debugMode) { if (this._parentNode) { var parentModel = this._parentNode.getMergedModel(debugMode); return _objectSpread(_objectSpread({}, parentModel), this.getModel(debugMode)); } return this.getModel(debugMode); }; /** * Get plain model object, from either dynamic function or plain object * @method getModel * @param {Boolean} debugMode indicate it's debug mode, will return additional information for debug tool * @return {Object} the plain object model */ I13nNode.prototype.getModel = function getModel(debugMode) { var model = null; var finalModel = null; if (typeof this._model === 'function') { model = this._model(); } else { model = this._model; } // always return new object to prevent reference issue finalModel = _objectSpread({}, model); if (debugMode) { var DOMNode = this.getDOMNode(); // add the DOMNode to the returned model, so that it can be used in debug tool Object.keys(finalModel).forEach(function (index) { finalModel[index] = { value: finalModel[index], DOMNode: DOMNode }; }); } return finalModel; }; /** * Get the parent node * @method getParentNode * @return {Object} the parent node */ I13nNode.prototype.getParentNode = function getParentNode() { return this._parentNode; }; /** * Get the position of its parent * @method getPosition * @return {Number} the position */ I13nNode.prototype.getPosition = function getPosition() { var parentNode = this._parentNode; if (!parentNode) { return 1; } if (parentNode.isOrderDirty()) { parentNode.sortChildrenNodes(); } return parentNode.getChildrenNodes().indexOf(this) + 1; }; /** * Get text of the i13nNode * @method getText * @param {Object} target the event target, would take the target's text then i13n node's text * @return {String} text of the node */ I13nNode.prototype.getText = function getText(target) { var DOMNode = this.getDOMNode(); if (!DOMNode && !target) { return ''; } var text = getDOMText(target) || getDOMText(DOMNode); if (text) { text = text.replace(TAG_PATTERN, ''); } return text; }; /** * Get isLeafNode value * @method isLeafNode * @return {Boolean} the isLeafNode value */ I13nNode.prototype.isLeafNode = function isLeafNode() { return this._isLeafNode; }; /** * Get isOrderDirty value, it become dirty once the children array changed * @method isOrderDirty * @return {Boolean} the isOrderDirty value */ I13nNode.prototype.isOrderDirty = function isOrderDirty() { return this._isOrderDirty; }; /** * Get isInViewport value * @method isInViewport * @return {Boolean} the isInViewport value */ I13nNode.prototype.isInViewport = function isInViewport() { return this._isInViewport; }; /** * Recursively traverse nodes * @method traverseNodes * @param {Function} handler function */ I13nNode.prototype.traverseNodes = function traverseNodes(handler) { handler && handler(this); this._childrenNodes.forEach(function (child) { child.traverseNodes(handler); }); return this; }; /** * Remove child node * @param {Object} childNode child node * @method removeChildNode */ I13nNode.prototype.removeChildNode = function removeChildNode(childNode) { var index = this._childrenNodes.indexOf(childNode); this._childrenNodes.splice(index, 1); this._isOrderDirty = true; }; /** * set react component * @method setReactComponent * @param {Object} react component */ I13nNode.prototype.setReactComponent = function setReactComponent(component) { this._component = component; }; /** * Update DOM node * @method setDOMNode * @param {Object} DOMNode */ I13nNode.prototype.setDOMNode = function setDOMNode(DOMNode) { this._DOMNode = DOMNode; }; /** * Set isInViewport value * @method setIsInViewport * @param {Boolean} isInViewport */ I13nNode.prototype.setIsInViewport = function setIsInViewport(isInViewport) { this._isInViewport = isInViewport; }; /** * Set custom attribute * @method setCustomAttribute * @param {String} name attribute name * @param {Boolean|Number|String|Array|Object} value attribute value, can be any types */ I13nNode.prototype.setCustomAttribute = function setCustomAttribute(name, value) { this._customAttributes[name] = value; }; /** * Set the parent node, this method provide you the ability to update I13n Tree dynamically * @method setParentNode * @param {Object} parentNode the parent node */ I13nNode.prototype.setParentNode = function setParentNode(parentNode) { this._parentNode = parentNode; }; /** * Update the i13n model * @method updateModel * @param {Object|Function} newModel the new i13n model */ I13nNode.prototype.updateModel = function updateModel(newModel) { // if i13n is a function, just assign it to _model, otherwise use Object.assign to merge old and new model data if (typeof newModel === 'function') { this._model = newModel; } else { this._model = _objectSpread(_objectSpread({}, this._model), newModel); } }; /** * Sort children according to the position in the page * @method sortChildrenNodes * @param {Boolean} propagate indicate if want to propagate the sorting event to its parent */ I13nNode.prototype.sortChildrenNodes = function sortChildrenNodes(propagate) { this._childrenNodes.sort(function (childA, childB) { var domA = childA.getDOMNode(); var domB = childB.getDOMNode(); if (domA && domB) { if (domB.compareDocumentPosition) { var comparison = domB.compareDocumentPosition(domA); if (comparison & Node.DOCUMENT_POSITION_PRECEDING) { // eslint-disable-line no-bitwise return -1; } } else if (domB.sourceIndex) { // IE 8 return domA.sourceIndex - domB.sourceIndex; } } return 1; }); this._isOrderDirty = false; if (this._parentNode && propagate) { this._parentNode.sortChildrenNodes(propagate); } }; var _default = I13nNode; exports["default"] = _default;