| 'use strict'; | 
|   | 
| var cssSelect = require('css-select'); | 
|   | 
| var svgoCssSelectAdapter = require('./css-select-adapter'); | 
| var cssSelectOpts = { | 
|   xmlMode: true, | 
|   adapter: svgoCssSelectAdapter | 
| }; | 
|   | 
| var JSAPI = module.exports = function(data, parentNode) { | 
|     Object.assign(this, data); | 
|     if (parentNode) { | 
|         Object.defineProperty(this, 'parentNode', { | 
|             writable: true, | 
|             value: parentNode | 
|         }); | 
|     } | 
| }; | 
|   | 
| /** | 
|  * Perform a deep clone of this node. | 
|  * | 
|  * @return {Object} element | 
|  */ | 
| JSAPI.prototype.clone = function() { | 
|     var node = this; | 
|     var nodeData = {}; | 
|   | 
|     Object.keys(node).forEach(function(key) { | 
|         if (key !== 'class' && key !== 'style' && key !== 'content') { | 
|             nodeData[key] = node[key]; | 
|         } | 
|     }); | 
|   | 
|     // Deep-clone node data. | 
|     nodeData = JSON.parse(JSON.stringify(nodeData)); | 
|   | 
|     // parentNode gets set to a proper object by the parent clone, | 
|     // but it needs to be true/false now to do the right thing | 
|     // in the constructor. | 
|     var clonedNode = new JSAPI(nodeData, !!node.parentNode); | 
|   | 
|     if (node.class) { | 
|         clonedNode.class = node.class.clone(clonedNode); | 
|     } | 
|     if (node.style) { | 
|         clonedNode.style = node.style.clone(clonedNode); | 
|     } | 
|     if (node.content) { | 
|         clonedNode.content = node.content.map(function(childNode) { | 
|             var clonedChild = childNode.clone(); | 
|             clonedChild.parentNode = clonedNode; | 
|             return clonedChild; | 
|         }); | 
|     } | 
|   | 
|     return clonedNode; | 
| }; | 
|   | 
| /** | 
|  * Determine if item is an element | 
|  * (any, with a specific name or in a names array). | 
|  * | 
|  * @param {String|Array} [param] element name or names arrays | 
|  * @return {Boolean} | 
|  */ | 
| JSAPI.prototype.isElem = function(param) { | 
|   | 
|     if (!param) return !!this.elem; | 
|   | 
|     if (Array.isArray(param)) return !!this.elem && (param.indexOf(this.elem) > -1); | 
|   | 
|     return !!this.elem && this.elem === param; | 
|   | 
| }; | 
|   | 
| /** | 
|  * Renames an element | 
|  * | 
|  * @param {String} name new element name | 
|  * @return {Object} element | 
|  */ | 
| JSAPI.prototype.renameElem = function(name) { | 
|   | 
|     if (name && typeof name === 'string') | 
|         this.elem = this.local = name; | 
|   | 
|     return this; | 
|   | 
| }; | 
|   | 
| /** | 
|  * Determine if element is empty. | 
|  * | 
|  * @return {Boolean} | 
|  */ | 
|  JSAPI.prototype.isEmpty = function() { | 
|   | 
|     return !this.content || !this.content.length; | 
|   | 
| }; | 
|   | 
| /** | 
|  * Find the closest ancestor of the current element. | 
|  * @param elemName | 
|  * | 
|  * @return {?Object} | 
|  */ | 
|  JSAPI.prototype.closestElem = function(elemName) { | 
|     var elem = this; | 
|   | 
|     while ((elem = elem.parentNode) && !elem.isElem(elemName)); | 
|   | 
|     return elem; | 
| }; | 
|   | 
| /** | 
|  * Changes content by removing elements and/or adding new elements. | 
|  * | 
|  * @param {Number} start Index at which to start changing the content. | 
|  * @param {Number} n Number of elements to remove. | 
|  * @param {Array|Object} [insertion] Elements to add to the content. | 
|  * @return {Array} Removed elements. | 
|  */ | 
|  JSAPI.prototype.spliceContent = function(start, n, insertion) { | 
|   | 
|     if (arguments.length < 2) return []; | 
|   | 
|     if (!Array.isArray(insertion)) | 
|         insertion = Array.apply(null, arguments).slice(2); | 
|   | 
|     insertion.forEach(function(inner) { inner.parentNode = this }, this); | 
|   | 
|     return this.content.splice.apply(this.content, [start, n].concat(insertion)); | 
|   | 
|   | 
| }; | 
|   | 
| /** | 
|  * Determine if element has an attribute | 
|  * (any, or by name or by name + value). | 
|  * | 
|  * @param {String} [name] attribute name | 
|  * @param {String} [val] attribute value (will be toString()'ed) | 
|  * @return {Boolean} | 
|  */ | 
|  JSAPI.prototype.hasAttr = function(name, val) { | 
|   | 
|     if (!this.attrs || !Object.keys(this.attrs).length) return false; | 
|   | 
|     if (!arguments.length) return !!this.attrs; | 
|   | 
|     if (val !== undefined) return !!this.attrs[name] && this.attrs[name].value === val.toString(); | 
|   | 
|     return !!this.attrs[name]; | 
|   | 
| }; | 
|   | 
| /** | 
|  * Determine if element has an attribute by local name | 
|  * (any, or by name or by name + value). | 
|  * | 
|  * @param {String} [localName] local attribute name | 
|  * @param {Number|String|RegExp|Function} [val] attribute value (will be toString()'ed or executed, otherwise ignored) | 
|  * @return {Boolean} | 
|  */ | 
|  JSAPI.prototype.hasAttrLocal = function(localName, val) { | 
|   | 
|     if (!this.attrs || !Object.keys(this.attrs).length) return false; | 
|   | 
|     if (!arguments.length) return !!this.attrs; | 
|   | 
|     var callback; | 
|   | 
|     switch (val != null && val.constructor && val.constructor.name) { | 
|         case 'Number':   // same as String | 
|         case 'String':   callback = stringValueTest; break; | 
|         case 'RegExp':   callback = regexpValueTest; break; | 
|         case 'Function': callback = funcValueTest; break; | 
|         default:         callback = nameTest; | 
|     } | 
|     return this.someAttr(callback); | 
|   | 
|     function nameTest(attr) { | 
|         return attr.local === localName; | 
|     } | 
|   | 
|     function stringValueTest(attr) { | 
|         return attr.local === localName && val == attr.value; | 
|     } | 
|   | 
|     function regexpValueTest(attr) { | 
|         return attr.local === localName && val.test(attr.value); | 
|     } | 
|   | 
|     function funcValueTest(attr) { | 
|         return attr.local === localName && val(attr.value); | 
|     } | 
|   | 
| }; | 
|   | 
| /** | 
|  * Get a specific attribute from an element | 
|  * (by name or name + value). | 
|  * | 
|  * @param {String} name attribute name | 
|  * @param {String} [val] attribute value (will be toString()'ed) | 
|  * @return {Object|Undefined} | 
|  */ | 
|  JSAPI.prototype.attr = function(name, val) { | 
|   | 
|     if (!this.hasAttr() || !arguments.length) return undefined; | 
|   | 
|     if (val !== undefined) return this.hasAttr(name, val) ? this.attrs[name] : undefined; | 
|   | 
|     return this.attrs[name]; | 
|   | 
| }; | 
|   | 
| /** | 
|  * Get computed attribute value from an element | 
|  * | 
|  * @param {String} name attribute name | 
|  * @return {Object|Undefined} | 
|  */ | 
|  JSAPI.prototype.computedAttr = function(name, val) { | 
|     /* jshint eqnull: true */ | 
|     if (!arguments.length) return; | 
|   | 
|     for (var elem = this; elem && (!elem.hasAttr(name) || !elem.attr(name).value); elem = elem.parentNode); | 
|   | 
|     if (val != null) { | 
|         return elem ? elem.hasAttr(name, val) : false; | 
|     } else if (elem && elem.hasAttr(name)) { | 
|         return elem.attrs[name].value; | 
|     } | 
|   | 
| }; | 
|   | 
| /** | 
|  * Remove a specific attribute. | 
|  * | 
|  * @param {String|Array} name attribute name | 
|  * @param {String} [val] attribute value | 
|  * @return {Boolean} | 
|  */ | 
|  JSAPI.prototype.removeAttr = function(name, val, recursive) { | 
|   | 
|     if (!arguments.length) return false; | 
|   | 
|     if (Array.isArray(name)) { | 
|         name.forEach(this.removeAttr, this); | 
|         return false; | 
|     } | 
|   | 
|     if (!this.hasAttr(name)) return false; | 
|   | 
|     if (!recursive && val && this.attrs[name].value !== val) return false; | 
|   | 
|     delete this.attrs[name]; | 
|   | 
|     if (!Object.keys(this.attrs).length) delete this.attrs; | 
|   | 
|     return true; | 
|   | 
| }; | 
|   | 
| /** | 
|  * Add attribute. | 
|  * | 
|  * @param {Object} [attr={}] attribute object | 
|  * @return {Object|Boolean} created attribute or false if no attr was passed in | 
|  */ | 
|  JSAPI.prototype.addAttr = function(attr) { | 
|     attr = attr || {}; | 
|   | 
|     if (attr.name === undefined || | 
|         attr.prefix === undefined || | 
|         attr.local === undefined | 
|     ) return false; | 
|   | 
|     this.attrs = this.attrs || {}; | 
|     this.attrs[attr.name] = attr; | 
|   | 
|     if(attr.name === 'class') { // newly added class attribute | 
|         this.class.hasClass(); | 
|     } | 
|   | 
|     if(attr.name === 'style') { // newly added style attribute | 
|         this.style.hasStyle(); | 
|     } | 
|   | 
|     return this.attrs[attr.name]; | 
|   | 
| }; | 
|   | 
| /** | 
|  * Iterates over all attributes. | 
|  * | 
|  * @param {Function} callback callback | 
|  * @param {Object} [context] callback context | 
|  * @return {Boolean} false if there are no any attributes | 
|  */ | 
|  JSAPI.prototype.eachAttr = function(callback, context) { | 
|   | 
|     if (!this.hasAttr()) return false; | 
|   | 
|     for (var name in this.attrs) { | 
|         callback.call(context, this.attrs[name]); | 
|     } | 
|   | 
|     return true; | 
|   | 
| }; | 
|   | 
| /** | 
|  * Tests whether some attribute passes the test. | 
|  * | 
|  * @param {Function} callback callback | 
|  * @param {Object} [context] callback context | 
|  * @return {Boolean} false if there are no any attributes | 
|  */ | 
|  JSAPI.prototype.someAttr = function(callback, context) { | 
|   | 
|     if (!this.hasAttr()) return false; | 
|   | 
|     for (var name in this.attrs) { | 
|         if (callback.call(context, this.attrs[name])) return true; | 
|     } | 
|   | 
|     return false; | 
|   | 
| }; | 
|   | 
| /** | 
|  * Evaluate a string of CSS selectors against the element and returns matched elements. | 
|  * | 
|  * @param {String} selectors CSS selector(s) string | 
|  * @return {Array} null if no elements matched | 
|  */ | 
|  JSAPI.prototype.querySelectorAll = function(selectors) { | 
|   | 
|    var matchedEls = cssSelect(selectors, this, cssSelectOpts); | 
|   | 
|    return matchedEls.length > 0 ? matchedEls : null; | 
|   | 
| }; | 
|   | 
| /** | 
|  * Evaluate a string of CSS selectors against the element and returns only the first matched element. | 
|  * | 
|  * @param {String} selectors CSS selector(s) string | 
|  * @return {Array} null if no element matched | 
|  */ | 
|  JSAPI.prototype.querySelector = function(selectors) { | 
|   | 
|    return cssSelect.selectOne(selectors, this, cssSelectOpts); | 
|   | 
| }; | 
|   | 
| /** | 
|  * Test if a selector matches a given element. | 
|  * | 
|  * @param {String} selector CSS selector string | 
|  * @return {Boolean} true if element would be selected by selector string, false if it does not | 
|  */ | 
|  JSAPI.prototype.matches = function(selector) { | 
|   | 
|    return cssSelect.is(this, selector, cssSelectOpts); | 
|   | 
| }; |