/**
* The jQuery reference
* @external jQuery
* @property {external:jQuery-Deferred} Deferred
* @see {@link https://api.jquery.com/category/selectors/|jQuery}
*/
/**
* The jQuery Deferred reference
* @external jQuery-Deferred
* @see {@link https://api.jquery.com/category/deferred-object/|jQuery.Deferred}
*/
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(['underscore', 'backbone'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('underscore'), require('backbone'));
} else {
root.Torso = root.Torso || {};
root.Torso.Utils = root.Torso.Utils || {};
root.Torso.Utils.templateRenderer = factory(root._, root.Backbone);
}
}(this, function(_, Backbone) {
'use strict';
var $ = Backbone.$;
/**
* Changes DOM Nodes that are different, and leaves others untouched.
*
* Algorithm:
* Delegates to a particular swapMethod, depending on the Node type.
* Recurses for nested Element Nodes only.
* There is always room for optimizing this method.
*
* @memberof templateRenderer
* @param {Node} currentNode The DOM Node corresponding to the existing page content to update
* @param {Node} newNode The detached DOM Node representing the desired DOM subtree
* @param {Array} ignoreElements Array of jQuery selectors for DOM Elements to ignore during render. Can be an expensive check.
*/
function hotswap(currentNode, newNode, ignoreElements) {
var newNodeType = newNode.nodeType,
currentNodeType = currentNode.nodeType,
swapMethod;
if(newNodeType !== currentNodeType) {
$(currentNode).replaceWith(newNode);
} else {
swapMethod = swapMethods[newNodeType] || swapMethods['default'];
swapMethod(currentNode, newNode, ignoreElements);
}
}
/**
* Stickit will rely on the 'stickit-bind-val' jQuery data attribute to determine the value to use for a given option.
* If the value DOM attribute is not the same as the stickit-bind-val, then this will clear the jquery data attribute
* so that stickit will use the value DOM attribute of the option. This happens when templateRenderer merges
* the attributes of the newNode into a current node of the same type when the current node has the stickit-bind-val
* jQuery data attribute set.
*
* If the node value is not set, then the stickit-bind-val might be how the view is communicating the value for stickit to use
* (possibly in the case of non-string values). In this case trust the stickit-bind-val.
*
* @param {Node} node the DoM element to test and fix the stickit data on.
*/
function cleanupStickitData(node) {
var $node = $(node);
var stickitValue = $node.data('stickit-bind-val');
if (node.tagName === 'OPTION' && node.value !== undefined && stickitValue !== node.value) {
$node.removeData('stickit-bind-val');
}
}
/*
* Swap method for Element Nodes
* @param {Element} currentNode The pre-existing DOM Element to update
* @param {Element} newNode The detached DOM Element representing the desired DOM Element subtree
* @param {Array} ignoreElements Array of jQuery selectors for DOM Elements to ignore during render. Can be an expensive check.
*/
function swapElementNodes(currentNode, newNode, ignoreElements) {
var currentAttr, shouldIgnore, $currChildNodes, $newChildNodes, currentAttributes,
$currentNode = $(currentNode),
$newNode = $(newNode),
idx = 0;
shouldIgnore = _.some(ignoreElements, function(selector) {
return $currentNode.is(selector);
});
if (shouldIgnore) {
return;
}