mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-03-18 07:50:22 +00:00
rewrite of JQuery lite implementation, which now better supports selected sets
This commit is contained in:
parent
ef4bb28be1
commit
00cc9eb32a
7 changed files with 657 additions and 255 deletions
|
|
@ -1,6 +1,10 @@
|
||||||
<a name="0.9.12"><a/>
|
<a name="0.9.12"><a/>
|
||||||
# <angular/> 0.9.12 thought-implanter (in-progress) #
|
# <angular/> 0.9.12 thought-implanter (in-progress) #
|
||||||
|
|
||||||
|
### API
|
||||||
|
- rewrite of JQuery lite implementation for better supports operations on multiple nodes when
|
||||||
|
matched by a selector.
|
||||||
|
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
- Removed the $init() method after the compilation. The old way of compiling the DOM element was
|
- Removed the $init() method after the compilation. The old way of compiling the DOM element was
|
||||||
angular.compile(element).$init(); The $init was there to allow the users to do any work to the
|
angular.compile(element).$init(); The $init was there to allow the users to do any work to the
|
||||||
|
|
|
||||||
|
|
@ -268,24 +268,6 @@ function extensionMap(angular, name, transform) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function jqLiteWrap(element) {
|
|
||||||
// for some reasons the parentNode of an orphan looks like _null but its typeof is object.
|
|
||||||
if (element) {
|
|
||||||
if (isString(element)) {
|
|
||||||
var div = document.createElement('div');
|
|
||||||
// Read about the NoScope elements here:
|
|
||||||
// http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx
|
|
||||||
div.innerHTML = '<div> </div>' + element; // IE insanity to make NoScope elements work!
|
|
||||||
div.removeChild(div.firstChild); // remove the superfluous div
|
|
||||||
element = new JQLite(div.childNodes);
|
|
||||||
} else if (!(element instanceof JQLite)) {
|
|
||||||
element = new JQLite(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return element;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @workInProgress
|
* @workInProgress
|
||||||
* @ngdoc function
|
* @ngdoc function
|
||||||
|
|
@ -422,7 +404,9 @@ function isBoolean(value) { return typeof value == $boolean;}
|
||||||
function isTextNode(node) { return nodeName_(node) == '#text'; }
|
function isTextNode(node) { return nodeName_(node) == '#text'; }
|
||||||
function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; }
|
function trim(value) { return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; }
|
||||||
function isElement(node) {
|
function isElement(node) {
|
||||||
return node && (node.nodeName || node instanceof JQLite || (jQuery && node instanceof jQuery));
|
return node &&
|
||||||
|
(node.nodeName // we are a direct element
|
||||||
|
|| (node.bind && node.find)); // we have a bind and find method part of jQuery API
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1057,11 +1041,11 @@ function bindJQuery(){
|
||||||
// bind to jQuery if present;
|
// bind to jQuery if present;
|
||||||
jQuery = window.jQuery;
|
jQuery = window.jQuery;
|
||||||
// reset to jQuery or default to us.
|
// reset to jQuery or default to us.
|
||||||
if (window.jQuery) {
|
if (jQuery) {
|
||||||
jqLite = window.jQuery;
|
jqLite = jQuery;
|
||||||
extend(jqLite.fn, {
|
extend(jQuery.fn, {
|
||||||
scope: JQLite.prototype.scope,
|
scope: JQLitePrototype.scope,
|
||||||
cloneNode: cloneNode
|
cloneNode: JQLitePrototype.cloneNode
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
jqLite = jqLiteWrap;
|
jqLite = jqLiteWrap;
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ Template.prototype = {
|
||||||
if (!queue) {
|
if (!queue) {
|
||||||
inits[this.priority] = queue = [];
|
inits[this.priority] = queue = [];
|
||||||
}
|
}
|
||||||
element = jqLite(element);
|
|
||||||
if (this.newScope) {
|
if (this.newScope) {
|
||||||
childScope = createScope(scope);
|
childScope = createScope(scope);
|
||||||
scope.$onEval(childScope.$eval);
|
scope.$onEval(childScope.$eval);
|
||||||
|
|
@ -45,7 +44,7 @@ Template.prototype = {
|
||||||
paths = this.paths,
|
paths = this.paths,
|
||||||
length = paths.length;
|
length = paths.length;
|
||||||
for (i = 0; i < length; i++) {
|
for (i = 0; i < length; i++) {
|
||||||
children[i].collectInits(childNodes[paths[i]], inits, childScope);
|
children[i].collectInits(jqLite(childNodes[paths[i]]), inits, childScope);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -98,7 +97,7 @@ Compiler.prototype = {
|
||||||
scope = scope || createScope();
|
scope = scope || createScope();
|
||||||
element = element === true
|
element = element === true
|
||||||
? templateElement.cloneNode()
|
? templateElement.cloneNode()
|
||||||
: (jqLite(element) || templateElement);
|
: (element ? jqLite(element) : templateElement);
|
||||||
element.data($$scope, scope);
|
element.data($$scope, scope);
|
||||||
template.attach(element, scope);
|
template.attach(element, scope);
|
||||||
scope.$element = element;
|
scope.$element = element;
|
||||||
|
|
|
||||||
573
src/jqLite.js
573
src/jqLite.js
|
|
@ -14,20 +14,6 @@ var jqCache = {},
|
||||||
|
|
||||||
function jqNextId() { return (jqId++); }
|
function jqNextId() { return (jqId++); }
|
||||||
|
|
||||||
function jqClearData(element) {
|
|
||||||
var cacheId = element[jqName],
|
|
||||||
cache = jqCache[cacheId];
|
|
||||||
if (cache) {
|
|
||||||
forEach(cache.bind || {}, function(fn, type){
|
|
||||||
removeEventListenerFn(element, type, fn);
|
|
||||||
});
|
|
||||||
delete jqCache[cacheId];
|
|
||||||
if (msie)
|
|
||||||
element[jqName] = ''; // ie does not allow deletion of attributes on elements.
|
|
||||||
else
|
|
||||||
delete element[jqName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStyle(element) {
|
function getStyle(element) {
|
||||||
var current = {}, style = element[0].style, value, name, i;
|
var current = {}, style = element[0].style, value, name, i;
|
||||||
|
|
@ -46,216 +32,6 @@ function getStyle(element) {
|
||||||
return current;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
function JQLite(element) {
|
|
||||||
if (!isElement(element) && isDefined(element.length) && element.item && !isWindow(element)) {
|
|
||||||
for(var i=0; i < element.length; i++) {
|
|
||||||
this[i] = element[i];
|
|
||||||
}
|
|
||||||
this.length = element.length;
|
|
||||||
} else {
|
|
||||||
this[0] = element;
|
|
||||||
this.length = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JQLite.prototype = {
|
|
||||||
data: function(key, value) {
|
|
||||||
var element = this[0],
|
|
||||||
cacheId = element[jqName],
|
|
||||||
cache = jqCache[cacheId || -1];
|
|
||||||
if (isDefined(value)) {
|
|
||||||
if (!cache) {
|
|
||||||
element[jqName] = cacheId = jqNextId();
|
|
||||||
cache = jqCache[cacheId] = {};
|
|
||||||
}
|
|
||||||
cache[key] = value;
|
|
||||||
} else {
|
|
||||||
return cache ? cache[key] : _null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
removeData: function(){
|
|
||||||
jqClearData(this[0]);
|
|
||||||
},
|
|
||||||
|
|
||||||
dealoc: function(){
|
|
||||||
(function dealoc(element){
|
|
||||||
jqClearData(element);
|
|
||||||
for ( var i = 0, children = element.childNodes || []; i < children.length; i++) {
|
|
||||||
dealoc(children[i]);
|
|
||||||
}
|
|
||||||
})(this[0]);
|
|
||||||
},
|
|
||||||
|
|
||||||
scope: function() {
|
|
||||||
var scope, element = this;
|
|
||||||
while (element && element.length && !(scope = element.data($$scope))) {
|
|
||||||
element = element.parent();
|
|
||||||
}
|
|
||||||
return scope;
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
ready: function(fn) {
|
|
||||||
var fired = false;
|
|
||||||
|
|
||||||
function trigger() {
|
|
||||||
if (fired) return;
|
|
||||||
fired = true;
|
|
||||||
fn();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9
|
|
||||||
// we can not use jqLite since we are not done loading and jQuery could be loaded later.
|
|
||||||
new JQLite(window).bind('load', trigger); // fallback to window.onload for others
|
|
||||||
},
|
|
||||||
|
|
||||||
bind: function(type, fn){
|
|
||||||
var self = this,
|
|
||||||
element = self[0],
|
|
||||||
bind = self.data('bind'),
|
|
||||||
eventHandler;
|
|
||||||
if (!bind) this.data('bind', bind = {});
|
|
||||||
forEach(type.split(' '), function(type){
|
|
||||||
eventHandler = bind[type];
|
|
||||||
if (!eventHandler) {
|
|
||||||
bind[type] = eventHandler = function(event) {
|
|
||||||
if (!event.preventDefault) {
|
|
||||||
event.preventDefault = function(){
|
|
||||||
event.returnValue = false; //ie
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (!event.stopPropagation) {
|
|
||||||
event.stopPropagation = function() {
|
|
||||||
event.cancelBubble = true; //ie
|
|
||||||
};
|
|
||||||
}
|
|
||||||
forEach(eventHandler.fns, function(fn){
|
|
||||||
fn.call(self, event);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
eventHandler.fns = [];
|
|
||||||
addEventListenerFn(element, type, eventHandler);
|
|
||||||
}
|
|
||||||
eventHandler.fns.push(fn);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
replaceWith: function(replaceNode) {
|
|
||||||
this[0].parentNode.replaceChild(jqLite(replaceNode)[0], this[0]);
|
|
||||||
},
|
|
||||||
|
|
||||||
children: function() {
|
|
||||||
return new JQLite(this[0].childNodes);
|
|
||||||
},
|
|
||||||
|
|
||||||
append: function(node) {
|
|
||||||
var self = this[0];
|
|
||||||
node = jqLite(node);
|
|
||||||
forEach(node, function(child){
|
|
||||||
self.appendChild(child);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
remove: function() {
|
|
||||||
this.dealoc();
|
|
||||||
var parentNode = this[0].parentNode;
|
|
||||||
if (parentNode) parentNode.removeChild(this[0]);
|
|
||||||
},
|
|
||||||
|
|
||||||
removeAttr: function(name) {
|
|
||||||
this[0].removeAttribute(name);
|
|
||||||
},
|
|
||||||
|
|
||||||
after: function(element) {
|
|
||||||
this[0].parentNode.insertBefore(jqLite(element)[0], this[0].nextSibling);
|
|
||||||
},
|
|
||||||
|
|
||||||
hasClass: function(selector) {
|
|
||||||
var className = " " + selector + " ";
|
|
||||||
if ( (" " + this[0].className + " ").replace(/[\n\t]/g, " ").indexOf( className ) > -1 ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
removeClass: function(selector) {
|
|
||||||
this[0].className = trim((" " + this[0].className + " ").replace(/[\n\t]/g, " ").replace(" " + selector + " ", ""));
|
|
||||||
},
|
|
||||||
|
|
||||||
toggleClass: function(selector, condition) {
|
|
||||||
var self = this;
|
|
||||||
(condition ? self.addClass : self.removeClass).call(self, selector);
|
|
||||||
},
|
|
||||||
|
|
||||||
addClass: function( selector ) {
|
|
||||||
if (!this.hasClass(selector)) {
|
|
||||||
this[0].className = trim(this[0].className + ' ' + selector);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
css: function(name, value) {
|
|
||||||
var style = this[0].style;
|
|
||||||
if (isString(name)) {
|
|
||||||
if (isDefined(value)) {
|
|
||||||
style[name] = value;
|
|
||||||
} else {
|
|
||||||
return style[name];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
extend(style, name);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
attr: function(name, value){
|
|
||||||
var e = this[0];
|
|
||||||
if (isObject(name)) {
|
|
||||||
forEach(name, function(value, name){
|
|
||||||
e.setAttribute(name, value);
|
|
||||||
});
|
|
||||||
} else if (isDefined(value)) {
|
|
||||||
e.setAttribute(name, value);
|
|
||||||
} else {
|
|
||||||
// the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
|
|
||||||
// some elements (e.g. Document) don't have get attribute, so return undefined
|
|
||||||
if (e.getAttribute) return e.getAttribute(name, 2);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
text: function(value) {
|
|
||||||
if (isDefined(value)) {
|
|
||||||
this[0].textContent = value;
|
|
||||||
}
|
|
||||||
return this[0].textContent;
|
|
||||||
},
|
|
||||||
|
|
||||||
val: function(value) {
|
|
||||||
if (isDefined(value)) {
|
|
||||||
this[0].value = value;
|
|
||||||
}
|
|
||||||
return this[0].value;
|
|
||||||
},
|
|
||||||
|
|
||||||
html: function(value) {
|
|
||||||
if (isDefined(value)) {
|
|
||||||
var i = 0, childNodes = this[0].childNodes;
|
|
||||||
for ( ; i < childNodes.length; i++) {
|
|
||||||
jqLite(childNodes[i]).dealoc();
|
|
||||||
}
|
|
||||||
this[0].innerHTML = value;
|
|
||||||
}
|
|
||||||
return this[0].innerHTML;
|
|
||||||
},
|
|
||||||
|
|
||||||
parent: function() {
|
|
||||||
return jqLite(this[0].parentNode);
|
|
||||||
},
|
|
||||||
|
|
||||||
clone: cloneNode,
|
|
||||||
cloneNode: cloneNode
|
|
||||||
};
|
|
||||||
function cloneNode() { return jqLite(this[0].cloneNode(true)); }
|
|
||||||
|
|
||||||
if (msie) {
|
if (msie) {
|
||||||
extend(JQLite.prototype, {
|
extend(JQLite.prototype, {
|
||||||
text: function(value) {
|
text: function(value) {
|
||||||
|
|
@ -271,3 +47,352 @@ if (msie) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////
|
||||||
|
function jqLiteWrap(element) {
|
||||||
|
if (isString(element) && element.charAt(0) != '<') {
|
||||||
|
throw new Error('selectors not implemented');
|
||||||
|
}
|
||||||
|
return new JQLite(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
function JQLite(element) {
|
||||||
|
if (element instanceof JQLite) {
|
||||||
|
return element;
|
||||||
|
} else if (isString(element)) {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
// Read about the NoScope elements here:
|
||||||
|
// http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx
|
||||||
|
div.innerHTML = '<div> </div>' + element; // IE insanity to make NoScope elements work!
|
||||||
|
div.removeChild(div.firstChild); // remove the superfluous div
|
||||||
|
JQLiteAddNodes(this, div.childNodes);
|
||||||
|
this.remove(); // detach the elements form the temporary DOM div.
|
||||||
|
} else {
|
||||||
|
JQLiteAddNodes(this, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function JQLiteCloneNode(element) {
|
||||||
|
return element.cloneNode(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function JQLiteDealoc(element){
|
||||||
|
JQLiteRemoveData(element);
|
||||||
|
for ( var i = 0, children = element.childNodes || []; i < children.length; i++) {
|
||||||
|
JQLiteDealoc(children[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function JQLiteRemoveData(element) {
|
||||||
|
var cacheId = element[jqName],
|
||||||
|
cache = jqCache[cacheId];
|
||||||
|
if (cache) {
|
||||||
|
forEach(cache.bind || {}, function(fn, type){
|
||||||
|
removeEventListenerFn(element, type, fn);
|
||||||
|
});
|
||||||
|
delete jqCache[cacheId];
|
||||||
|
element[jqName] = undefined; // ie does not allow deletion of attributes on elements.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function JQLiteData(element, key, value) {
|
||||||
|
var cacheId = element[jqName],
|
||||||
|
cache = jqCache[cacheId || -1];
|
||||||
|
if (isDefined(value)) {
|
||||||
|
if (!cache) {
|
||||||
|
element[jqName] = cacheId = jqNextId();
|
||||||
|
cache = jqCache[cacheId] = {};
|
||||||
|
}
|
||||||
|
cache[key] = value;
|
||||||
|
} else {
|
||||||
|
return cache ? cache[key] : _null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function JQLiteHasClass(element, selector, _) {
|
||||||
|
// the argument '_' is important, since it makes the function have 3 arguments, which
|
||||||
|
// is neede for delegate function to realize the this is a getter.
|
||||||
|
var className = " " + selector + " ";
|
||||||
|
return ((" " + element.className + " ").replace(/[\n\t]/g, " ").indexOf( className ) > -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function JQLiteRemoveClass(element, selector) {
|
||||||
|
element.className = trim(
|
||||||
|
(" " + element.className + " ")
|
||||||
|
.replace(/[\n\t]/g, " ")
|
||||||
|
.replace(" " + selector + " ", "")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function JQLiteAddClass(element, selector ) {
|
||||||
|
if (!JQLiteHasClass(element, selector)) {
|
||||||
|
element.className = trim(element.className + ' ' + selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function JQLiteAddNodes(root, elements) {
|
||||||
|
if (elements) {
|
||||||
|
elements = (!elements.nodeName && isDefined(elements.length))
|
||||||
|
? elements
|
||||||
|
: [ elements ];
|
||||||
|
for(var i=0; i < elements.length; i++) {
|
||||||
|
if (elements[i].nodeType != 11)
|
||||||
|
root.push(elements[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// Functions which are declared directly.
|
||||||
|
//////////////////////////////////////////
|
||||||
|
var JQLitePrototype = JQLite.prototype = extend([], {
|
||||||
|
ready: function(fn) {
|
||||||
|
var fired = false;
|
||||||
|
|
||||||
|
function trigger() {
|
||||||
|
if (fired) return;
|
||||||
|
fired = true;
|
||||||
|
fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9
|
||||||
|
// we can not use jqLite since we are not done loading and jQuery could be loaded later.
|
||||||
|
jqLiteWrap(window).bind('load', trigger); // fallback to window.onload for others
|
||||||
|
},
|
||||||
|
toString: function(){
|
||||||
|
var value = [];
|
||||||
|
forEach(this, function(e){ value.push('' + e);});
|
||||||
|
return '[' + value.join(', ') + ']';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// Functions iterating getter/setters.
|
||||||
|
// these functions return self on setter and
|
||||||
|
// value on get.
|
||||||
|
//////////////////////////////////////////
|
||||||
|
forEach({
|
||||||
|
data: JQLiteData,
|
||||||
|
|
||||||
|
scope: function(element) {
|
||||||
|
var scope;
|
||||||
|
while (element && !(scope = jqLite(element).data($$scope))) {
|
||||||
|
element = element.parentNode;
|
||||||
|
}
|
||||||
|
return scope;
|
||||||
|
},
|
||||||
|
|
||||||
|
removeAttr: function(element,name) {
|
||||||
|
element.removeAttribute(name);
|
||||||
|
},
|
||||||
|
|
||||||
|
hasClass: JQLiteHasClass,
|
||||||
|
|
||||||
|
css: function(element, name, value) {
|
||||||
|
if (isDefined(value)) {
|
||||||
|
element.style[name] = value;
|
||||||
|
} else {
|
||||||
|
return element.style[name];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
attr: function(element, name, value){
|
||||||
|
if (isDefined(value)) {
|
||||||
|
element.setAttribute(name, value);
|
||||||
|
} else if (element.getAttribute) {
|
||||||
|
// the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
|
||||||
|
// some elements (e.g. Document) don't have get attribute, so return undefined
|
||||||
|
return element.getAttribute(name, 2);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
text: extend(msie
|
||||||
|
? function(element, value) {
|
||||||
|
// NodeType == 3 is text node
|
||||||
|
if (element.nodeType == 3) {
|
||||||
|
if (isUndefined(value))
|
||||||
|
return element.nodeValue;
|
||||||
|
element.nodeValue = value;
|
||||||
|
} else {
|
||||||
|
if (isUndefined(value))
|
||||||
|
return element.innerText;
|
||||||
|
element.innerText = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: function(element, value) {
|
||||||
|
if (isUndefined(value)) {
|
||||||
|
return element.textContent;
|
||||||
|
}
|
||||||
|
element.textContent = value;
|
||||||
|
}, {$dv:''}),
|
||||||
|
|
||||||
|
val: function(element, value) {
|
||||||
|
if (isUndefined(value)) {
|
||||||
|
return element.value;
|
||||||
|
}
|
||||||
|
element.value = value;
|
||||||
|
},
|
||||||
|
|
||||||
|
html: function(element, value) {
|
||||||
|
if (isUndefined(value)) {
|
||||||
|
return element.innerHTML;
|
||||||
|
}
|
||||||
|
for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) {
|
||||||
|
JQLiteDealoc(childNodes[i]);
|
||||||
|
}
|
||||||
|
element.innerHTML = value;
|
||||||
|
}
|
||||||
|
}, function(fn, name){
|
||||||
|
/**
|
||||||
|
* Properties: writes return selection, reads return first value
|
||||||
|
*/
|
||||||
|
JQLite.prototype[name] = function(arg1, arg2) {
|
||||||
|
if ((fn.length == 2 ? arg1 : arg2) === undefined) {
|
||||||
|
if (isObject(arg1)) {
|
||||||
|
// we are a write, but the object properties are the key/values
|
||||||
|
for(var i=0; i < this.length; i++) {
|
||||||
|
for ( var key in arg1) {
|
||||||
|
fn(this[i], key, arg1[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return self for chaining
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
// we are a read, so read the first child.
|
||||||
|
if (this.length)
|
||||||
|
return fn(this[0], arg1, arg2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// we are a write, so apply to all children
|
||||||
|
for(var i=0; i < this.length; i++) {
|
||||||
|
fn(this[i], arg1, arg2);
|
||||||
|
}
|
||||||
|
// return self for chaining
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return fn.$dv;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// Functions iterating traversal.
|
||||||
|
// These functions chain results into a single
|
||||||
|
// selector.
|
||||||
|
//////////////////////////////////////////
|
||||||
|
forEach({
|
||||||
|
removeData: JQLiteRemoveData,
|
||||||
|
|
||||||
|
dealoc: JQLiteDealoc,
|
||||||
|
|
||||||
|
bind: function(element, type, fn){
|
||||||
|
var bind = JQLiteData(element, 'bind'),
|
||||||
|
eventHandler;
|
||||||
|
if (!bind) JQLiteData(element, 'bind', bind = {});
|
||||||
|
forEach(type.split(' '), function(type){
|
||||||
|
eventHandler = bind[type];
|
||||||
|
if (!eventHandler) {
|
||||||
|
bind[type] = eventHandler = function(event) {
|
||||||
|
if (!event.preventDefault) {
|
||||||
|
event.preventDefault = function(){
|
||||||
|
event.returnValue = false; //ie
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (!event.stopPropagation) {
|
||||||
|
event.stopPropagation = function() {
|
||||||
|
event.cancelBubble = true; //ie
|
||||||
|
};
|
||||||
|
}
|
||||||
|
forEach(eventHandler.fns, function(fn){
|
||||||
|
fn.call(element, event);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
eventHandler.fns = [];
|
||||||
|
addEventListenerFn(element, type, eventHandler);
|
||||||
|
}
|
||||||
|
eventHandler.fns.push(fn);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
replaceWith: function(element, replaceNode) {
|
||||||
|
var index, parent = element.parentNode;
|
||||||
|
JQLiteDealoc(element);
|
||||||
|
forEach(new JQLite(replaceNode), function(node){
|
||||||
|
if (index) {
|
||||||
|
parent.insertBefore(node, index.nextSibling);
|
||||||
|
} else {
|
||||||
|
parent.replaceChild(node, element);
|
||||||
|
}
|
||||||
|
index = node;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
children: function(element) {
|
||||||
|
var children = [];
|
||||||
|
forEach(element.childNodes, function(element){
|
||||||
|
if (element.nodeName != '#text')
|
||||||
|
children.push(element);
|
||||||
|
});
|
||||||
|
return children;
|
||||||
|
},
|
||||||
|
|
||||||
|
append: function(element, node) {
|
||||||
|
forEach(new JQLite(node), function(child){
|
||||||
|
element.appendChild(child);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: function(element) {
|
||||||
|
JQLiteDealoc(element);
|
||||||
|
var parent = element.parentNode;
|
||||||
|
if (parent) parent.removeChild(element);
|
||||||
|
},
|
||||||
|
|
||||||
|
after: function(element, newElement) {
|
||||||
|
var index = element, parent = element.parentNode;
|
||||||
|
forEach(new JQLite(newElement), function(node){
|
||||||
|
parent.insertBefore(node, index.nextSibling);
|
||||||
|
index = node;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
addClass: JQLiteAddClass,
|
||||||
|
removeClass: JQLiteRemoveClass,
|
||||||
|
|
||||||
|
toggleClass: function(element, selector, condition) {
|
||||||
|
if (isUndefined(condition)) {
|
||||||
|
condition = !JQLiteHasClass(element, selector);
|
||||||
|
}
|
||||||
|
(condition ? JQLiteAddClass : JQLiteRemoveClass)(element, selector);
|
||||||
|
},
|
||||||
|
|
||||||
|
parent: function(element) {
|
||||||
|
// in IE it returns undefined, but we need differentiate it from functions which have no return
|
||||||
|
return element.parentNode || null;
|
||||||
|
},
|
||||||
|
|
||||||
|
find: function(element, selector) {
|
||||||
|
return element.getElementsByTagName(selector);
|
||||||
|
},
|
||||||
|
|
||||||
|
clone: JQLiteCloneNode,
|
||||||
|
cloneNode: JQLiteCloneNode
|
||||||
|
}, function(fn, name){
|
||||||
|
/**
|
||||||
|
* chaining functions
|
||||||
|
*/
|
||||||
|
JQLite.prototype[name] = function(arg1, arg2) {
|
||||||
|
var value;
|
||||||
|
for(var i=0; i < this.length; i++) {
|
||||||
|
if (value == undefined) {
|
||||||
|
value = fn(this[i], arg1, arg2);
|
||||||
|
if (value !== undefined) {
|
||||||
|
// any function which returns a value needs to be wrapped
|
||||||
|
value = jqLite(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
JQLiteAddNodes(value, fn(this[i], arg1, arg2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value == undefined ? this : value;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,12 @@ function browserTrigger(element, type) {
|
||||||
element.checked = !element.checked;
|
element.checked = !element.checked;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// WTF!!! Error: Unspecified error.
|
||||||
|
// Don't know why, but some elements when detached seem to be in inconsistent state and
|
||||||
|
// calling .fireEvent() on them will result in very unhelpful error (Error: Unspecified error)
|
||||||
|
// forcing the browser to compute the element position (by reading its CSS)
|
||||||
|
// puts the element in consistent state.
|
||||||
|
element.style.posLeft;
|
||||||
element.fireEvent('on' + type);
|
element.fireEvent('on' + type);
|
||||||
if (lowercase(element.type) == 'submit') {
|
if (lowercase(element.type) == 'submit') {
|
||||||
while(element) {
|
while(element) {
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,52 @@
|
||||||
describe('jqLite', function(){
|
describe('jqLite', function(){
|
||||||
var scope;
|
var scope;
|
||||||
|
var a, b, c;
|
||||||
|
beforeEach(function(){
|
||||||
|
a = jqLite('<div>A</div>')[0];
|
||||||
|
b = jqLite('<div>B</div>')[0];
|
||||||
|
c = jqLite('<div>C</div>')[0];
|
||||||
|
});
|
||||||
|
|
||||||
beforeEach(function(){
|
beforeEach(function(){
|
||||||
scope = angular.scope();
|
scope = angular.scope();
|
||||||
|
this.addMatchers({
|
||||||
|
toJqEqual: function(expected) {
|
||||||
|
var msg = "Unequal length";
|
||||||
|
this.message = function() { return msg; };
|
||||||
|
|
||||||
|
var value = this.actual && expected && this.actual.length == expected.length;
|
||||||
|
for (var i = 0; value && i < expected.length; i++) {
|
||||||
|
var actual = jqLite(this.actual[i])[0];
|
||||||
|
var expect = jqLite(expected[i])[0];
|
||||||
|
value = value && equals(expect, actual);
|
||||||
|
msg = "Not equal at index: " + i
|
||||||
|
+ " - Expected: " + expect
|
||||||
|
+ " - Actual: " + actual;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function(){
|
||||||
|
dealoc(a);
|
||||||
|
dealoc(b);
|
||||||
|
dealoc(c);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('construction', function(){
|
||||||
|
it('should allow construction with text node', function(){
|
||||||
|
var text = a.firstChild;
|
||||||
|
var selected = jqLite(text);
|
||||||
|
expect(selected.length).toEqual(1);
|
||||||
|
expect(selected[0]).toEqual(text);
|
||||||
|
});
|
||||||
|
it('should allow construction with html', function(){
|
||||||
|
var nodes = jqLite('<div>1</div><span>2</span>');
|
||||||
|
expect(nodes.length).toEqual(2);
|
||||||
|
expect(nodes[0].innerHTML).toEqual('1');
|
||||||
|
expect(nodes[1].innerHTML).toEqual('2');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('scope', function() {
|
describe('scope', function() {
|
||||||
|
|
@ -24,8 +68,245 @@ describe('jqLite', function(){
|
||||||
it('should return undefined when no scope was found', function() {
|
it('should return undefined when no scope was found', function() {
|
||||||
var element = jqLite('<ul><li><p><b>deep deep</b><p></li></ul>');
|
var element = jqLite('<ul><li><p><b>deep deep</b><p></li></ul>');
|
||||||
var deepChild = jqLite(element[0].getElementsByTagName('b')[0]);
|
var deepChild = jqLite(element[0].getElementsByTagName('b')[0]);
|
||||||
expect(deepChild.scope()).toBeNull();
|
expect(deepChild.scope()).toBeFalsy();
|
||||||
dealoc(element);
|
dealoc(element);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('data', function(){
|
||||||
|
it('should set and get ande remove data', function(){
|
||||||
|
var selected = jqLite([a, b, c]);
|
||||||
|
|
||||||
|
expect(selected.data('prop', 'value')).toEqual(selected);
|
||||||
|
expect(selected.data('prop')).toEqual('value');
|
||||||
|
expect(jqLite(a).data('prop')).toEqual('value');
|
||||||
|
expect(jqLite(b).data('prop')).toEqual('value');
|
||||||
|
expect(jqLite(c).data('prop')).toEqual('value');
|
||||||
|
|
||||||
|
jqLite(a).data('prop', 'new value');
|
||||||
|
expect(jqLite(a).data('prop')).toEqual('new value');
|
||||||
|
expect(selected.data('prop')).toEqual('new value');
|
||||||
|
expect(jqLite(b).data('prop')).toEqual('value');
|
||||||
|
expect(jqLite(c).data('prop')).toEqual('value');
|
||||||
|
|
||||||
|
expect(selected.removeData('prop')).toEqual(selected);
|
||||||
|
expect(jqLite(a).data('prop')).toEqual(undefined);
|
||||||
|
expect(jqLite(b).data('prop')).toEqual(undefined);
|
||||||
|
expect(jqLite(c).data('prop')).toEqual(undefined);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('attr', function(){
|
||||||
|
it('shoul read wirite and remove attr', function(){
|
||||||
|
var selector = jqLite([a, b]);
|
||||||
|
|
||||||
|
expect(selector.attr('prop', 'value')).toEqual(selector);
|
||||||
|
expect(jqLite(a).attr('prop')).toEqual('value');
|
||||||
|
expect(jqLite(b).attr('prop')).toEqual('value');
|
||||||
|
|
||||||
|
expect(selector.attr({'prop': 'new value'})).toEqual(selector);
|
||||||
|
expect(jqLite(a).attr('prop')).toEqual('new value');
|
||||||
|
expect(jqLite(b).attr('prop')).toEqual('new value');
|
||||||
|
|
||||||
|
jqLite(b).attr({'prop': 'new value 2'});
|
||||||
|
expect(jqLite(selector).attr('prop')).toEqual('new value');
|
||||||
|
expect(jqLite(b).attr('prop')).toEqual('new value 2');
|
||||||
|
|
||||||
|
selector.removeAttr('prop');
|
||||||
|
expect(jqLite(a).attr('prop')).toBeFalsy();
|
||||||
|
expect(jqLite(b).attr('prop')).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('class', function(){
|
||||||
|
describe('hasClass', function(){
|
||||||
|
it('should check class', function(){
|
||||||
|
var selector = jqLite([a, b]);
|
||||||
|
expect(selector.hasClass('abc')).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('addClass', function(){
|
||||||
|
it('should allow adding of class', function(){
|
||||||
|
var selector = jqLite([a, b]);
|
||||||
|
expect(selector.addClass('abc')).toEqual(selector);
|
||||||
|
expect(jqLite(a).hasClass('abc')).toEqual(true);
|
||||||
|
expect(jqLite(b).hasClass('abc')).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('toggleClass', function(){
|
||||||
|
it('should allow toggling of class', function(){
|
||||||
|
var selector = jqLite([a, b]);
|
||||||
|
expect(selector.toggleClass('abc')).toEqual(selector);
|
||||||
|
expect(jqLite(a).hasClass('abc')).toEqual(true);
|
||||||
|
expect(jqLite(b).hasClass('abc')).toEqual(true);
|
||||||
|
|
||||||
|
expect(selector.toggleClass('abc')).toEqual(selector);
|
||||||
|
expect(jqLite(a).hasClass('abc')).toEqual(false);
|
||||||
|
expect(jqLite(b).hasClass('abc')).toEqual(false);
|
||||||
|
|
||||||
|
expect(selector.toggleClass('abc'), true).toEqual(selector);
|
||||||
|
expect(jqLite(a).hasClass('abc')).toEqual(true);
|
||||||
|
expect(jqLite(b).hasClass('abc')).toEqual(true);
|
||||||
|
|
||||||
|
expect(selector.toggleClass('abc'), false).toEqual(selector);
|
||||||
|
expect(jqLite(a).hasClass('abc')).toEqual(false);
|
||||||
|
expect(jqLite(b).hasClass('abc')).toEqual(false);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('removeClass', function(){
|
||||||
|
it('should allow removal of class', function(){
|
||||||
|
var selector = jqLite([a, b]);
|
||||||
|
expect(selector.addClass('abc')).toEqual(selector);
|
||||||
|
expect(selector.removeClass('abc')).toEqual(selector);
|
||||||
|
expect(jqLite(a).hasClass('abc')).toEqual(false);
|
||||||
|
expect(jqLite(b).hasClass('abc')).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('css', function(){
|
||||||
|
it('should set and read css', function(){
|
||||||
|
var selector = jqLite([a, b]);
|
||||||
|
|
||||||
|
expect(selector.css('prop', 'value')).toEqual(selector);
|
||||||
|
expect(jqLite(a).css('prop')).toEqual('value');
|
||||||
|
expect(jqLite(b).css('prop')).toEqual('value');
|
||||||
|
|
||||||
|
expect(selector.css({'prop': 'new value'})).toEqual(selector);
|
||||||
|
expect(jqLite(a).css('prop')).toEqual('new value');
|
||||||
|
expect(jqLite(b).css('prop')).toEqual('new value');
|
||||||
|
|
||||||
|
jqLite(b).css({'prop': 'new value 2'});
|
||||||
|
expect(jqLite(selector).css('prop')).toEqual('new value');
|
||||||
|
expect(jqLite(b).css('prop')).toEqual('new value 2');
|
||||||
|
|
||||||
|
selector.css('prop', null);
|
||||||
|
expect(jqLite(a).css('prop')).toBeFalsy();
|
||||||
|
expect(jqLite(b).css('prop')).toBeFalsy();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('text', function(){
|
||||||
|
it('should return null on empty', function(){
|
||||||
|
expect(jqLite().length).toEqual(0);
|
||||||
|
expect(jqLite().text()).toEqual('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should read/write value', function(){
|
||||||
|
var element = jqLite('<div>abc</div>');
|
||||||
|
expect(element.length).toEqual(1);
|
||||||
|
expect(element[0].innerHTML).toEqual('abc');
|
||||||
|
expect(element.text()).toEqual('abc');
|
||||||
|
expect(element.text('xyz') == element).toBeTruthy();
|
||||||
|
expect(element.text()).toEqual('xyz');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('val', function(){
|
||||||
|
it('should read, write value', function(){
|
||||||
|
var input = jqLite('<input type="text"/>');
|
||||||
|
expect(input.val('abc')).toEqual(input);
|
||||||
|
expect(input[0].value).toEqual('abc');
|
||||||
|
expect(input.val()).toEqual('abc');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('html', function(){
|
||||||
|
it('should return null on empty', function(){
|
||||||
|
expect(jqLite().length).toEqual(0);
|
||||||
|
expect(jqLite().html()).toEqual(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should read/write value', function(){
|
||||||
|
var element = jqLite('<div>abc</div>');
|
||||||
|
expect(element.length).toEqual(1);
|
||||||
|
expect(element[0].innerHTML).toEqual('abc');
|
||||||
|
expect(element.html()).toEqual('abc');
|
||||||
|
expect(element.html('xyz') == element).toBeTruthy();
|
||||||
|
expect(element.html()).toEqual('xyz');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bind', function(){
|
||||||
|
it('should bind to all elements and return functions', function(){
|
||||||
|
var selected = jqLite([a, b]);
|
||||||
|
var log = '';
|
||||||
|
expect(selected.bind('click', function(){
|
||||||
|
log += 'click on: ' + jqLite(this).text() + ';';
|
||||||
|
})).toEqual(selected);
|
||||||
|
browserTrigger(a, 'click');
|
||||||
|
expect(log).toEqual('click on: A;');
|
||||||
|
browserTrigger(b, 'click');
|
||||||
|
expect(log).toEqual('click on: A;click on: B;');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('replaceWith', function(){
|
||||||
|
it('should replaceWith', function(){
|
||||||
|
var root = jqLite('<div>').html('before-<div></div>after');
|
||||||
|
var div = root.find('div');
|
||||||
|
expect(div.replaceWith('<span>span-</span><b>bold-</b>')).toEqual(div);
|
||||||
|
expect(root.text()).toEqual('before-span-bold-after');
|
||||||
|
});
|
||||||
|
it('should replaceWith text', function(){
|
||||||
|
var root = jqLite('<div>').html('before-<div></div>after');
|
||||||
|
var div = root.find('div');
|
||||||
|
expect(div.replaceWith('text-')).toEqual(div);
|
||||||
|
expect(root.text()).toEqual('before-text-after');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('children', function(){
|
||||||
|
it('should select non-text children', function(){
|
||||||
|
var root = jqLite('<div>').html('before-<div></div>after-<span></span>');
|
||||||
|
var div = root.find('div');
|
||||||
|
var span = root.find('span');
|
||||||
|
expect(root.children()).toJqEqual([div, span]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('append', function(){
|
||||||
|
it('should append', function(){
|
||||||
|
var root = jqLite('<div>');
|
||||||
|
expect(root.append('<span>abc</span>')).toEqual(root);
|
||||||
|
expect(root.html().toLowerCase()).toEqual('<span>abc</span>');
|
||||||
|
});
|
||||||
|
it('should append text', function(){
|
||||||
|
var root = jqLite('<div>');
|
||||||
|
expect(root.append('text')).toEqual(root);
|
||||||
|
expect(root.html()).toEqual('text');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('remove', function(){
|
||||||
|
it('should remove', function(){
|
||||||
|
var root = jqLite('<div><span>abc</span></div>');
|
||||||
|
var span = root.find('span');
|
||||||
|
expect(span.remove()).toEqual(span);
|
||||||
|
expect(root.html()).toEqual('');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('after', function(){
|
||||||
|
it('should after', function(){
|
||||||
|
var root = jqLite('<div><span></span></div>');
|
||||||
|
var span = root.find('span');
|
||||||
|
expect(span.after('<i></i><b></b>')).toEqual(span);
|
||||||
|
expect(root.html().toLowerCase()).toEqual('<span></span><i></i><b></b>');
|
||||||
|
});
|
||||||
|
it('should allow taking text', function(){
|
||||||
|
var root = jqLite('<div><span></span></div>');
|
||||||
|
var span = root.find('span');
|
||||||
|
span.after('abc');
|
||||||
|
expect(root.html().toLowerCase()).toEqual('<span></span>abc');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('parent', function(){
|
||||||
|
it('should return empty set when no parent', function(){
|
||||||
|
var element = jqLite('<div>abc</div>');
|
||||||
|
expect(element.parent()).toBeTruthy();
|
||||||
|
expect(element.parent().length).toEqual(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
describe('find', function(){
|
||||||
|
it('should find child by name', function(){
|
||||||
|
var root = jqLite('<div><div>text</div></div>');
|
||||||
|
var innerDiv = root.find('div');
|
||||||
|
expect(innerDiv.length).toEqual(1);
|
||||||
|
expect(innerDiv.html()).toEqual('text');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -142,8 +142,11 @@ function childNode(element, index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function dealoc(obj) {
|
function dealoc(obj) {
|
||||||
var element = (obj||{}).$element || obj;
|
if (obj) {
|
||||||
if (element && element.dealoc) element.dealoc();
|
var element = obj.$element || obj || {};
|
||||||
|
if (element.nodeName) element = jqLite(element);
|
||||||
|
if (element.dealoc) element.dealoc();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extend(angular, {
|
extend(angular, {
|
||||||
|
|
@ -179,7 +182,7 @@ function sortedHtml(element, showNgClass) {
|
||||||
replace(/</g, '<').
|
replace(/</g, '<').
|
||||||
replace(/>/g, '>');
|
replace(/>/g, '>');
|
||||||
} else {
|
} else {
|
||||||
html += '<' + node.nodeName.toLowerCase();
|
html += '<' + (node.nodeName || '?NOT_A_NODE?').toLowerCase();
|
||||||
var attributes = node.attributes || [];
|
var attributes = node.attributes || [];
|
||||||
var attrs = [];
|
var attrs = [];
|
||||||
var className = node.className || '';
|
var className = node.className || '';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue