UNPKG

sajari-website

Version:

Website extensions for the Sajari API. Automatically index site content, add user profiles, render search and recommendations, etc.

296 lines (274 loc) 9.98 kB
/* * dom.js - Handles DOM manipulation, reading, writing, etc */ /** * Query constructor */ function dom(opts) { opts = opts || {}; if (opts.prefix) { this.prefix = opts.prefix; } this.nodes = {}; if (opts.nodes) { this.nodes = opts.nodes; } this.targets = {}; if (opts.targets) { this.targets = opts.targets; } } dom.prototype = { /** * Bind to a DOM event */ bind : function(node, eventType, handler) { if (node.addEventListener) { node.addEventListener(eventType, handler, false); } else if (node.attachEvent) { // IE8/9 compatibility if (eventType == 'input') { eventType = 'onpropertychange'; } node.attachEvent('on' + eventType, function (e) { handler.apply(node, [e]); }); } }, /** * Retrieves content from a document meta tag */ getMeta : function(metaName) { var i, metas = document.getElementsByTagName('meta'); for (i = 0; i < metas.length; i++) { if (metas[i].getAttribute('name') == metaName) { return metas[i].getAttribute('content'); } } return ''; }, /** * Imports a CSS file to the page */ importCss : function(path) { var css = document.createElement('link') css.setAttribute('rel', 'stylesheet'); css.setAttribute('type', 'text/css'); css.setAttribute('href', path); document.getElementsByTagName('head')[0].appendChild(css); }, /** * Return the html entitised version of a text string */ entitise : function(text) { var div = document.createElement('div'); div.appendChild(document.createTextNode(text)); return div.innerHTML; }, /** * Propagates data parameters from the 'from' node to the 'to' node */ copyDynamicProperties : function(from, to) { if ((from !== undefined) && (to !== undefined)) { var attrs = this.dynamicAttrs(from); if (typeof attrs === 'object') { for (var key in attrs) { if (attrs.hasOwnProperty(key)) { // Propagate relevant data parameters from the query input to the results div to.setAttribute(this.prefix+key, attrs[key]); } } } } }, /** * Try to get all the target elements from the DOM * Polyfill for browsers that don't have getElementsByAttribute */ scanShim : function(scope) { // Try to use the built in if it is supported if (document.getElementsByAttribute !== undefined) { for (var i = 0; i < this.targets.length; i++) { elements = document.getElementsByAttribute(this.prefix + this.targets[i], '*'); if (elements.length > 0) { dom.nodes[this.targets[i]] = elements; } } return } // Not supported, run the polyfill instead if (scope.nodeType === 1) { for (var i = 0; i < this.targets.length; i++) { if (scope.hasAttribute(this.prefix + this.targets[i])) { if (this.nodes[this.targets[i]] === undefined) { this.nodes[this.targets[i]] = []; } this.nodes[this.targets[i]].push(scope); } } if (scope.childNodes !== undefined) { for (var j = 0; j < scope.childNodes.length; j++) { this.scanShim(scope.childNodes[j], this.targets, this.nodes); } } } }, /** * Returns the value of a this.prefix attribute or the default value */ attrVal : function(node, attr, defaultVal) { if (node.hasAttribute(this.prefix+attr)) { return node.getAttribute(this.prefix+attr); } else { return defaultVal; } }, /** * Returns an object of the dynamic attributes (e.g. custom parameters to send with the request) * Optionally add a prefix to get only specific params */ dynamicAttrs : function(node, prefixSuff) { if (prefixSuff === undefined) { prefixSuff = ""; } var m, attrs = {}; for (var i in node.attributes) { var attrib = node.attributes[i]; if (attrib && attrib.specified) { var reg = new RegExp(this.prefix + prefixSuff + "(.+)$", "g"); m = reg.exec(attrib.name); if (m && this.targets.indexOf(m[1]) === -1) { attrs[m[1]] = attrib.value; } } } return attrs; }, /** * Returns all the meta and data-sj-meta params from the DOM. Not the most * efficient function but stuck with this for now and it only runs once. * * Now also supports data-sj-field based meta. Priority is based on the "priority" * array in the function below then falls back to the innerText value * Sample tests: * <meta data-sj-field="sj-field-content" content="content" /> * <meta data-sj-field="sj-field-name" name="name" /> * <meta data-sj-field="sj-field-and-value" data-sj-value="value" /> */ dynamicMeta : function(node, attrs) { if (attrs === undefined) { attrs = {}; } // Standard meta first var metas = document.getElementsByTagName('meta'); for (var i=0; i<metas.length; i++) { var prop = metas[i].getAttribute("property"); var name = metas[i].getAttribute("name"); var content = metas[i].getAttribute("content"); if (name != "" && name != null) { attrs[name] = content; } else if (prop != "" && prop != null) { attrs[prop] = content; } } // Scan for sj meta var all = document.getElementsByTagName("*"); for (var i=0, max=all.length; i < max; i++) { // data-sj-meta var matches = this.dynamicAttrs(all[i], 'meta-'); for (m in matches) { if (matches[m] !== undefined &&matches[m] !== "") { attrs[m] = matches[m]; continue; } if (all[i].content !== undefined && all[i].content !== "") { attrs[m] = all[i].content; continue; } if (all[i].innerText !== undefined &&all[i].innerText !== "") { attrs[m] = all[i].innerText; } } // data-sj-field var field = all[i].getAttribute(this.prefix+"field"); if (field !== undefined && field !== null) { var priority = [this.prefix + 'value', 'name', 'property', 'content']; for (var j = 0; j < priority.length; j++) { var val = all[i].getAttribute(priority[j]); if (val !== undefined && val !== null) { if (val !== "") { if (typeof attrs[field] === 'undefined') { attrs[field] = val; } else if (typeof attrs[field] === 'object') { attrs[field].push(val); } else if (typeof attrs[field] === 'string') { var old = attrs[field]; attrs[field] = [old, val]; } break } } } if (all[i].innerText !== undefined &&all[i].innerText !== "") { attrs[field] = all[i].innerText; } } } return attrs }, /** * Returns the URI parameters of dynamic this.prefix attributes */ dynamicAttrsUri : function(node) { var uri = [], attrs = this.dynamicAttrs(node); for (var i in attrs) { uri.push(encodeURIComponent(i)+'='+encodeURIComponent(attrs[i])); } return uri.join('&'); }, /** * Call the callback for each node with the given attribute. * First argument to the callback is the node, second is the attribute we looked for. */ eachNode : function(attr, callback) { if (this.hasNode(attr)) { for (var i = 0; i < this.nodes[attr].length; i++) { callback(this.nodes[attr][i], this.prefix + attr); } } }, /** * Return the first node for the given attribute, or undefined if there isn't one. */ hasNode : function(attr) { return (this.nodes[attr] !== undefined && this.nodes[attr].length > 0); }, /** * Return the first node for the given attribute, or undefined if there isn't one. */ firstNode : function(attr) { if (!this.hasNode(attr)) { return undefined; } return this.nodes[attr][0]; }, /** * Return the last node for the given attribute, or undefined if there isn't one. */ lastNode : function(attr) { if (!this.hasNode(attr)) { return undefined; } return this.nodes[attr][this.nodes[attr].length]; }, /** * Return the attribute value of the first node for the given attribute. * Returns undefined if there is no node for that attribute. */ firstNodeVal : function(attr, defaultVal) { var node = this.firstNode(attr); return (node !== undefined ? node.getAttribute(this.prefix + attr) : defaultVal); } } module.exports = dom;