mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Merge pull request #444 from silverstripe-scienceninjas/feature/entwine-enhancements
Feature/entwine enhancements
This commit is contained in:
commit
cd68e5ec66
@ -291,6 +291,10 @@ class LeftAndMain extends Controller implements PermissionProvider {
|
||||
))
|
||||
);
|
||||
|
||||
if (Director::isDev()) {
|
||||
Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/src/jquery.entwine.inspector.js');
|
||||
}
|
||||
|
||||
Requirements::css(FRAMEWORK_ADMIN_DIR . '/thirdparty/jquery-notice/jquery.notice.css');
|
||||
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
|
||||
Requirements::css(FRAMEWORK_ADMIN_DIR .'/thirdparty/chosen/chosen/chosen.css');
|
||||
|
8
thirdparty/jquery-entwine/.piston.yml
vendored
8
thirdparty/jquery-entwine/.piston.yml
vendored
@ -1,8 +1,8 @@
|
||||
---
|
||||
format: 1
|
||||
repository_url: https://github.com/hafriedlander/jquery.entwine.git
|
||||
repository_class: Piston::Git::Repository
|
||||
handler:
|
||||
commit: dd1b1d3c0958aa12990cba80a9b9d429a7e89bee
|
||||
commit: 860ffe280044c3f88e64ba400f8f17b5d2616c39
|
||||
branch: master
|
||||
lock: false
|
||||
repository_class: Piston::Git::Repository
|
||||
repository_url: https://github.com/hafriedlander/jquery.entwine.git
|
||||
format: 1
|
||||
|
1
thirdparty/jquery-entwine/build.sh
vendored
1
thirdparty/jquery-entwine/build.sh
vendored
@ -13,6 +13,7 @@ for x in \
|
||||
vendor/jquery.selector/jquery.selector.js \
|
||||
vendor/jquery.selector/jquery.selector.specifity.js \
|
||||
vendor/jquery.selector/jquery.selector.matches.js \
|
||||
src/jquery.selector.affectedby.js \
|
||||
src/jquery.focusinout.js \
|
||||
src/jquery.entwine.js \
|
||||
src/jquery.entwine.dommaybechanged.js \
|
||||
|
1971
thirdparty/jquery-entwine/dist/jquery.concrete-dist.js
vendored
Normal file
1971
thirdparty/jquery-entwine/dist/jquery.concrete-dist.js
vendored
Normal file
@ -0,0 +1,1971 @@
|
||||
/* jQuery.Entwine - Copyright 2009-2011 Hamish Friedlander and SilverStripe. Version . */
|
||||
|
||||
/* vendor/jquery.selector/jquery.class.js */
|
||||
|
||||
/**
|
||||
* Very basic Class utility. Based on base and jquery.class.
|
||||
*
|
||||
* Class definition: var Foo = Base.extend({ init: function(){ Constructor }; method_name: function(){ Method } });
|
||||
*
|
||||
* Inheritance: var Bar = Foo.extend({ method_name: function(){ this._super(); } });
|
||||
*
|
||||
* new-less Constructor: new Foo(arg) <-same as-> Foo(arg)
|
||||
*/
|
||||
|
||||
var Base;
|
||||
|
||||
(function(){
|
||||
|
||||
var marker = {}, fnTest = /xyz/.test(function(){var xyz;}) ? /\b_super\b/ : /.*/;
|
||||
|
||||
// The base Class implementation (does nothing)
|
||||
Base = function(){};
|
||||
|
||||
Base.addMethod = function(name, func) {
|
||||
var parent = this._super && this._super.prototype;
|
||||
|
||||
if (parent && fnTest.test(func)) {
|
||||
this.prototype[name] = function(){
|
||||
var tmp = this._super;
|
||||
this._super = parent[name];
|
||||
try {
|
||||
var ret = func.apply(this, arguments);
|
||||
}
|
||||
finally {
|
||||
this._super = tmp;
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
else this.prototype[name] = func;
|
||||
};
|
||||
|
||||
Base.addMethods = function(props) {
|
||||
for (var name in props) {
|
||||
if (typeof props[name] == 'function') this.addMethod(name, props[name]);
|
||||
else this.prototype[name] = props[name];
|
||||
}
|
||||
};
|
||||
|
||||
Base.subclassOf = function(parentkls) {
|
||||
var kls = this;
|
||||
while (kls) {
|
||||
if (kls === parentkls) return true;
|
||||
kls = kls._super;
|
||||
}
|
||||
};
|
||||
|
||||
// Create a new Class that inherits from this class
|
||||
Base.extend = function(props) {
|
||||
|
||||
// The dummy class constructor
|
||||
var Kls = function() {
|
||||
if (arguments[0] === marker) return;
|
||||
|
||||
if (this instanceof Kls) {
|
||||
if (this.init) this.init.apply(this, arguments);
|
||||
}
|
||||
else {
|
||||
var ret = new Kls(marker); if (ret.init) ret.init.apply(ret, arguments); return ret;
|
||||
}
|
||||
};
|
||||
|
||||
// Add the common class variables and methods
|
||||
Kls.constructor = Kls;
|
||||
Kls.extend = Base.extend;
|
||||
Kls.addMethod = Base.addMethod;
|
||||
Kls.addMethods = Base.addMethods;
|
||||
Kls.subclassOf = Base.subclassOf;
|
||||
|
||||
Kls._super = this;
|
||||
|
||||
// Attach the parent object to the inheritance chain
|
||||
Kls.prototype = new this(marker);
|
||||
Kls.prototype.constructor = Kls;
|
||||
|
||||
// Copy the properties over onto the new prototype
|
||||
Kls.addMethods(props);
|
||||
|
||||
return Kls;
|
||||
};
|
||||
})();;
|
||||
|
||||
|
||||
/* vendor/jquery.selector/jquery.selector.js */
|
||||
|
||||
(function($){
|
||||
|
||||
var tokens = {
|
||||
UNICODE: /\\[0-9a-f]{1,6}(?:\r\n|[ \n\r\t\f])?/,
|
||||
ESCAPE: /(?:UNICODE)|\\[^\n\r\f0-9a-f]/,
|
||||
NONASCII: /[^\x00-\x7F]/,
|
||||
NMSTART: /[_a-z]|(?:NONASCII)|(?:ESCAPE)/,
|
||||
NMCHAR: /[_a-z0-9-]|(?:NONASCII)|(?:ESCAPE)/,
|
||||
IDENT: /-?(?:NMSTART)(?:NMCHAR)*/,
|
||||
|
||||
NL: /\n|\r\n|\r|\f/,
|
||||
|
||||
STRING: /(?:STRING1)|(?:STRING2)|(?:STRINGBARE)/,
|
||||
STRING1: /"(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\"])*"/,
|
||||
STRING2: /'(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\'])*'/,
|
||||
STRINGBARE: /(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\]])*/,
|
||||
|
||||
FUNCTION: /(?:IDENT)\(\)/,
|
||||
|
||||
INTEGER: /[0-9]+/,
|
||||
|
||||
WITHN: /([-+])?(INTEGER)?(n)\s*(?:([-+])\s*(INTEGER))?/,
|
||||
WITHOUTN: /([-+])?(INTEGER)/
|
||||
};
|
||||
|
||||
var rx = {
|
||||
not: /:not\(/,
|
||||
not_end: /\)/,
|
||||
|
||||
tag: /((?:IDENT)|\*)/,
|
||||
id: /#(IDENT)/,
|
||||
cls: /\.(IDENT)/,
|
||||
attr: /\[\s*(IDENT)\s*(?:([^=]?=)\s*(STRING)\s*)?\]/,
|
||||
pseudo_el: /(?::(first-line|first-letter|before|after))|(?:::((?:FUNCTION)|(?:IDENT)))/,
|
||||
pseudo_cls_nth: /:nth-child\(\s*(?:(?:WITHN)|(?:WITHOUTN)|(odd|even))\s*\)/,
|
||||
pseudo_cls: /:(IDENT)/,
|
||||
|
||||
comb: /\s*(\+|~|>)\s*|\s+/,
|
||||
comma: /\s*,\s*/,
|
||||
important: /\s+!important\s*$/
|
||||
};
|
||||
|
||||
/* Replace placeholders with actual regex, and mark all as case insensitive */
|
||||
var token = /[A-Z][A-Z0-9]+/;
|
||||
for (var k in rx) {
|
||||
var m, src = rx[k].source;
|
||||
while (m = src.match(token)) src = src.replace(m[0], tokens[m[0]].source);
|
||||
rx[k] = new RegExp(src, 'gi');
|
||||
}
|
||||
|
||||
/**
|
||||
* A string that matches itself against regexii, and keeps track of how much of itself has been matched
|
||||
*/
|
||||
var ConsumableString = Base.extend({
|
||||
init: function(str) {
|
||||
this.str = str;
|
||||
this.pos = 0;
|
||||
},
|
||||
match: function(rx) {
|
||||
var m;
|
||||
rx.lastIndex = this.pos;
|
||||
if ((m = rx.exec(this.str)) && m.index == this.pos ) {
|
||||
this.pos = rx.lastIndex ? rx.lastIndex : this.str.length ;
|
||||
return m;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
peek: function(rx) {
|
||||
var m;
|
||||
rx.lastIndex = this.pos;
|
||||
if ((m = rx.exec(this.str)) && m.index == this.pos ) return m;
|
||||
return null;
|
||||
},
|
||||
showpos: function() {
|
||||
return this.str.slice(0,this.pos)+'<HERE>' + this.str.slice(this.pos);
|
||||
},
|
||||
done: function() {
|
||||
return this.pos == this.str.length;
|
||||
}
|
||||
});
|
||||
|
||||
/* A base class that all Selectors inherit off */
|
||||
var SelectorBase = Base.extend({});
|
||||
|
||||
/**
|
||||
* A class representing a Simple Selector, as per the CSS3 selector spec
|
||||
*/
|
||||
var SimpleSelector = SelectorBase.extend({
|
||||
init: function() {
|
||||
this.tag = null;
|
||||
this.id = null;
|
||||
this.classes = [];
|
||||
this.attrs = [];
|
||||
this.nots = [];
|
||||
this.pseudo_classes = [];
|
||||
this.pseudo_els = [];
|
||||
},
|
||||
parse: function(selector) {
|
||||
var m;
|
||||
|
||||
/* Pull out the initial tag first, if there is one */
|
||||
if (m = selector.match(rx.tag)) this.tag = m[1];
|
||||
|
||||
/* Then for each selection type, try and find a match */
|
||||
do {
|
||||
if (m = selector.match(rx.not)) {
|
||||
this.nots[this.nots.length] = SelectorsGroup().parse(selector);
|
||||
if (!(m = selector.match(rx.not_end))) {
|
||||
throw 'Invalid :not term in selector';
|
||||
}
|
||||
}
|
||||
else if (m = selector.match(rx.id)) this.id = m[1];
|
||||
else if (m = selector.match(rx.cls)) this.classes[this.classes.length] = m[1];
|
||||
else if (m = selector.match(rx.attr)) this.attrs[this.attrs.length] = [ m[1], m[2], m[3] ];
|
||||
else if (m = selector.match(rx.pseudo_el)) this.pseudo_els[this.pseudo_els.length] = m[1] || m[2];
|
||||
else if (m = selector.match(rx.pseudo_cls_nth)) {
|
||||
if (m[3]) {
|
||||
var a = parseInt((m[1]||'')+(m[2]||'1'));
|
||||
var b = parseInt((m[4]||'')+(m[5]||'0'));
|
||||
}
|
||||
else {
|
||||
var a = m[8] ? 2 : 0;
|
||||
var b = m[8] ? (4-m[8].length) : parseInt((m[6]||'')+m[7]);
|
||||
}
|
||||
this.pseudo_classes[this.pseudo_classes.length] = ['nth-child', [a, b]];
|
||||
}
|
||||
else if (m = selector.match(rx.pseudo_cls)) this.pseudo_classes[this.pseudo_classes.length] = [m[1]];
|
||||
|
||||
} while(m && !selector.done());
|
||||
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* A class representing a Selector, as per the CSS3 selector spec
|
||||
*/
|
||||
var Selector = SelectorBase.extend({
|
||||
init: function(){
|
||||
this.parts = [];
|
||||
},
|
||||
parse: function(cons){
|
||||
this.parts[this.parts.length] = SimpleSelector().parse(cons);
|
||||
|
||||
while (!cons.done() && !cons.peek(rx.comma) && (m = cons.match(rx.comb))) {
|
||||
this.parts[this.parts.length] = m[1] || ' ';
|
||||
this.parts[this.parts.length] = SimpleSelector().parse(cons);
|
||||
}
|
||||
|
||||
return this.parts.length == 1 ? this.parts[0] : this;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* A class representing a sequence of selectors, as per the CSS3 selector spec
|
||||
*/
|
||||
var SelectorsGroup = SelectorBase.extend({
|
||||
init: function(){
|
||||
this.parts = [];
|
||||
},
|
||||
parse: function(cons){
|
||||
this.parts[this.parts.length] = Selector().parse(cons);
|
||||
|
||||
while (!cons.done() && (m = cons.match(rx.comma))) {
|
||||
this.parts[this.parts.length] = Selector().parse(cons);
|
||||
}
|
||||
|
||||
return this.parts.length == 1 ? this.parts[0] : this;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$.selector = function(s){
|
||||
var cons = ConsumableString(s);
|
||||
var res = SelectorsGroup().parse(cons);
|
||||
|
||||
res.selector = s;
|
||||
|
||||
if (!cons.done()) throw 'Could not parse selector - ' + cons.showpos() ;
|
||||
else return res;
|
||||
};
|
||||
|
||||
$.selector.SelectorBase = SelectorBase;
|
||||
$.selector.SimpleSelector = SimpleSelector;
|
||||
$.selector.Selector = Selector;
|
||||
$.selector.SelectorsGroup = SelectorsGroup;
|
||||
|
||||
})(jQuery);
|
||||
;
|
||||
|
||||
|
||||
/* vendor/jquery.selector/jquery.selector.specifity.js */
|
||||
|
||||
(function($) {
|
||||
|
||||
$.selector.SimpleSelector.addMethod('specifity', function() {
|
||||
if (this.spec) return this.spec;
|
||||
|
||||
var spec = [
|
||||
this.id ? 1 : 0,
|
||||
this.classes.length + this.attrs.length + this.pseudo_classes.length,
|
||||
((this.tag && this.tag != '*') ? 1 : 0) + this.pseudo_els.length
|
||||
];
|
||||
$.each(this.nots, function(i,not){
|
||||
var ns = not.specifity(); spec[0] += ns[0]; spec[1] += ns[1]; spec[2] += ns[2];
|
||||
});
|
||||
|
||||
return this.spec = spec;
|
||||
});
|
||||
|
||||
$.selector.Selector.addMethod('specifity', function(){
|
||||
if (this.spec) return this.spec;
|
||||
|
||||
var spec = [0,0,0];
|
||||
$.each(this.parts, function(i,part){
|
||||
if (i%2) return;
|
||||
var ps = part.specifity(); spec[0] += ps[0]; spec[1] += ps[1]; spec[2] += ps[2];
|
||||
});
|
||||
|
||||
return this.spec = spec;
|
||||
});
|
||||
|
||||
$.selector.SelectorsGroup.addMethod('specifity', function(){
|
||||
if (this.spec) return this.spec;
|
||||
|
||||
var spec = [0,0,0];
|
||||
$.each(this.parts, function(i,part){
|
||||
var ps = part.specifity(); spec[0] += ps[0]; spec[1] += ps[1]; spec[2] += ps[2];
|
||||
});
|
||||
|
||||
return this.spec = spec;
|
||||
});
|
||||
|
||||
|
||||
})(jQuery);
|
||||
;
|
||||
|
||||
|
||||
/* vendor/jquery.selector/jquery.selector.matches.js */
|
||||
|
||||
/*
|
||||
This attempts to do the opposite of Sizzle.
|
||||
Sizzle is good for finding elements for a selector, but not so good for telling if an individual element matches a selector
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
/**** CAPABILITY TESTS ****/
|
||||
var div = document.createElement('div');
|
||||
div.innerHTML = '<form id="test"><input name="id" type="text"/></form>';
|
||||
|
||||
// In IE 6-7, getAttribute often does the wrong thing (returns similar to el.attr), so we need to use getAttributeNode on that browser
|
||||
var getAttributeDodgy = div.firstChild.getAttribute('id') !== 'test';
|
||||
|
||||
// Does browser support Element.firstElementChild, Element.previousElementSibling, etc.
|
||||
var hasElementTraversal = div.firstElementChild && div.firstElementChild.tagName == 'FORM';
|
||||
|
||||
// Does browser support Element.children
|
||||
var hasChildren = div.children && div.children[0].tagName == 'FORM';
|
||||
|
||||
var FUNC_IN = /^\s*function\s*\([^)]*\)\s*\{/;
|
||||
var FUNC_OUT = /}\s*$/;
|
||||
|
||||
var funcToString = function(f) {
|
||||
return (''+f).replace(FUNC_IN,'').replace(FUNC_OUT,'');
|
||||
};
|
||||
|
||||
// Can we use Function#toString ?
|
||||
try {
|
||||
var testFunc = function(){ return 'good'; };
|
||||
if ((new Function('',funcToString(testFunc)))() != 'good') funcToString = false;
|
||||
}
|
||||
catch(e) { funcToString = false; console.log(e.message);/*pass*/ }
|
||||
|
||||
/**** INTRO ****/
|
||||
|
||||
var GOOD = /GOOD/g;
|
||||
var BAD = /BAD/g;
|
||||
|
||||
var STARTS_WITH_QUOTES = /^['"]/g;
|
||||
|
||||
var join = function(js) {
|
||||
return js.join('\n');
|
||||
};
|
||||
|
||||
var join_complex = function(js) {
|
||||
var code = new String(js.join('\n')); // String objects can have properties set. strings can't
|
||||
code.complex = true;
|
||||
return code;
|
||||
};
|
||||
|
||||
/**** ATTRIBUTE ACCESSORS ****/
|
||||
|
||||
// Not all attribute names can be used as identifiers, so we encode any non-acceptable characters as hex
|
||||
var varForAttr = function(attr) {
|
||||
return '_' + attr.replace(/^[^A-Za-z]|[^A-Za-z0-9]/g, function(m){ return '_0x' + m.charCodeAt(0).toString(16) + '_'; });
|
||||
};
|
||||
|
||||
var getAttr;
|
||||
|
||||
// Good browsers
|
||||
if (!getAttributeDodgy) {
|
||||
getAttr = function(attr){ return 'var '+varForAttr(attr)+' = el.getAttribute("'+attr+'");' ; };
|
||||
}
|
||||
// IE 6, 7
|
||||
else {
|
||||
// On IE 6 + 7, getAttribute still has to be called with DOM property mirror name, not attribute name. Map attributes to those names
|
||||
var getAttrIEMap = { 'class': 'className', 'for': 'htmlFor' };
|
||||
|
||||
getAttr = function(attr) {
|
||||
var ieattr = getAttrIEMap[attr] || attr;
|
||||
return 'var '+varForAttr(attr)+' = el.getAttribute("'+ieattr+'",2) || (el.getAttributeNode("'+attr+'")||{}).nodeValue;';
|
||||
};
|
||||
}
|
||||
|
||||
/**** ATTRIBUTE COMPARITORS ****/
|
||||
|
||||
var attrchecks = {
|
||||
'-': '!K',
|
||||
'=': 'K != "V"',
|
||||
'!=': 'K == "V"',
|
||||
'~=': '_WS_K.indexOf(" V ") == -1',
|
||||
'^=': '!K || K.indexOf("V") != 0',
|
||||
'*=': '!K || K.indexOf("V") == -1',
|
||||
'$=': '!K || K.substr(K.length-"V".length) != "V"'
|
||||
};
|
||||
|
||||
/**** STATE TRACKER ****/
|
||||
|
||||
var State = $.selector.State = Base.extend({
|
||||
init: function(){
|
||||
this.reset();
|
||||
},
|
||||
reset: function() {
|
||||
this.attrs = {}; this.wsattrs = {};
|
||||
},
|
||||
|
||||
prev: function(){
|
||||
this.reset();
|
||||
if (hasElementTraversal) return 'el = el.previousElementSibling';
|
||||
return 'while((el = el.previousSibling) && el.nodeType != 1) {}';
|
||||
},
|
||||
next: function() {
|
||||
this.reset();
|
||||
if (hasElementTraversal) return 'el = el.nextElementSibling';
|
||||
return 'while((el = el.nextSibling) && el.nodeType != 1) {}';
|
||||
},
|
||||
prevLoop: function(body){
|
||||
this.reset();
|
||||
if (hasElementTraversal) return join([ 'while(el = el.previousElementSibling){', body]);
|
||||
return join([
|
||||
'while(el = el.previousSibling){',
|
||||
'if (el.nodeType != 1) continue;',
|
||||
body
|
||||
]);
|
||||
},
|
||||
parent: function() {
|
||||
this.reset();
|
||||
return 'el = el.parentNode;';
|
||||
},
|
||||
parentLoop: function(body) {
|
||||
this.reset();
|
||||
return join([
|
||||
'while((el = el.parentNode) && el.nodeType == 1){',
|
||||
body,
|
||||
'}'
|
||||
]);
|
||||
},
|
||||
|
||||
uses_attr: function(attr) {
|
||||
if (this.attrs[attr]) return;
|
||||
this.attrs[attr] = true;
|
||||
return getAttr(attr);
|
||||
},
|
||||
uses_wsattr: function(attr) {
|
||||
if (this.wsattrs[attr]) return;
|
||||
this.wsattrs[attr] = true;
|
||||
return join([this.uses_attr(attr), 'var _WS_'+varForAttr(attr)+' = " "+'+varForAttr(attr)+'+" ";']);
|
||||
},
|
||||
|
||||
save: function(lbl) {
|
||||
return 'var el'+lbl+' = el;';
|
||||
},
|
||||
restore: function(lbl) {
|
||||
this.reset();
|
||||
return 'el = el'+lbl+';';
|
||||
}
|
||||
});
|
||||
|
||||
/**** PSEUDO-CLASS DETAILS ****/
|
||||
|
||||
var pseudoclschecks = {
|
||||
'first-child': join([
|
||||
'var cel = el;',
|
||||
'while(cel = cel.previousSibling){ if (cel.nodeType === 1) BAD; }'
|
||||
]),
|
||||
'last-child': join([
|
||||
'var cel = el;',
|
||||
'while(cel = cel.nextSibling){ if (cel.nodeType === 1) BAD; }'
|
||||
]),
|
||||
'nth-child': function(a,b) {
|
||||
var get_i = join([
|
||||
'var i = 1, cel = el;',
|
||||
'while(cel = cel.previousSibling){',
|
||||
'if (cel.nodeType === 1) i++;',
|
||||
'}'
|
||||
]);
|
||||
|
||||
if (a == 0) return join([
|
||||
get_i,
|
||||
'if (i- '+b+' != 0) BAD;'
|
||||
]);
|
||||
else if (b == 0 && a >= 0) return join([
|
||||
get_i,
|
||||
'if (i%'+a+' != 0 || i/'+a+' < 0) BAD;'
|
||||
]);
|
||||
else if (b == 0 && a < 0) return join([
|
||||
'BAD;'
|
||||
]);
|
||||
else return join([
|
||||
get_i,
|
||||
'if ((i- '+b+')%'+a+' != 0 || (i- '+b+')/'+a+' < 0) BAD;'
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
// Needs to refence contents of object, so must be injected after definition
|
||||
pseudoclschecks['only-child'] = join([
|
||||
pseudoclschecks['first-child'],
|
||||
pseudoclschecks['last-child']
|
||||
]);
|
||||
|
||||
/**** SimpleSelector ****/
|
||||
|
||||
$.selector.SimpleSelector.addMethod('compile', function(el) {
|
||||
var js = [];
|
||||
|
||||
/* Check against element name */
|
||||
if (this.tag && this.tag != '*') {
|
||||
js[js.length] = 'if (el.tagName != "'+this.tag.toUpperCase()+'") BAD;';
|
||||
}
|
||||
|
||||
/* Check against ID */
|
||||
if (this.id) {
|
||||
js[js.length] = el.uses_attr('id');
|
||||
js[js.length] = 'if (_id !== "'+this.id+'") BAD;';
|
||||
}
|
||||
|
||||
/* Build className checking variable */
|
||||
if (this.classes.length) {
|
||||
js[js.length] = el.uses_wsattr('class');
|
||||
|
||||
/* Check against class names */
|
||||
$.each(this.classes, function(i, cls){
|
||||
js[js.length] = 'if (_WS__class.indexOf(" '+cls+' ") == -1) BAD;';
|
||||
});
|
||||
}
|
||||
|
||||
/* Check against attributes */
|
||||
$.each(this.attrs, function(i, attr){
|
||||
js[js.length] = (attr[1] == '~=') ? el.uses_wsattr(attr[0]) : el.uses_attr(attr[0]);
|
||||
var check = attrchecks[ attr[1] || '-' ];
|
||||
check = check.replace( /K/g, varForAttr(attr[0])).replace( /V/g, attr[2] && attr[2].match(STARTS_WITH_QUOTES) ? attr[2].slice(1,-1) : attr[2] );
|
||||
js[js.length] = 'if ('+check+') BAD;';
|
||||
});
|
||||
|
||||
/* Check against nots */
|
||||
$.each(this.nots, function(i, not){
|
||||
var lbl = ++lbl_id;
|
||||
var func = join([
|
||||
'l'+lbl+':{',
|
||||
not.compile(el).replace(BAD, 'break l'+lbl).replace(GOOD, 'BAD'),
|
||||
'}'
|
||||
]);
|
||||
|
||||
if (!(not instanceof $.selector.SimpleSelector)) func = join([
|
||||
el.save(lbl),
|
||||
func,
|
||||
el.restore(lbl)
|
||||
]);
|
||||
|
||||
js[js.length] = func;
|
||||
});
|
||||
|
||||
/* Check against pseudo-classes */
|
||||
$.each(this.pseudo_classes, function(i, pscls){
|
||||
var check = pseudoclschecks[pscls[0]];
|
||||
if (check) {
|
||||
js[js.length] = ( typeof check == 'function' ? check.apply(this, pscls[1]) : check );
|
||||
}
|
||||
else if (check = $.find.selectors.filters[pscls[0]]) {
|
||||
if (funcToString) {
|
||||
js[js.length] = funcToString(check).replace(/elem/g,'el').replace(/return([^;]+);/,'if (!($1)) BAD;');
|
||||
}
|
||||
else {
|
||||
js[js.length] = 'if (!$.find.selectors.filters.'+pscls[0]+'(el)) BAD;';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
js[js.length] = 'GOOD';
|
||||
|
||||
/* Pass */
|
||||
return join(js);
|
||||
});
|
||||
|
||||
var lbl_id = 0;
|
||||
/** Turns an compiled fragment into the first part of a combination */
|
||||
function as_subexpr(f) {
|
||||
if (f.complex)
|
||||
return join([
|
||||
'l'+(++lbl_id)+':{',
|
||||
f.replace(GOOD, 'break l'+lbl_id),
|
||||
'}'
|
||||
]);
|
||||
else
|
||||
return f.replace(GOOD, '');
|
||||
}
|
||||
|
||||
var combines = {
|
||||
' ': function(el, f1, f2) {
|
||||
return join_complex([
|
||||
f2,
|
||||
'while(true){',
|
||||
el.parent(),
|
||||
'if (!el || el.nodeType !== 1) BAD;',
|
||||
f1.compile(el).replace(BAD, 'continue'),
|
||||
'}'
|
||||
]);
|
||||
},
|
||||
|
||||
'>': function(el, f1, f2) {
|
||||
return join([
|
||||
f2,
|
||||
el.parent(),
|
||||
'if (!el || el.nodeType !== 1) BAD;',
|
||||
f1.compile(el)
|
||||
]);
|
||||
},
|
||||
|
||||
'~': function(el, f1, f2) {
|
||||
return join_complex([
|
||||
f2,
|
||||
el.prevLoop(),
|
||||
f1.compile(el).replace(BAD, 'continue'),
|
||||
'}',
|
||||
'BAD;'
|
||||
]);
|
||||
},
|
||||
|
||||
'+': function(el, f1, f2) {
|
||||
return join([
|
||||
f2,
|
||||
el.prev(),
|
||||
'if (!el) BAD;',
|
||||
f1.compile(el)
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
$.selector.Selector.addMethod('compile', function(el) {
|
||||
var l = this.parts.length;
|
||||
|
||||
var expr = this.parts[--l].compile(el);
|
||||
while (l) {
|
||||
var combinator = this.parts[--l];
|
||||
expr = combines[combinator](el, this.parts[--l], as_subexpr(expr));
|
||||
}
|
||||
|
||||
return expr;
|
||||
});
|
||||
|
||||
$.selector.SelectorsGroup.addMethod('compile', function(el) {
|
||||
var expr = [], lbl = ++lbl_id;
|
||||
|
||||
for (var i=0; i < this.parts.length; i++) {
|
||||
expr[expr.length] = join([
|
||||
i == 0 ? el.save(lbl) : el.restore(lbl),
|
||||
'l'+lbl+'_'+i+':{',
|
||||
this.parts[i].compile(el).replace(BAD, 'break l'+lbl+'_'+i),
|
||||
'}'
|
||||
]);
|
||||
}
|
||||
|
||||
expr[expr.length] = 'BAD;';
|
||||
return join(expr);
|
||||
});
|
||||
|
||||
$.selector.SelectorBase.addMethod('matches', function(el){
|
||||
this.matches = new Function('el', join([
|
||||
'if (!el) return false;',
|
||||
this.compile(new State()).replace(BAD, 'return false').replace(GOOD, 'return true')
|
||||
]));
|
||||
return this.matches(el);
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
|
||||
;
|
||||
|
||||
|
||||
/* src/jquery.selector.affectedby.js */
|
||||
|
||||
(function($) {
|
||||
|
||||
// TODO:
|
||||
// Make attributes & IDs work
|
||||
|
||||
var DIRECT = /DIRECT/g;
|
||||
var CONTEXT = /CONTEXT/g;
|
||||
var EITHER = /DIRECT|CONTEXT/g;
|
||||
|
||||
$.selector.SelectorBase.addMethod('affectedBy', function(props) {
|
||||
this.affectedBy = new Function('props', ([
|
||||
'var direct_classes, context_classes, direct_attrs, context_attrs, t;',
|
||||
this.ABC_compile().replace(DIRECT, 'direct').replace(CONTEXT, 'context'),
|
||||
'return {classes: {context: context_classes, direct: direct_classes}, attrs: {context: context_attrs, direct: direct_attrs}};'
|
||||
]).join("\n"));
|
||||
|
||||
// DEBUG: Print out the compiled funciton
|
||||
// console.log(this.selector, ''+this.affectedBy);
|
||||
|
||||
return this.affectedBy(props);
|
||||
});
|
||||
|
||||
$.selector.SimpleSelector.addMethod('ABC_compile', function() {
|
||||
var parts = [];
|
||||
|
||||
$.each(this.classes, function(i, cls){
|
||||
parts[parts.length] = "if (t = props.classes['"+cls+"']) (DIRECT_classes || (DIRECT_classes = {}))['"+cls+"'] = t;";
|
||||
});
|
||||
|
||||
$.each(this.nots, function(i, not){
|
||||
parts[parts.length] = not.ABC_compile();
|
||||
});
|
||||
|
||||
return parts.join("\n");
|
||||
});
|
||||
|
||||
$.selector.Selector.addMethod('ABC_compile', function(arg){
|
||||
var parts = [];
|
||||
var i = this.parts.length-1;
|
||||
|
||||
parts[parts.length] = this.parts[i].ABC_compile();
|
||||
while ((i = i - 2) >= 0) parts[parts.length] = this.parts[i].ABC_compile().replace(EITHER, 'CONTEXT');
|
||||
|
||||
return parts.join("\n");
|
||||
});
|
||||
|
||||
$.selector.SelectorsGroup.addMethod('ABC_compile', function(){
|
||||
var parts = [];
|
||||
|
||||
$.each(this.parts, function(i,part){
|
||||
parts[parts.length] = part.ABC_compile();
|
||||
});
|
||||
|
||||
return parts.join("\n");
|
||||
});
|
||||
|
||||
|
||||
})(jQuery);
|
||||
;
|
||||
|
||||
|
||||
/* src/jquery.focusinout.js */
|
||||
|
||||
(function($){
|
||||
|
||||
/**
|
||||
* Add focusin and focusout support to bind and live for browers other than IE. Designed to be usable in a delegated fashion (like $.live)
|
||||
* Copyright (c) 2007 Jörn Zaefferer
|
||||
*/
|
||||
$.support.focusInOut = !!($.browser.msie);
|
||||
if (!$.support.focusInOut) {
|
||||
// Emulate focusin and focusout by binding focus and blur in capturing mode
|
||||
$.each({focus: 'focusin', blur: 'focusout'}, function(original, fix){
|
||||
$.event.special[fix] = {
|
||||
setup: function(){
|
||||
if (!this.addEventListener) return false;
|
||||
this.addEventListener(original, $.event.special[fix].handler, true);
|
||||
},
|
||||
teardown: function(){
|
||||
if (!this.removeEventListener) return false;
|
||||
this.removeEventListener(original, $.event.special[fix].handler, true);
|
||||
},
|
||||
handler: function(e){
|
||||
arguments[0] = $.event.fix(e);
|
||||
arguments[0].type = fix;
|
||||
return $.event.handle.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
(function(){
|
||||
//IE has some trouble with focusout with select and keyboard navigation
|
||||
var activeFocus = null;
|
||||
|
||||
$(document)
|
||||
.bind('focusin', function(e){
|
||||
var target = e.realTarget || e.target;
|
||||
if (activeFocus && activeFocus !== target) {
|
||||
e.type = 'focusout';
|
||||
$(activeFocus).trigger(e);
|
||||
e.type = 'focusin';
|
||||
e.target = target;
|
||||
}
|
||||
activeFocus = target;
|
||||
})
|
||||
.bind('focusout', function(e){
|
||||
activeFocus = null;
|
||||
});
|
||||
})();
|
||||
|
||||
})(jQuery);;
|
||||
|
||||
|
||||
/* src/jquery.entwine.js */
|
||||
|
||||
try {
|
||||
console.log;
|
||||
}
|
||||
catch (e) {
|
||||
window.console = undefined;
|
||||
}
|
||||
|
||||
(function($) {
|
||||
|
||||
var namespaces = {};
|
||||
|
||||
$.entwine = function() {
|
||||
$.fn.entwine.apply(null, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
* A couple of utility functions for accessing the store outside of this closure, and for making things
|
||||
* operate in a little more easy-to-test manner
|
||||
*/
|
||||
$.extend($.entwine, {
|
||||
/**
|
||||
* Get all the namespaces. Useful for introspection? Internal interface of Namespace not guaranteed consistant
|
||||
*/
|
||||
namespaces: namespaces,
|
||||
|
||||
/**
|
||||
* Remove all entwine rules
|
||||
*/
|
||||
clear_all_rules: function() {
|
||||
// Remove proxy functions
|
||||
for (var k in $.fn) { if ($.fn[k].isentwinemethod) delete $.fn[k]; }
|
||||
// Remove bound events - TODO: Make this pluggable, so this code can be moved to jquery.entwine.events.js
|
||||
$(document).unbind('.entwine');
|
||||
// Remove namespaces, and start over again
|
||||
namespaces = $.entwine.namespaces = {};
|
||||
},
|
||||
|
||||
WARN_LEVEL_NONE: 0,
|
||||
WARN_LEVEL_IMPORTANT: 1,
|
||||
WARN_LEVEL_BESTPRACTISE: 2,
|
||||
|
||||
/**
|
||||
* Warning level. Set to a higher level to get warnings dumped to console.
|
||||
*/
|
||||
warningLevel: 0,
|
||||
|
||||
/** Utility to optionally display warning messages depending on level */
|
||||
warn: function(message, level) {
|
||||
if (level <= $.entwine.warningLevel && console && console.warn) {
|
||||
console.warn(message);
|
||||
if (console.trace) console.trace();
|
||||
}
|
||||
},
|
||||
|
||||
warn_exception: function(where, /* optional: */ on, e) {
|
||||
if ($.entwine.WARN_LEVEL_IMPORTANT <= $.entwine.warningLevel && console && console.warn) {
|
||||
if (arguments.length == 2) { e = on; on = null; }
|
||||
|
||||
if (on) console.warn('Uncaught exception',e,'in',where,'on',on);
|
||||
else console.warn('Uncaught exception',e,'in',where);
|
||||
|
||||
if (e.stack) console.warn("Stack Trace:\n" + e.stack);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/** Stores a count of definitions, so that we can sort identical selectors by definition order */
|
||||
var rulecount = 0;
|
||||
|
||||
var Rule = Base.extend({
|
||||
init: function(selector, name) {
|
||||
this.selector = selector;
|
||||
this.specifity = selector.specifity();
|
||||
this.important = 0;
|
||||
this.name = name;
|
||||
this.rulecount = rulecount++;
|
||||
}
|
||||
});
|
||||
|
||||
Rule.compare = function(a, b) {
|
||||
var as = a.specifity, bs = b.specifity;
|
||||
|
||||
return (a.important - b.important) ||
|
||||
(as[0] - bs[0]) ||
|
||||
(as[1] - bs[1]) ||
|
||||
(as[2] - bs[2]) ||
|
||||
(a.rulecount - b.rulecount) ;
|
||||
};
|
||||
|
||||
$.entwine.RuleList = function() {
|
||||
var list = [];
|
||||
|
||||
list.addRule = function(selector, name){
|
||||
var rule = Rule(selector, name);
|
||||
|
||||
list[list.length] = rule;
|
||||
list.sort(Rule.compare);
|
||||
|
||||
return rule;
|
||||
};
|
||||
|
||||
return list;
|
||||
};
|
||||
|
||||
var handlers = [];
|
||||
|
||||
/**
|
||||
* A Namespace holds all the information needed for adding entwine methods to a namespace (including the _null_ namespace)
|
||||
*/
|
||||
$.entwine.Namespace = Base.extend({
|
||||
init: function(name){
|
||||
if (name && !name.match(/^[A-Za-z0-9.]+$/)) $.entwine.warn('Entwine namespace '+name+' is not formatted as period seperated identifiers', $.entwine.WARN_LEVEL_BESTPRACTISE);
|
||||
name = name || '__base';
|
||||
|
||||
this.name = name;
|
||||
this.store = {};
|
||||
|
||||
namespaces[name] = this;
|
||||
|
||||
if (name == "__base") {
|
||||
this.injectee = $.fn;
|
||||
this.$ = $;
|
||||
}
|
||||
else {
|
||||
// We're in a namespace, so we build a Class that subclasses the jQuery Object Class to inject namespace functions into
|
||||
|
||||
// jQuery 1.5 already provides a nice way to subclass, so use it
|
||||
if ($.sub) {
|
||||
this.$ = $.sub();
|
||||
this.injectee = this.$.prototype;
|
||||
}
|
||||
// For jQuery < 1.5 we have to do it ourselves
|
||||
else {
|
||||
var subfn = function(){};
|
||||
this.injectee = subfn.prototype = new $;
|
||||
|
||||
// And then we provide an overriding $ that returns objects of our new Class, and an overriding pushStack to catch further selection building
|
||||
var bound$ = this.$ = function(a) {
|
||||
// Try the simple way first
|
||||
var jq = $.fn.init.apply(new subfn(), arguments);
|
||||
if (jq instanceof subfn) return jq;
|
||||
|
||||
// That didn't return a bound object, so now we need to copy it
|
||||
var rv = new subfn();
|
||||
rv.selector = jq.selector; rv.context = jq.context; var i = rv.length = jq.length;
|
||||
while (i--) rv[i] = jq[i];
|
||||
return rv;
|
||||
};
|
||||
|
||||
this.injectee.pushStack = function(elems, name, selector){
|
||||
var ret = bound$(elems);
|
||||
|
||||
// Add the old object onto the stack (as a reference)
|
||||
ret.prevObject = this;
|
||||
ret.context = this.context;
|
||||
|
||||
if ( name === "find" ) ret.selector = this.selector + (this.selector ? " " : "") + selector;
|
||||
else if ( name ) ret.selector = this.selector + "." + name + "(" + selector + ")";
|
||||
|
||||
// Return the newly-formed element set
|
||||
return ret;
|
||||
};
|
||||
|
||||
// Copy static functions through from $ to this.$ so e.g. $.ajax still works
|
||||
// @bug, @cantfix: Any class functions added to $ after this call won't get mirrored through
|
||||
$.extend(this.$, $);
|
||||
}
|
||||
|
||||
// We override entwine to inject the name of this namespace when defining blocks inside this namespace
|
||||
var entwine_wrapper = this.injectee.entwine = function(spacename) {
|
||||
var args = arguments;
|
||||
|
||||
if (!spacename || typeof spacename != 'string') { args = $.makeArray(args); args.unshift(name); }
|
||||
else if (spacename.charAt(0) != '.') args[0] = name+'.'+spacename;
|
||||
|
||||
return $.fn.entwine.apply(this, args);
|
||||
};
|
||||
|
||||
this.$.entwine = function() {
|
||||
entwine_wrapper.apply(null, arguments);
|
||||
};
|
||||
|
||||
for (var i = 0; i < handlers.length; i++) {
|
||||
var handler = handlers[i], builder;
|
||||
|
||||
// Inject jQuery object method overrides
|
||||
if (builder = handler.namespaceMethodOverrides) {
|
||||
var overrides = builder(this);
|
||||
for (var k in overrides) this.injectee[k] = overrides[k];
|
||||
}
|
||||
|
||||
// Inject $.entwine function overrides
|
||||
if (builder = handler.namespaceStaticOverrides) {
|
||||
var overrides = builder(this);
|
||||
for (var k in overrides) this.$.entwine[k] = overrides[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a function that does selector matching against the function list for a function name
|
||||
* Used by proxy for all calls, and by ctorProxy to handle _super calls
|
||||
* @param {String} name - name of the function as passed in the construction object
|
||||
* @param {String} funcprop - the property on the Rule object that gives the actual function to call
|
||||
* @param {function} basefunc - the non-entwine function to use as the catch-all function at the bottom of the stack
|
||||
*/
|
||||
one: function(name, funcprop, basefunc) {
|
||||
var namespace = this;
|
||||
var funcs = this.store[name];
|
||||
|
||||
var one = function(el, args, i){
|
||||
if (i === undefined) i = funcs.length;
|
||||
while (i--) {
|
||||
if (funcs[i].selector.matches(el)) {
|
||||
var ret, tmp_i = el.i, tmp_f = el.f;
|
||||
el.i = i; el.f = one;
|
||||
try { ret = funcs[i][funcprop].apply(namespace.$(el), args); }
|
||||
finally { el.i = tmp_i; el.f = tmp_f; }
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
// If we didn't find a entwine-defined function, but there is a non-entwine function to use as a base, try that
|
||||
if (basefunc) return basefunc.apply(namespace.$(el), args);
|
||||
};
|
||||
|
||||
return one;
|
||||
},
|
||||
|
||||
/**
|
||||
* A proxy is a function attached to a callable object (either the base jQuery.fn or a subspace object) which handles
|
||||
* finding and calling the correct function for each member of the current jQuery context
|
||||
* @param {String} name - name of the function as passed in the construction object
|
||||
* @param {function} basefunc - the non-entwine function to use as the catch-all function at the bottom of the stack
|
||||
*/
|
||||
build_proxy: function(name, basefunc) {
|
||||
var one = this.one(name, 'func', basefunc);
|
||||
|
||||
var prxy = function() {
|
||||
var rv, ctx = $(this);
|
||||
|
||||
var i = ctx.length;
|
||||
while (i--) rv = one(ctx[i], arguments);
|
||||
return rv;
|
||||
};
|
||||
|
||||
return prxy;
|
||||
},
|
||||
|
||||
bind_proxy: function(selector, name, func) {
|
||||
var rulelist = this.store[name] || (this.store[name] = $.entwine.RuleList());
|
||||
|
||||
var rule = rulelist.addRule(selector, name); rule.func = func;
|
||||
|
||||
if (!this.injectee.hasOwnProperty(name) || !this.injectee[name].isentwinemethod) {
|
||||
this.injectee[name] = this.build_proxy(name, this.injectee.hasOwnProperty(name) ? this.injectee[name] : null);
|
||||
this.injectee[name].isentwinemethod = true;
|
||||
}
|
||||
|
||||
if (!this.injectee[name].isentwinemethod) {
|
||||
$.entwine.warn('Warning: Entwine function '+name+' clashes with regular jQuery function - entwine function will not be callable directly on jQuery object', $.entwine.WARN_LEVEL_IMPORTANT);
|
||||
}
|
||||
},
|
||||
|
||||
add: function(selector, data) {
|
||||
// For every item in the hash, try ever method handler, until one returns true
|
||||
for (var k in data) {
|
||||
var v = data[k];
|
||||
|
||||
for (var i = 0; i < handlers.length; i++) {
|
||||
if (handlers[i].bind && handlers[i].bind.call(this, selector, k, v)) break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
has: function(ctx, name) {
|
||||
var rulelist = this.store[name];
|
||||
if (!rulelist) return false;
|
||||
|
||||
/* We go forward this time, since low specifity is likely to knock out a bunch of elements quickly */
|
||||
for (var i = 0 ; i < rulelist.length; i++) {
|
||||
ctx = ctx.not(rulelist[i].selector);
|
||||
if (!ctx.length) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* A handler is some javascript code that adds support for some time of key / value pair passed in the hash to the Namespace add method.
|
||||
* The default handlers provided (and included by default) are event, ctor and properties
|
||||
*/
|
||||
$.entwine.Namespace.addHandler = function(handler) {
|
||||
for (var i = 0; i < handlers.length && handlers[i].order < handler.order; i++) { /* Pass */ }
|
||||
handlers.splice(i, 0, handler);
|
||||
};
|
||||
|
||||
$.entwine.Namespace.addHandler({
|
||||
order: 50,
|
||||
|
||||
bind: function(selector, k, v){
|
||||
if ($.isFunction(v)) {
|
||||
this.bind_proxy(selector, k, v);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$.extend($.fn, {
|
||||
/**
|
||||
* Main entwine function. Used for new definitions, calling into a namespace (or forcing the base namespace) and entering a using block
|
||||
*
|
||||
*/
|
||||
entwine: function(spacename) {
|
||||
var i = 0;
|
||||
/* Don't actually work out selector until we try and define something on it - we might be opening a namespace on an function-traveresed object
|
||||
which have non-standard selectors like .parents(.foo).slice(0,1) */
|
||||
var selector = null;
|
||||
|
||||
/* By default we operator on the base namespace */
|
||||
var namespace = namespaces.__base || $.entwine.Namespace();
|
||||
|
||||
/* If the first argument is a string, then it's the name of a namespace. Look it up */
|
||||
if (typeof spacename == 'string') {
|
||||
if (spacename.charAt('0') == '.') spacename = spacename.substr(1);
|
||||
if (spacename) namespace = namespaces[spacename] || $.entwine.Namespace(spacename);
|
||||
i=1;
|
||||
}
|
||||
|
||||
/* All remaining arguments should either be using blocks or definition hashs */
|
||||
while (i < arguments.length) {
|
||||
var res = arguments[i++];
|
||||
|
||||
// If it's a function, call it - either it's a using block or it's a namespaced entwine definition
|
||||
if ($.isFunction(res)) {
|
||||
if (res.length != 1) $.entwine.warn('Function block inside entwine definition does not take $ argument properly', $.entwine.WARN_LEVEL_IMPORTANT);
|
||||
res = res.call(namespace.$(this), namespace.$);
|
||||
}
|
||||
|
||||
// If we have a entwine definition hash, inject it into namespace
|
||||
if (res) {
|
||||
if (selector === null) selector = this.selector ? $.selector(this.selector) : false;
|
||||
|
||||
if (selector) namespace.add(selector, res);
|
||||
else $.entwine.warn('Entwine block given to entwine call without selector. Make sure you call $(selector).entwine when defining blocks', $.entwine.WARN_LEVEL_IMPORTANT);
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally, return the jQuery object 'this' refers to, wrapped in the new namespace */
|
||||
return namespace.$(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Calls the next most specific version of the current entwine method
|
||||
*/
|
||||
_super: function(){
|
||||
var rv, i = this.length;
|
||||
while (i--) {
|
||||
var el = this[0];
|
||||
rv = el.f(el, arguments, el.i);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
;
|
||||
|
||||
|
||||
/* src/jquery.entwine.dommaybechanged.js */
|
||||
|
||||
(function($){
|
||||
|
||||
/** Utility function to monkey-patch a jQuery method */
|
||||
var monkey = function( /* method, method, ...., patch */){
|
||||
var methods = $.makeArray(arguments);
|
||||
var patch = methods.pop();
|
||||
|
||||
$.each(methods, function(i, method){
|
||||
var old = $.fn[method];
|
||||
|
||||
$.fn[method] = function() {
|
||||
var self = this, args = $.makeArray(arguments);
|
||||
|
||||
var rv = old.apply(self, args);
|
||||
patch.apply(self, args);
|
||||
return rv;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** What to call to run a function 'soon'. Normally setTimeout, but for syncronous mode we override so soon === now */
|
||||
var runSoon = window.setTimeout;
|
||||
|
||||
/** The timer handle for the asyncronous matching call */
|
||||
var ChangeDetails = Base.extend({
|
||||
|
||||
init: function() {
|
||||
this.global = false;
|
||||
this.attrs = {};
|
||||
this.classes = {};
|
||||
},
|
||||
|
||||
/** Fire the change event. Only fires on the document node, so bind to that */
|
||||
triggerEvent: function() {
|
||||
// If we're not the active changes instance any more, don't trigger
|
||||
if (changes != this) return;
|
||||
|
||||
// Cancel any pending timeout (if we're directly called in the mean time)
|
||||
if (this.check_id) clearTimeout(this.check_id);
|
||||
|
||||
// Create a new event object
|
||||
var event = $.Event("DOMMaybeChanged");
|
||||
event.changes = this;
|
||||
|
||||
// Reset the global changes object to be a new instance (do before trigger, in case trigger fires changes itself)
|
||||
changes = new ChangeDetails();
|
||||
|
||||
// Fire event
|
||||
$(document).triggerHandler(event);
|
||||
},
|
||||
|
||||
changed: function() {
|
||||
if (!this.check_id) {
|
||||
var self = this;
|
||||
this.check_id = runSoon(function(){ self.check_id = null; self.triggerEvent(); }, 10);
|
||||
}
|
||||
},
|
||||
|
||||
addAll: function() {
|
||||
if (this.global) return this; // If we've already flagged as a global change, just skip
|
||||
|
||||
this.global = true;
|
||||
this.changed();
|
||||
return this;
|
||||
},
|
||||
|
||||
addSubtree: function(node) {
|
||||
return this.addAll();
|
||||
},
|
||||
|
||||
/* For now we don't do this. It's expensive, and jquery.entwine.ctors doesn't use this information anyway */
|
||||
addSubtreeFuture: function(node) {
|
||||
if (this.global) return this; // If we've already flagged as a global change, just skip
|
||||
|
||||
this.subtree = this.subtree ? this.subtree.add(node) : $(node);
|
||||
this.changed();
|
||||
return this;
|
||||
},
|
||||
|
||||
addAttr: function(attr, node) {
|
||||
if (this.global) return this;
|
||||
|
||||
this.attrs[attr] = (attr in this.attrs) ? this.attrs[attr].add(node) : $(node);
|
||||
this.changed();
|
||||
return this;
|
||||
},
|
||||
|
||||
addClass: function(klass, node) {
|
||||
if (this.global) return this;
|
||||
|
||||
this.classes[klass] = (klass in this.classes) ? this.classes[klass].add(node) : $(node);
|
||||
this.changed();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
var changes = new ChangeDetails();
|
||||
|
||||
|
||||
monkey('append', 'prepend', 'empty', 'html', function(){
|
||||
changes.addSubtree(this);
|
||||
});
|
||||
|
||||
monkey('after', 'before', 'remove', 'detach', function(){
|
||||
changes.addSubtree(this.parent());
|
||||
})
|
||||
|
||||
monkey('removeAttr', function(attr){
|
||||
changes.addAttr(attr, this);
|
||||
});
|
||||
|
||||
monkey('addClass', 'removeClass', 'toggleClass', function(klass){
|
||||
if (typeof klass == 'string') changes.addClass(klass, this);
|
||||
});
|
||||
|
||||
monkey('attr', function(a, b){
|
||||
if (b !== undefined && typeof a == 'string') changes.addAttr(a, this);
|
||||
else if (typeof a != 'string') { for (var k in a) changes.addAttr(k, this); }
|
||||
});
|
||||
|
||||
/*
|
||||
These manipulation functions call one or more of the above to do the actual manipulation:
|
||||
appendTo -> append
|
||||
prependTo -> prepend
|
||||
insertBefore -> before
|
||||
insertAfter -> after
|
||||
replaceWith -> before || append
|
||||
replaceAll -> replaceWith
|
||||
text -> empty, appendWith
|
||||
wrapAll -> insertBefore, append
|
||||
wrapInner -> wrapAll || append
|
||||
wrap -> wrapAll
|
||||
unwrap -> replaceWith
|
||||
*/
|
||||
|
||||
$.extend($.entwine, {
|
||||
/**
|
||||
* Make onmatch and onunmatch work in synchronous mode - that is, new elements will be detected immediately after
|
||||
* the DOM manipulation that made them match. This is only really useful for during testing, since it's pretty slow
|
||||
* (otherwise we'd make it the default).
|
||||
*/
|
||||
synchronous_mode: function() {
|
||||
if (changes && changes.check_id) clearTimeout(changes.check_id);
|
||||
changes = new ChangeDetails();
|
||||
|
||||
runSoon = function(func, delay){ func.call(this); return null; };
|
||||
},
|
||||
|
||||
/**
|
||||
* Trigger onmatch and onunmatch now - usefull for after DOM manipulation by methods other than through jQuery.
|
||||
* Called automatically on document.ready
|
||||
*/
|
||||
triggerMatching: function() {
|
||||
changes.addAll(); //.triggerEvent();
|
||||
}
|
||||
});
|
||||
|
||||
// And on DOM ready, trigger matching once
|
||||
$(function(){
|
||||
$.entwine.triggerMatching();
|
||||
});
|
||||
|
||||
})(jQuery);;
|
||||
|
||||
|
||||
/* src/jquery.entwine.events.js */
|
||||
|
||||
(function($) {
|
||||
|
||||
/** Taken from jQuery 1.5.2 for backwards compatibility */
|
||||
if ($.support.changeBubbles == undefined) {
|
||||
$.support.changeBubbles = true;
|
||||
|
||||
var el = document.createElement("div");
|
||||
eventName = "onchange";
|
||||
|
||||
if (el.attachEvent) {
|
||||
var isSupported = (eventName in el);
|
||||
if (!isSupported) {
|
||||
el.setAttribute(eventName, "return;");
|
||||
isSupported = typeof el[eventName] === "function";
|
||||
}
|
||||
|
||||
$.support.changeBubbles = isSupported;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return true if node b is the same as, or is a descendant of, node a */
|
||||
if (document.compareDocumentPosition) {
|
||||
var is_or_contains = function(a, b) {
|
||||
return a && b && (a == b || !!(a.compareDocumentPosition(b) & 16));
|
||||
};
|
||||
}
|
||||
else {
|
||||
var is_or_contains = function(a, b) {
|
||||
return a && b && (a == b || (a.contains ? a.contains(b) : true));
|
||||
};
|
||||
}
|
||||
|
||||
/* Add the methods to handle event binding to the Namespace class */
|
||||
$.entwine.Namespace.addMethods({
|
||||
build_event_proxy: function(name) {
|
||||
var one = this.one(name, 'func');
|
||||
|
||||
var prxy = function(e, data) {
|
||||
// For events that do not bubble we manually trigger delegation (see delegate_submit below)
|
||||
// If this event is a manual trigger, the event we actually want to bubble is attached as a property of the passed event
|
||||
e = e.delegatedEvent || e;
|
||||
|
||||
var el = e.target;
|
||||
while (el && el.nodeType == 1 && !e.isPropagationStopped()) {
|
||||
var ret = one(el, arguments);
|
||||
if (ret !== undefined) e.result = ret;
|
||||
if (ret === false) { e.preventDefault(); e.stopPropagation(); }
|
||||
|
||||
el = el.parentNode;
|
||||
}
|
||||
};
|
||||
|
||||
return prxy;
|
||||
},
|
||||
|
||||
build_mouseenterleave_proxy: function(name) {
|
||||
var one = this.one(name, 'func');
|
||||
|
||||
var prxy = function(e) {
|
||||
var el = e.target;
|
||||
var rel = e.relatedTarget;
|
||||
|
||||
while (el && el.nodeType == 1 && !e.isPropagationStopped()) {
|
||||
/* We know el contained target. If it also contains relatedTarget then we didn't mouseenter / leave. What's more, every ancestor will also
|
||||
contan el and rel, and so we can just stop bubbling */
|
||||
if (is_or_contains(el, rel)) break;
|
||||
|
||||
var ret = one(el, arguments);
|
||||
if (ret !== undefined) e.result = ret;
|
||||
if (ret === false) { e.preventDefault(); e.stopPropagation(); }
|
||||
|
||||
el = el.parentNode;
|
||||
}
|
||||
};
|
||||
|
||||
return prxy;
|
||||
},
|
||||
|
||||
build_change_proxy: function(name) {
|
||||
var one = this.one(name, 'func');
|
||||
|
||||
/*
|
||||
This change bubble emulation code is taken mostly from jQuery 1.6 - unfortunately we can't easily reuse any of
|
||||
it without duplication, so we'll have to re-migrate any bugfixes
|
||||
*/
|
||||
|
||||
// Get the value of an item. Isn't supposed to be interpretable, just stable for some value, and different
|
||||
// once the value changes
|
||||
var getVal = function( elem ) {
|
||||
var type = elem.type, val = elem.value;
|
||||
|
||||
if (type === "radio" || type === "checkbox") {
|
||||
val = elem.checked;
|
||||
}
|
||||
else if (type === "select-multiple") {
|
||||
val = "";
|
||||
if (elem.selectedIndex > -1) {
|
||||
val = jQuery.map(elem.options, function(elem){ return elem.selected; }).join("-");
|
||||
}
|
||||
}
|
||||
else if (jQuery.nodeName(elem, "select")) {
|
||||
val = elem.selectedIndex;
|
||||
}
|
||||
|
||||
return val;
|
||||
};
|
||||
|
||||
// Test if a node name is a form input
|
||||
var rformElems = /^(?:textarea|input|select)$/i;
|
||||
|
||||
// Check if this event is a change, and bubble the change event if it is
|
||||
var testChange = function(e) {
|
||||
var elem = e.target, data, val;
|
||||
|
||||
if (!rformElems.test(elem.nodeName) || elem.readOnly) return;
|
||||
|
||||
data = jQuery.data(elem, "_entwine_change_data");
|
||||
val = getVal(elem);
|
||||
|
||||
// the current data will be also retrieved by beforeactivate
|
||||
if (e.type !== "focusout" || elem.type !== "radio") {
|
||||
jQuery.data(elem, "_entwine_change_data", val);
|
||||
}
|
||||
|
||||
if (data === undefined || val === data) return;
|
||||
|
||||
if (data != null || val) {
|
||||
e.type = "change";
|
||||
|
||||
while (elem && elem.nodeType == 1 && !e.isPropagationStopped()) {
|
||||
var ret = one(elem, arguments);
|
||||
if (ret !== undefined) e.result = ret;
|
||||
if (ret === false) { e.preventDefault(); e.stopPropagation(); }
|
||||
|
||||
elem = elem.parentNode;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// The actual proxy - responds to several events, some of which triger a change check, some
|
||||
// of which just store the value for future change checks
|
||||
var prxy = function(e) {
|
||||
var event = e.type, elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : "";
|
||||
|
||||
switch (event) {
|
||||
case 'focusout':
|
||||
case 'beforedeactivate':
|
||||
testChange.apply(this, arguments);
|
||||
break;
|
||||
|
||||
case 'click':
|
||||
if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) {
|
||||
testChange.apply(this, arguments);
|
||||
}
|
||||
break;
|
||||
|
||||
// Change has to be called before submit
|
||||
// Keydown will be called before keypress, which is used in submit-event delegation
|
||||
case 'keydown':
|
||||
if (
|
||||
(e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) ||
|
||||
(e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
|
||||
type === "select-multiple"
|
||||
) {
|
||||
testChange.apply(this, arguments);
|
||||
}
|
||||
break;
|
||||
|
||||
// Beforeactivate happens also before the previous element is blurred
|
||||
// with this event you can't trigger a change event, but you can store
|
||||
// information
|
||||
case 'focusin':
|
||||
case 'beforeactivate':
|
||||
jQuery.data( elem, "_entwine_change_data", getVal(elem) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return prxy;
|
||||
},
|
||||
|
||||
bind_event: function(selector, name, func, event) {
|
||||
var funcs = this.store[name] || (this.store[name] = $.entwine.RuleList()) ;
|
||||
var proxies = funcs.proxies || (funcs.proxies = {});
|
||||
|
||||
var rule = funcs.addRule(selector, name); rule.func = func;
|
||||
|
||||
if (!proxies[name]) {
|
||||
switch (name) {
|
||||
case 'onmouseenter':
|
||||
proxies[name] = this.build_mouseenterleave_proxy(name);
|
||||
event = 'mouseover';
|
||||
break;
|
||||
case 'onmouseleave':
|
||||
proxies[name] = this.build_mouseenterleave_proxy(name);
|
||||
event = 'mouseout';
|
||||
break;
|
||||
case 'onchange':
|
||||
if (!$.support.changeBubbles) {
|
||||
proxies[name] = this.build_change_proxy(name);
|
||||
event = 'click keydown focusin focusout beforeactivate beforedeactivate';
|
||||
}
|
||||
break;
|
||||
case 'onsubmit':
|
||||
event = 'delegatedSubmit';
|
||||
break;
|
||||
case 'onfocus':
|
||||
case 'onblur':
|
||||
$.entwine.warn('Event '+event+' not supported - using focusin / focusout instead', $.entwine.WARN_LEVEL_IMPORTANT);
|
||||
}
|
||||
|
||||
// If none of the special handlers created a proxy, use the generic proxy
|
||||
if (!proxies[name]) proxies[name] = this.build_event_proxy(name);
|
||||
|
||||
$(document).bind(event.replace(/(\s+|$)/g, '.entwine$1'), proxies[name]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$.entwine.Namespace.addHandler({
|
||||
order: 40,
|
||||
|
||||
bind: function(selector, k, v){
|
||||
var match, event;
|
||||
if ($.isFunction(v) && (match = k.match(/^on(.*)/))) {
|
||||
event = match[1];
|
||||
this.bind_event(selector, k, v, event);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Find all forms and bind onsubmit to trigger on the document too.
|
||||
// This is the only event that can't be grabbed via delegation
|
||||
|
||||
var form_binding_cache = $([]); // A cache for already-handled form elements
|
||||
var delegate_submit = function(e, data){
|
||||
var delegationEvent = $.Event('delegatedSubmit'); delegationEvent.delegatedEvent = e;
|
||||
return $(document).trigger(delegationEvent, data);
|
||||
};
|
||||
|
||||
$(document).bind('DOMMaybeChanged', function(){
|
||||
var forms = $('form');
|
||||
// Only bind to forms we haven't processed yet
|
||||
forms.not(form_binding_cache).bind('submit', delegate_submit);
|
||||
// Then remember the current set of forms
|
||||
form_binding_cache = forms;
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
;
|
||||
|
||||
|
||||
/* src/jquery.entwine.ctors.js */
|
||||
|
||||
(function($) {
|
||||
|
||||
/* Add the methods to handle constructor & destructor binding to the Namespace class */
|
||||
$.entwine.Namespace.addMethods({
|
||||
bind_condesc: function(selector, name, func) {
|
||||
var ctors = this.store.ctors || (this.store.ctors = $.entwine.RuleList()) ;
|
||||
|
||||
var rule;
|
||||
for (var i = 0 ; i < ctors.length; i++) {
|
||||
if (ctors[i].selector.selector == selector.selector) {
|
||||
rule = ctors[i]; break;
|
||||
}
|
||||
}
|
||||
if (!rule) {
|
||||
rule = ctors.addRule(selector, 'ctors');
|
||||
}
|
||||
|
||||
rule[name] = func;
|
||||
|
||||
if (!ctors[name+'proxy']) {
|
||||
var one = this.one('ctors', name);
|
||||
var namespace = this;
|
||||
|
||||
var proxy = function(els, i, func) {
|
||||
var j = els.length;
|
||||
while (j--) {
|
||||
var el = els[j];
|
||||
|
||||
var tmp_i = el.i, tmp_f = el.f;
|
||||
el.i = i; el.f = one;
|
||||
|
||||
try { func.call(namespace.$(el)); }
|
||||
catch(e) { $.entwine.warn_exception(name, el, e); }
|
||||
finally { el.i = tmp_i; el.f = tmp_f; }
|
||||
}
|
||||
};
|
||||
|
||||
ctors[name+'proxy'] = proxy;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$.entwine.Namespace.addHandler({
|
||||
order: 30,
|
||||
|
||||
bind: function(selector, k, v) {
|
||||
if ($.isFunction(v) && (k == 'onmatch' || k == 'onunmatch')) {
|
||||
// When we add new matchers we need to trigger a full global recalc once, regardless of the DOM changes that triggered the event
|
||||
this.matchersDirty = true;
|
||||
|
||||
this.bind_condesc(selector, k, v);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Finds all the elements that now match a different rule (or have been removed) and call onmatch on onunmatch as appropriate
|
||||
*
|
||||
* Because this has to scan the DOM, and is therefore fairly slow, this is normally triggered off a short timeout, so that
|
||||
* a series of DOM manipulations will only trigger this once.
|
||||
*
|
||||
* The downside of this is that things like:
|
||||
* $('#foo').addClass('tabs'); $('#foo').tabFunctionBar();
|
||||
* won't work.
|
||||
*/
|
||||
$(document).bind('DOMMaybeChanged', function(e){
|
||||
// Get the change delta. Can help stop us from doing heavy lifting if none of the changes could actually trigger an onmatch or onunmatch function
|
||||
var changes = e.changes;
|
||||
|
||||
// var start = (new Date).getTime();
|
||||
|
||||
// For every namespace
|
||||
for (var k in $.entwine.namespaces) {
|
||||
var namespace = $.entwine.namespaces[k];
|
||||
|
||||
// That has constructors or destructors
|
||||
var ctors = namespace.store.ctors;
|
||||
if (ctors) {
|
||||
|
||||
// Keep a record of elements that have matched some previous more specific rule.
|
||||
// Not that we _don't_ actually do that until this is needed. If matched is null, it's not been calculated yet.
|
||||
// We also keep track of any elements that have newly been taken or released by a specific rule
|
||||
var matched = null, taken = $([]), released = $([]);
|
||||
|
||||
// Updates matched to contain all the previously matched elements as if we'd been keeping track all along
|
||||
var calcmatched = function(j){
|
||||
if (matched !== null) return;
|
||||
matched = $([]);
|
||||
|
||||
var cache, k = ctors.length;
|
||||
while ((--k) > j) {
|
||||
if (cache = ctors[k].cache) matched = matched.add(cache);
|
||||
}
|
||||
}
|
||||
|
||||
// Some declared variables used in the loop
|
||||
var add, rem, res, rule, sel, ctor, dtor, full;
|
||||
|
||||
// Stepping through each selector from most to least specific
|
||||
var j = ctors.length;
|
||||
while (j--) {
|
||||
// Build some quick-access variables
|
||||
rule = ctors[j];
|
||||
sel = rule.selector.selector;
|
||||
ctor = rule.onmatch;
|
||||
dtor = rule.onunmatch;
|
||||
|
||||
/*
|
||||
Rule.cache might be stale or fresh. It'll be stale if
|
||||
- some more specific selector now has some of rule.cache in it
|
||||
- some change has happened that means new elements match this selector now
|
||||
- some change has happened that means elements no longer match this selector
|
||||
|
||||
The first we can just compare rules.cache with matched, removing anything that's there already.
|
||||
*/
|
||||
|
||||
// Reset the "elements that match this selector and no more specific selector with an onmatch rule" to null.
|
||||
// Staying null means this selector is fresh.
|
||||
res = null;
|
||||
|
||||
// If this gets changed to true, it's too hard to do a delta update, so do a full update
|
||||
full = false;
|
||||
|
||||
if (namespace.matchersDirty || changes.global) {
|
||||
// For now, just fall back to old version. We need to do something like changed.Subtree.find('*').andSelf().filter(sel), but that's _way_ slower on modern browsers than the below
|
||||
full = true;
|
||||
}
|
||||
else {
|
||||
// We don't deal with attributes yet, so any attribute change means we need to do a full recalc
|
||||
for (var k in e.changes.attrs) { full = true; break; }
|
||||
|
||||
/*
|
||||
If a class changes, but it isn't listed in our selector, we don't care - the change couldn't affect whether or not any element matches
|
||||
|
||||
If it is listed on our selector
|
||||
- If it is on the direct match part, it could have added or removed the node it changed on
|
||||
- If it is on the context part, it could have added or removed any node that were previously included or excluded because of a match or failure to match with the context required on that node
|
||||
- NOTE: It might be on _both_
|
||||
*/
|
||||
|
||||
var method = rule.selector.affectedBy(e.changes);
|
||||
|
||||
if (method.classes.context) {
|
||||
full = true;
|
||||
}
|
||||
else {
|
||||
for (var k in method.classes.direct) {
|
||||
calcmatched(j);
|
||||
var recheck = e.changes.classes[k].not(matched);
|
||||
|
||||
if (res === null) {
|
||||
res = rule.cache ? rule.cache.not(taken).add(released.filter(sel)) : $([]);
|
||||
}
|
||||
|
||||
res = res.not(recheck).add(recheck.filter(sel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (full) {
|
||||
calcmatched(j);
|
||||
res = $(sel).not(matched);
|
||||
}
|
||||
else {
|
||||
if (!res) {
|
||||
// We weren't stale because of any changes to the DOM that affected this selector, but more specific
|
||||
// onmatches might have caused stale-ness
|
||||
|
||||
// Do any of the previous released elements match this selector?
|
||||
add = released.length && released.filter(sel);
|
||||
|
||||
if (add && add.length) {
|
||||
// Yes, so we're stale as we need to include them. Filter for any possible taken value at the same time
|
||||
res = rule.cache ? rule.cache.not(taken).add(add) : add;
|
||||
}
|
||||
else {
|
||||
// Do we think we own any of the elements now taken by more specific rules?
|
||||
rem = taken.length && rule.cache && rule.cache.filter(taken);
|
||||
|
||||
if (rem && rem.length) {
|
||||
// Yes, so we're stale as we need to exclude them.
|
||||
res = rule.cache.not(rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Res will be null if we know we are fresh (no full needed, selector not affectedBy changes)
|
||||
if (res === null) {
|
||||
// If we are tracking matched, add ourselves
|
||||
if (matched && rule.cache) matched = matched.add(rule.cache);
|
||||
}
|
||||
else {
|
||||
// If this selector has a list of elements it matched against last time
|
||||
if (rule.cache) {
|
||||
// Find the ones that are extra this time
|
||||
add = res.not(rule.cache);
|
||||
rem = rule.cache.not(res);
|
||||
}
|
||||
else {
|
||||
add = res; rem = null;
|
||||
}
|
||||
|
||||
if ((add && add.length) || (rem && rem.length)) {
|
||||
if (rem && rem.length) {
|
||||
released = released.add(rem);
|
||||
|
||||
if (dtor && !rule.onunmatchRunning) {
|
||||
rule.onunmatchRunning = true;
|
||||
ctors.onunmatchproxy(rem, j, dtor);
|
||||
rule.onunmatchRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Call the constructor on the newly matched ones
|
||||
if (add && add.length) {
|
||||
taken = taken.add(add);
|
||||
released = released.not(add);
|
||||
|
||||
if (ctor && !rule.onmatchRunning) {
|
||||
rule.onmatchRunning = true;
|
||||
ctors.onmatchproxy(add, j, ctor);
|
||||
rule.onmatchRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we are tracking matched, add ourselves
|
||||
if (matched) matched = matched.add(res);
|
||||
|
||||
// And remember this list of matching elements again this selector, so next matching we can find the unmatched ones
|
||||
rule.cache = res;
|
||||
}
|
||||
}
|
||||
|
||||
namespace.matchersDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
// console.log((new Date).getTime() - start);
|
||||
});
|
||||
|
||||
|
||||
})(jQuery);
|
||||
;
|
||||
|
||||
|
||||
/* src/jquery.entwine.properties.js */
|
||||
|
||||
(function($) {
|
||||
|
||||
var entwine_prepend = '__entwine!';
|
||||
|
||||
var getEntwineData = function(el, namespace, property) {
|
||||
return el.data(entwine_prepend + namespace + '!' + property);
|
||||
};
|
||||
|
||||
var setEntwineData = function(el, namespace, property, value) {
|
||||
return el.data(entwine_prepend + namespace + '!' + property, value);
|
||||
};
|
||||
|
||||
var getEntwineDataAsHash = function(el, namespace) {
|
||||
var hash = {};
|
||||
var id = jQuery.data(el[0]);
|
||||
|
||||
var matchstr = entwine_prepend + namespace + '!';
|
||||
var matchlen = matchstr.length;
|
||||
|
||||
var cache = jQuery.cache[id];
|
||||
for (var k in cache) {
|
||||
if (k.substr(0,matchlen) == matchstr) hash[k.substr(matchlen)] = cache[k];
|
||||
}
|
||||
|
||||
return hash;
|
||||
};
|
||||
|
||||
var setEntwineDataFromHash = function(el, namespace, hash) {
|
||||
for (var k in hash) setEntwineData(namespace, k, hash[k]);
|
||||
};
|
||||
|
||||
var entwineData = function(el, namespace, args) {
|
||||
switch (args.length) {
|
||||
case 0:
|
||||
return getEntwineDataAsHash(el, namespace);
|
||||
case 1:
|
||||
if (typeof args[0] == 'string') return getEntwineData(el, namespace, args[0]);
|
||||
else return setEntwineDataFromHash(el, namespace, args[0]);
|
||||
default:
|
||||
return setEntwineData(el, namespace, args[0], args[1]);
|
||||
}
|
||||
};
|
||||
|
||||
$.extend($.fn, {
|
||||
entwineData: function() {
|
||||
return entwineData(this, '__base', arguments);
|
||||
}
|
||||
});
|
||||
|
||||
$.entwine.Namespace.addHandler({
|
||||
order: 60,
|
||||
|
||||
bind: function(selector, k, v) {
|
||||
if (k.charAt(0) != k.charAt(0).toUpperCase()) $.entwine.warn('Entwine property '+k+' does not start with a capital letter', $.entwine.WARN_LEVEL_BESTPRACTISE);
|
||||
|
||||
// Create the getters and setters
|
||||
|
||||
var getterName = 'get'+k;
|
||||
var setterName = 'set'+k;
|
||||
|
||||
this.bind_proxy(selector, getterName, function() { var r = this.entwineData(k); return r === undefined ? v : r; });
|
||||
this.bind_proxy(selector, setterName, function(v){ return this.entwineData(k, v); });
|
||||
|
||||
// Get the get and set proxies we just created
|
||||
|
||||
var getter = this.injectee[getterName];
|
||||
var setter = this.injectee[setterName];
|
||||
|
||||
// And bind in the jQuery-style accessor
|
||||
|
||||
this.bind_proxy(selector, k, function(v){ return (arguments.length == 1 ? setter : getter).call(this, v) ; });
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
namespaceMethodOverrides: function(namespace){
|
||||
return {
|
||||
entwineData: function() {
|
||||
return entwineData(this, namespace.name, arguments);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
;
|
||||
|
||||
|
||||
/* src/jquery.entwine.legacy.js */
|
||||
|
||||
(function($) {
|
||||
|
||||
// Adds back concrete methods for backwards compatibility
|
||||
$.concrete = $.entwine;
|
||||
$.fn.concrete = $.fn.entwine;
|
||||
$.fn.concreteData = $.fn.entwineData;
|
||||
|
||||
// Use addHandler to hack in the namespace.$.concrete equivilent to the namespace.$.entwine namespace-injection
|
||||
$.entwine.Namespace.addHandler({
|
||||
order: 100,
|
||||
bind: function(selector, k, v) { return false; },
|
||||
|
||||
namespaceMethodOverrides: function(namespace){
|
||||
namespace.$.concrete = namespace.$.entwine;
|
||||
namespace.injectee.concrete = namespace.injectee.entwine;
|
||||
namespace.injectee.concreteData = namespace.injectee.entwineData;
|
||||
return {};
|
||||
}
|
||||
});
|
||||
|
||||
})(jQuery);
|
||||
;
|
||||
|
@ -694,6 +694,69 @@ Sizzle is good for finding elements for a selector, but not so good for telling
|
||||
;
|
||||
|
||||
|
||||
/* src/jquery.selector.affectedby.js */
|
||||
|
||||
(function($) {
|
||||
|
||||
// TODO:
|
||||
// Make attributes & IDs work
|
||||
|
||||
var DIRECT = /DIRECT/g;
|
||||
var CONTEXT = /CONTEXT/g;
|
||||
var EITHER = /DIRECT|CONTEXT/g;
|
||||
|
||||
$.selector.SelectorBase.addMethod('affectedBy', function(props) {
|
||||
this.affectedBy = new Function('props', ([
|
||||
'var direct_classes, context_classes, direct_attrs, context_attrs, t;',
|
||||
this.ABC_compile().replace(DIRECT, 'direct').replace(CONTEXT, 'context'),
|
||||
'return {classes: {context: context_classes, direct: direct_classes}, attrs: {context: context_attrs, direct: direct_attrs}};'
|
||||
]).join("\n"));
|
||||
|
||||
// DEBUG: Print out the compiled funciton
|
||||
// console.log(this.selector, ''+this.affectedBy);
|
||||
|
||||
return this.affectedBy(props);
|
||||
});
|
||||
|
||||
$.selector.SimpleSelector.addMethod('ABC_compile', function() {
|
||||
var parts = [];
|
||||
|
||||
$.each(this.classes, function(i, cls){
|
||||
parts[parts.length] = "if (t = props.classes['"+cls+"']) (DIRECT_classes || (DIRECT_classes = {}))['"+cls+"'] = t;";
|
||||
});
|
||||
|
||||
$.each(this.nots, function(i, not){
|
||||
parts[parts.length] = not.ABC_compile();
|
||||
});
|
||||
|
||||
return parts.join("\n");
|
||||
});
|
||||
|
||||
$.selector.Selector.addMethod('ABC_compile', function(arg){
|
||||
var parts = [];
|
||||
var i = this.parts.length-1;
|
||||
|
||||
parts[parts.length] = this.parts[i].ABC_compile();
|
||||
while ((i = i - 2) >= 0) parts[parts.length] = this.parts[i].ABC_compile().replace(EITHER, 'CONTEXT');
|
||||
|
||||
return parts.join("\n");
|
||||
});
|
||||
|
||||
$.selector.SelectorsGroup.addMethod('ABC_compile', function(){
|
||||
var parts = [];
|
||||
|
||||
$.each(this.parts, function(i,part){
|
||||
parts[parts.length] = part.ABC_compile();
|
||||
});
|
||||
|
||||
return parts.join("\n");
|
||||
});
|
||||
|
||||
|
||||
})(jQuery);
|
||||
;
|
||||
|
||||
|
||||
/* src/jquery.focusinout.js */
|
||||
|
||||
(function($){
|
||||
@ -1122,19 +1185,140 @@ catch (e) {
|
||||
/* src/jquery.entwine.dommaybechanged.js */
|
||||
|
||||
(function($){
|
||||
|
||||
|
||||
/** Utility function to monkey-patch a jQuery method */
|
||||
var monkey = function( /* method, method, ...., patch */){
|
||||
var methods = $.makeArray(arguments);
|
||||
var patch = methods.pop();
|
||||
|
||||
$.each(methods, function(i, method){
|
||||
var old = $.fn[method];
|
||||
|
||||
$.fn[method] = function() {
|
||||
var self = this, args = $.makeArray(arguments);
|
||||
|
||||
var rv = old.apply(self, args);
|
||||
patch.apply(self, args);
|
||||
return rv;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** What to call to run a function 'soon'. Normally setTimeout, but for syncronous mode we override so soon === now */
|
||||
var runSoon = window.setTimeout;
|
||||
|
||||
/** The timer handle for the asyncronous matching call */
|
||||
var check_id = null;
|
||||
|
||||
/** Fire the change event. Only fires on the document node, so bind to that */
|
||||
var triggerEvent = function() {
|
||||
$(document).triggerHandler('DOMMaybeChanged');
|
||||
check_id = null;
|
||||
};
|
||||
|
||||
var ChangeDetails = Base.extend({
|
||||
|
||||
init: function() {
|
||||
this.global = false;
|
||||
this.attrs = {};
|
||||
this.classes = {};
|
||||
},
|
||||
|
||||
/** Fire the change event. Only fires on the document node, so bind to that */
|
||||
triggerEvent: function() {
|
||||
// If we're not the active changes instance any more, don't trigger
|
||||
if (changes != this) return;
|
||||
|
||||
// Cancel any pending timeout (if we're directly called in the mean time)
|
||||
if (this.check_id) clearTimeout(this.check_id);
|
||||
|
||||
// Create a new event object
|
||||
var event = $.Event("DOMMaybeChanged");
|
||||
event.changes = this;
|
||||
|
||||
// Reset the global changes object to be a new instance (do before trigger, in case trigger fires changes itself)
|
||||
changes = new ChangeDetails();
|
||||
|
||||
// Fire event
|
||||
$(document).triggerHandler(event);
|
||||
},
|
||||
|
||||
changed: function() {
|
||||
if (!this.check_id) {
|
||||
var self = this;
|
||||
this.check_id = runSoon(function(){ self.check_id = null; self.triggerEvent(); }, 10);
|
||||
}
|
||||
},
|
||||
|
||||
addAll: function() {
|
||||
if (this.global) return this; // If we've already flagged as a global change, just skip
|
||||
|
||||
this.global = true;
|
||||
this.changed();
|
||||
return this;
|
||||
},
|
||||
|
||||
addSubtree: function(node) {
|
||||
return this.addAll();
|
||||
},
|
||||
|
||||
/* For now we don't do this. It's expensive, and jquery.entwine.ctors doesn't use this information anyway */
|
||||
addSubtreeFuture: function(node) {
|
||||
if (this.global) return this; // If we've already flagged as a global change, just skip
|
||||
|
||||
this.subtree = this.subtree ? this.subtree.add(node) : $(node);
|
||||
this.changed();
|
||||
return this;
|
||||
},
|
||||
|
||||
addAttr: function(attr, node) {
|
||||
if (this.global) return this;
|
||||
|
||||
this.attrs[attr] = (attr in this.attrs) ? this.attrs[attr].add(node) : $(node);
|
||||
this.changed();
|
||||
return this;
|
||||
},
|
||||
|
||||
addClass: function(klass, node) {
|
||||
if (this.global) return this;
|
||||
|
||||
this.classes[klass] = (klass in this.classes) ? this.classes[klass].add(node) : $(node);
|
||||
this.changed();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
var changes = new ChangeDetails();
|
||||
|
||||
|
||||
monkey('append', 'prepend', 'empty', 'html', function(){
|
||||
changes.addSubtree(this);
|
||||
});
|
||||
|
||||
monkey('after', 'before', 'remove', 'detach', function(){
|
||||
changes.addSubtree(this.parent());
|
||||
})
|
||||
|
||||
monkey('removeAttr', function(attr){
|
||||
changes.addAttr(attr, this);
|
||||
});
|
||||
|
||||
monkey('addClass', 'removeClass', 'toggleClass', function(klass){
|
||||
if (typeof klass == 'string') changes.addClass(klass, this);
|
||||
});
|
||||
|
||||
monkey('attr', function(a, b){
|
||||
if (b !== undefined && typeof a == 'string') changes.addAttr(a, this);
|
||||
else if (typeof a != 'string') { for (var k in a) changes.addAttr(k, this); }
|
||||
});
|
||||
|
||||
/*
|
||||
These manipulation functions call one or more of the above to do the actual manipulation:
|
||||
appendTo -> append
|
||||
prependTo -> prepend
|
||||
insertBefore -> before
|
||||
insertAfter -> after
|
||||
replaceWith -> before || append
|
||||
replaceAll -> replaceWith
|
||||
text -> empty, appendWith
|
||||
wrapAll -> insertBefore, append
|
||||
wrapInner -> wrapAll || append
|
||||
wrap -> wrapAll
|
||||
unwrap -> replaceWith
|
||||
*/
|
||||
|
||||
$.extend($.entwine, {
|
||||
/**
|
||||
* Make onmatch and onunmatch work in synchronous mode - that is, new elements will be detected immediately after
|
||||
@ -1142,48 +1326,26 @@ catch (e) {
|
||||
* (otherwise we'd make it the default).
|
||||
*/
|
||||
synchronous_mode: function() {
|
||||
if (check_id) clearTimeout(check_id); check_id = null;
|
||||
if (changes && changes.check_id) clearTimeout(changes.check_id);
|
||||
changes = new ChangeDetails();
|
||||
|
||||
runSoon = function(func, delay){ func.call(this); return null; };
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Trigger onmatch and onunmatch now - usefull for after DOM manipulation by methods other than through jQuery.
|
||||
* Called automatically on document.ready
|
||||
*/
|
||||
triggerMatching: function() {
|
||||
matching();
|
||||
changes.addAll(); //.triggerEvent();
|
||||
}
|
||||
});
|
||||
|
||||
function registerMutateFunction() {
|
||||
$.each(arguments, function(i,func){
|
||||
var old = $.fn[func];
|
||||
$.fn[func] = function() {
|
||||
var rv = old.apply(this, arguments);
|
||||
if (!check_id) check_id = runSoon(triggerEvent, 100);
|
||||
return rv;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function registerSetterGetterFunction() {
|
||||
$.each(arguments, function(i,func){
|
||||
var old = $.fn[func];
|
||||
$.fn[func] = function(a, b) {
|
||||
var rv = old.apply(this, arguments);
|
||||
if (!check_id && (b !== undefined || typeof a != 'string')) check_id = runSoon(triggerEvent, 100);
|
||||
return rv;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Register core DOM manipulation methods
|
||||
registerMutateFunction('append', 'prepend', 'after', 'before', 'wrap', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove');
|
||||
registerSetterGetterFunction('attr');
|
||||
|
||||
// And on DOM ready, trigger matching once
|
||||
$(function(){ triggerEvent(); });
|
||||
|
||||
$(function(){
|
||||
$.entwine.triggerMatching();
|
||||
});
|
||||
|
||||
})(jQuery);;
|
||||
|
||||
|
||||
@ -1491,6 +1653,9 @@ catch (e) {
|
||||
|
||||
bind: function(selector, k, v) {
|
||||
if ($.isFunction(v) && (k == 'onmatch' || k == 'onunmatch')) {
|
||||
// When we add new matchers we need to trigger a full global recalc once, regardless of the DOM changes that triggered the event
|
||||
this.matchersDirty = true;
|
||||
|
||||
this.bind_condesc(selector, k, v);
|
||||
return true;
|
||||
}
|
||||
@ -1507,15 +1672,39 @@ catch (e) {
|
||||
* $('#foo').addClass('tabs'); $('#foo').tabFunctionBar();
|
||||
* won't work.
|
||||
*/
|
||||
$(document).bind('DOMMaybeChanged', function(){
|
||||
$(document).bind('DOMMaybeChanged', function(e){
|
||||
// Get the change delta. Can help stop us from doing heavy lifting if none of the changes could actually trigger an onmatch or onunmatch function
|
||||
var changes = e.changes;
|
||||
|
||||
// var start = (new Date).getTime();
|
||||
|
||||
// For every namespace
|
||||
for (var k in $.entwine.namespaces) {
|
||||
var namespace = $.entwine.namespaces[k];
|
||||
|
||||
// That has constructors or destructors
|
||||
var ctors = $.entwine.namespaces[k].store.ctors;
|
||||
var ctors = namespace.store.ctors;
|
||||
if (ctors) {
|
||||
|
||||
// Keep a record of elements that have matched already
|
||||
var matched = $([]), add, rem, res, rule, sel, ctor, dtor;
|
||||
// Keep a record of elements that have matched some previous more specific rule.
|
||||
// Not that we _don't_ actually do that until this is needed. If matched is null, it's not been calculated yet.
|
||||
// We also keep track of any elements that have newly been taken or released by a specific rule
|
||||
var matched = null, taken = $([]), released = $([]);
|
||||
|
||||
// Updates matched to contain all the previously matched elements as if we'd been keeping track all along
|
||||
var calcmatched = function(j){
|
||||
if (matched !== null) return;
|
||||
matched = $([]);
|
||||
|
||||
var cache, k = ctors.length;
|
||||
while ((--k) > j) {
|
||||
if (cache = ctors[k].cache) matched = matched.add(cache);
|
||||
}
|
||||
}
|
||||
|
||||
// Some declared variables used in the loop
|
||||
var add, rem, res, rule, sel, ctor, dtor, full;
|
||||
|
||||
// Stepping through each selector from most to least specific
|
||||
var j = ctors.length;
|
||||
while (j--) {
|
||||
@ -1524,40 +1713,140 @@ catch (e) {
|
||||
sel = rule.selector.selector;
|
||||
ctor = rule.onmatch;
|
||||
dtor = rule.onunmatch;
|
||||
|
||||
// Get the list of elements that match this selector, that haven't yet matched a more specific selector
|
||||
res = add = $(sel).not(matched);
|
||||
|
||||
// If this selector has a list of elements it matched against last time
|
||||
if (rule.cache) {
|
||||
// Find the ones that are extra this time
|
||||
add = res.not(rule.cache);
|
||||
if (dtor) {
|
||||
// Find the ones that are gone this time
|
||||
rem = rule.cache.not(res);
|
||||
// And call the destructor on them
|
||||
if (rem.length && !rule.onunmatchRunning) {
|
||||
rule.onunmatchRunning = true;
|
||||
ctors.onunmatchproxy(rem, j, dtor);
|
||||
rule.onunmatchRunning = false;
|
||||
|
||||
/*
|
||||
Rule.cache might be stale or fresh. It'll be stale if
|
||||
- some more specific selector now has some of rule.cache in it
|
||||
- some change has happened that means new elements match this selector now
|
||||
- some change has happened that means elements no longer match this selector
|
||||
|
||||
The first we can just compare rules.cache with matched, removing anything that's there already.
|
||||
*/
|
||||
|
||||
// Reset the "elements that match this selector and no more specific selector with an onmatch rule" to null.
|
||||
// Staying null means this selector is fresh.
|
||||
res = null;
|
||||
|
||||
// If this gets changed to true, it's too hard to do a delta update, so do a full update
|
||||
full = false;
|
||||
|
||||
if (namespace.matchersDirty || changes.global) {
|
||||
// For now, just fall back to old version. We need to do something like changed.Subtree.find('*').andSelf().filter(sel), but that's _way_ slower on modern browsers than the below
|
||||
full = true;
|
||||
}
|
||||
else {
|
||||
// We don't deal with attributes yet, so any attribute change means we need to do a full recalc
|
||||
for (var k in e.changes.attrs) { full = true; break; }
|
||||
|
||||
/*
|
||||
If a class changes, but it isn't listed in our selector, we don't care - the change couldn't affect whether or not any element matches
|
||||
|
||||
If it is listed on our selector
|
||||
- If it is on the direct match part, it could have added or removed the node it changed on
|
||||
- If it is on the context part, it could have added or removed any node that were previously included or excluded because of a match or failure to match with the context required on that node
|
||||
- NOTE: It might be on _both_
|
||||
*/
|
||||
|
||||
var method = rule.selector.affectedBy(e.changes);
|
||||
|
||||
if (method.classes.context) {
|
||||
full = true;
|
||||
}
|
||||
else {
|
||||
for (var k in method.classes.direct) {
|
||||
calcmatched(j);
|
||||
var recheck = e.changes.classes[k].not(matched);
|
||||
|
||||
if (res === null) {
|
||||
res = rule.cache ? rule.cache.not(taken).add(released.filter(sel)) : $([]);
|
||||
}
|
||||
|
||||
res = res.not(recheck).add(recheck.filter(sel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call the constructor on the newly matched ones
|
||||
if (add.length && ctor && !rule.onmatchRunning) {
|
||||
rule.onmatchRunning = true;
|
||||
ctors.onmatchproxy(add, j, ctor);
|
||||
rule.onmatchRunning = false;
|
||||
|
||||
if (full) {
|
||||
calcmatched(j);
|
||||
res = $(sel).not(matched);
|
||||
}
|
||||
else {
|
||||
if (!res) {
|
||||
// We weren't stale because of any changes to the DOM that affected this selector, but more specific
|
||||
// onmatches might have caused stale-ness
|
||||
|
||||
// Do any of the previous released elements match this selector?
|
||||
add = released.length && released.filter(sel);
|
||||
|
||||
if (add && add.length) {
|
||||
// Yes, so we're stale as we need to include them. Filter for any possible taken value at the same time
|
||||
res = rule.cache ? rule.cache.not(taken).add(add) : add;
|
||||
}
|
||||
else {
|
||||
// Do we think we own any of the elements now taken by more specific rules?
|
||||
rem = taken.length && rule.cache && rule.cache.filter(taken);
|
||||
|
||||
if (rem && rem.length) {
|
||||
// Yes, so we're stale as we need to exclude them.
|
||||
res = rule.cache.not(rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Res will be null if we know we are fresh (no full needed, selector not affectedBy changes)
|
||||
if (res === null) {
|
||||
// If we are tracking matched, add ourselves
|
||||
if (matched && rule.cache) matched = matched.add(rule.cache);
|
||||
}
|
||||
else {
|
||||
// If this selector has a list of elements it matched against last time
|
||||
if (rule.cache) {
|
||||
// Find the ones that are extra this time
|
||||
add = res.not(rule.cache);
|
||||
rem = rule.cache.not(res);
|
||||
}
|
||||
else {
|
||||
add = res; rem = null;
|
||||
}
|
||||
|
||||
if ((add && add.length) || (rem && rem.length)) {
|
||||
if (rem && rem.length) {
|
||||
released = released.add(rem);
|
||||
|
||||
if (dtor && !rule.onunmatchRunning) {
|
||||
rule.onunmatchRunning = true;
|
||||
ctors.onunmatchproxy(rem, j, dtor);
|
||||
rule.onunmatchRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Call the constructor on the newly matched ones
|
||||
if (add && add.length) {
|
||||
taken = taken.add(add);
|
||||
released = released.not(add);
|
||||
|
||||
if (ctor && !rule.onmatchRunning) {
|
||||
rule.onmatchRunning = true;
|
||||
ctors.onmatchproxy(add, j, ctor);
|
||||
rule.onmatchRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we are tracking matched, add ourselves
|
||||
if (matched) matched = matched.add(res);
|
||||
|
||||
// And remember this list of matching elements again this selector, so next matching we can find the unmatched ones
|
||||
rule.cache = res;
|
||||
}
|
||||
|
||||
// Add these matched ones to the list tracking all elements matched so far
|
||||
matched = matched.add(res);
|
||||
// And remember this list of matching elements again this selector, so next matching we can find the unmatched ones
|
||||
ctors[j].cache = res;
|
||||
}
|
||||
|
||||
namespace.matchersDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
// console.log((new Date).getTime() - start);
|
||||
});
|
||||
|
||||
|
||||
|
10
thirdparty/jquery-entwine/spec/SpecRunner.html
vendored
10
thirdparty/jquery-entwine/spec/SpecRunner.html
vendored
@ -1,5 +1,5 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>Jasmine Test Runner</title>
|
||||
@ -10,14 +10,13 @@
|
||||
<script type="text/javascript" src="../vendor/jasmine-dom/jasmine-dom-matchers.js"></script>
|
||||
<script type="text/javascript" src="../vendor/jasmine-dom/jasmine-dom-fixtures.js"></script>
|
||||
|
||||
<!-- include source files here... -->
|
||||
<script type="text/javascript">
|
||||
versionarg = window.location.search.match(/version=([^&]+)/);
|
||||
jQueryVersion = versionarg ? versionarg[1] : '1.6';
|
||||
jQuerySource = 'local';
|
||||
|
||||
var jQuery;
|
||||
document.write('<'+'script src="../vendor/jquery-'+jQueryVersion+'.js"'+'><'+'/script'+'>');
|
||||
document.write('<'+'script src="../vendor/jquery-'+jQueryVersion+'.js"'+'><'+'/script'+'>');
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
@ -31,7 +30,9 @@
|
||||
<script src="../vendor/jquery.selector/jquery.selector.js"></script>
|
||||
<script src="../vendor/jquery.selector/jquery.selector.specifity.js"></script>
|
||||
<script src="../vendor/jquery.selector/jquery.selector.matches.js"></script>
|
||||
|
||||
|
||||
<script src="../src/jquery.selector.affectedby.js"></script>
|
||||
|
||||
<script src="../src/jquery.entwine.js"></script>
|
||||
<script src="../src/jquery.entwine.dommaybechanged.js"></script>
|
||||
<script src="../src/jquery.entwine.events.js"></script>
|
||||
@ -52,6 +53,7 @@
|
||||
margin: 0;
|
||||
padding: 4px 15px;
|
||||
}
|
||||
|
||||
#jqver.error {
|
||||
padding: 4px 13px;
|
||||
border: 2px solid red;
|
||||
|
@ -1,86 +1,95 @@
|
||||
describe( 'Entwine', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$.entwine.warningLevel = $.entwine.WARN_LEVEL_BESTPRACTISE;
|
||||
$('body').append('<div id="dom_test"></div>');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
$('#dom_test').remove();
|
||||
});
|
||||
describe('Entwine', function(){
|
||||
|
||||
describe( 'Basics', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$.entwine.clear_all_rules();
|
||||
$('#dom_test').html('<div id="a" class="a b c" data-fieldtype="foo"></div><div id="b" class="c d e"></div>');
|
||||
});
|
||||
beforeEach(function(){
|
||||
$.entwine.warningLevel = $.entwine.WARN_LEVEL_BESTPRACTISE;
|
||||
$('body').append('<div id="dom_test"></div>');
|
||||
});
|
||||
|
||||
it( 'can attach and call a base function', function() {
|
||||
$('#a').entwine({
|
||||
foo: function(){return this.attr('id');}
|
||||
});
|
||||
expect($('.a').foo()).toEqual('a');
|
||||
});
|
||||
|
||||
it( 'can attach and call a base function on a selector using a data attribute selection', function() {
|
||||
$('[data-fieldtype=foo]').entwine({
|
||||
foo: function(){return this.attr('id');}
|
||||
});
|
||||
expect($('.a').foo()).toEqual('a') ;
|
||||
});
|
||||
|
||||
it( 'can attach and call several base functions', function() {
|
||||
$('#a').entwine({
|
||||
foo: function(){return 'foo_' + this.attr('id');},
|
||||
bar: function(){return 'bar_' + this.attr('id');}
|
||||
});
|
||||
expect($('.a').foo()).toEqual( 'foo_a');
|
||||
expect($('.a').bar()).toEqual( 'bar_a');
|
||||
});
|
||||
|
||||
it( 'can attach and call a namespaced function', function() {
|
||||
$.entwine('bar', function($){
|
||||
$('#a').entwine({
|
||||
foo: function(){return this.attr('id');}
|
||||
});
|
||||
});
|
||||
expect($('.a').entwine('bar').foo()).toEqual( 'a');
|
||||
});
|
||||
|
||||
it( 'can attach and call a nested namespaced function', function() {
|
||||
$.entwine('qux.baz.bar', function($){
|
||||
$('#a').entwine({
|
||||
foo: function(){return this.attr('id');}
|
||||
});
|
||||
});
|
||||
expect($('.a').entwine('qux.baz.bar').foo()).toEqual( 'a');
|
||||
});
|
||||
|
||||
it( 'can call two functions on two elements', function() {
|
||||
var res = []
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(this.attr('id'));}
|
||||
});
|
||||
$('#b.c').entwine({
|
||||
foo: function(){res.push(this.attr('id'));}
|
||||
});
|
||||
$('#dom_test div').foo();
|
||||
expect(res).toEqual( ['b', 'a']);
|
||||
});
|
||||
|
||||
it( 'can call two namespaced functions on two elements', function() {
|
||||
var res = []
|
||||
$.entwine('bar', function($){
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(this.attr('id'));}
|
||||
});
|
||||
$('#b.c').entwine({
|
||||
foo: function(){res.push(this.attr('id'));}
|
||||
});
|
||||
});
|
||||
$('#dom_test div').entwine('bar').foo();
|
||||
expect(res).toEqual( ['b', 'a']);
|
||||
});
|
||||
});
|
||||
afterEach(function(){
|
||||
$('#dom_test').remove();
|
||||
});
|
||||
|
||||
describe('Basics', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
$.entwine.clear_all_rules();
|
||||
$('#dom_test').html('<div id="a" class="a b c" data-fieldtype="foo"></div><div id="b" class="c d e"></div>');
|
||||
});
|
||||
|
||||
it('can attach and call a base function', function(){
|
||||
$('#a').entwine({
|
||||
foo: function(){return this.attr('id');}
|
||||
});
|
||||
|
||||
expect($('.a').foo()).toEqual('a');
|
||||
});
|
||||
|
||||
it('can attach and call a base function on a selector using a data attribute selection', function(){
|
||||
$('[data-fieldtype=foo]').entwine({
|
||||
foo: function(){return this.attr('id');}
|
||||
});
|
||||
|
||||
expect($('.a').foo()).toEqual('a');
|
||||
});
|
||||
|
||||
it('can attach and call several base functions', function(){
|
||||
$('#a').entwine({
|
||||
foo: function(){return 'foo_' + this.attr('id');},
|
||||
bar: function(){return 'bar_' + this.attr('id');}
|
||||
});
|
||||
|
||||
expect($('.a').foo()).toEqual('foo_a');
|
||||
expect($('.a').bar()).toEqual('bar_a');
|
||||
});
|
||||
|
||||
it('can attach and call a namespaced function', function(){
|
||||
$.entwine('bar', function($){
|
||||
$('#a').entwine({
|
||||
foo: function(){return this.attr('id');}
|
||||
});
|
||||
});
|
||||
|
||||
expect($('.a').entwine('bar').foo()).toEqual('a');
|
||||
});
|
||||
|
||||
it('can attach and call a nested namespaced function', function(){
|
||||
$.entwine('qux.baz.bar', function($){
|
||||
$('#a').entwine({
|
||||
foo: function(){return this.attr('id');}
|
||||
});
|
||||
});
|
||||
|
||||
expect($('.a').entwine('qux.baz.bar').foo()).toEqual('a');
|
||||
});
|
||||
|
||||
it('can call two functions on two elements', function(){
|
||||
var res = [];
|
||||
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(this.attr('id'));}
|
||||
});
|
||||
$('#b.c').entwine({
|
||||
foo: function(){res.push(this.attr('id'));}
|
||||
});
|
||||
|
||||
$('#dom_test div').foo();
|
||||
expect(res).toEqual(['b', 'a']);
|
||||
});
|
||||
|
||||
it('can call two namespaced functions on two elements', function(){
|
||||
var res = [];
|
||||
|
||||
$.entwine('bar', function($){
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(this.attr('id'));}
|
||||
});
|
||||
$('#b.c').entwine({
|
||||
foo: function(){res.push(this.attr('id'));}
|
||||
});
|
||||
});
|
||||
|
||||
$('#dom_test div').entwine('bar').foo();
|
||||
expect(res).toEqual(['b', 'a']);
|
||||
});
|
||||
});
|
||||
});
|
207
thirdparty/jquery-entwine/spec/spec.entwine.ctors.js
vendored
207
thirdparty/jquery-entwine/spec/spec.entwine.ctors.js
vendored
@ -1,68 +1,147 @@
|
||||
describe( 'Entwine', function() {
|
||||
describe('Entwine', function(){
|
||||
|
||||
beforeEach(function() {
|
||||
$('body').append('<div id="dom_test"></div>')
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
$('#dom_test').remove()
|
||||
});
|
||||
beforeEach(function(){
|
||||
$('body').append('<div id="dom_test"></div>');
|
||||
});
|
||||
|
||||
describe( 'Ctors', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$.entwine.synchronous_mode();
|
||||
$.entwine.clear_all_rules()
|
||||
$('#dom_test').html('<div id="a" class="a b c"></div>')
|
||||
});
|
||||
|
||||
it( 'calls onmatch when new element created', function() {
|
||||
var a = false;
|
||||
$('#b').entwine({onmatch: function(){a = true;} });
|
||||
expect(a).toBeFalsy();
|
||||
$('#a').after('<div id="b"></div>');
|
||||
expect(a).toBeTruthy();
|
||||
});
|
||||
|
||||
it( 'calls onunmatch when new element deleted', function() {
|
||||
var a = 0;
|
||||
$('#b').entwine({onmatch: function(){a = 1;}, onunmatch: function(){a = 2;} });
|
||||
expect(a).toEqual( 0);
|
||||
$('#a').after('<div id="b"></div>');
|
||||
expect(a).toEqual( 1);
|
||||
$('#b').remove();
|
||||
expect(a).toEqual( 2);
|
||||
});
|
||||
|
||||
it( 'calls onmatch when ruleset matches after class added', function() {
|
||||
var a = 0;
|
||||
$('#a.foo').entwine({onmatch: function(){a = 1;} });
|
||||
expect(a).toEqual(0);
|
||||
$('#a').addClass('foo');
|
||||
expect(a).toEqual( 1);
|
||||
});
|
||||
afterEach(function(){
|
||||
$('#dom_test').remove();
|
||||
});
|
||||
|
||||
it( 'calls onmatch in both direct and namespaced onmatch, does not call less specific onmatch', function() {
|
||||
var a = 0, b=0, c=0, d=0;
|
||||
$('.foo').entwine({onmatch: function(){a = 1;}})
|
||||
$('.foo').entwine('bar', function($){return{onmatch: function(){b = 1;}}})
|
||||
$('#a.foo').entwine({onmatch: function(){c = 1;}})
|
||||
$('#a.foo').entwine('bar', function($){return{onmatch: function(){d = 1}}})
|
||||
expect([a, b, c, d]).toEqual( [0, 0, 0, 0]);
|
||||
$('#a').addClass('foo');
|
||||
expect([a, b, c, d]).toEqual( [0, 0, 1, 1]);
|
||||
});
|
||||
|
||||
it( 'calls onmatch in both direct and namespaced onmatch, super works as expected', function() {
|
||||
var a = 0, b=0, c=0, d=0;
|
||||
$('.foo').entwine({onmatch: function(){a += 1;}})
|
||||
$('.foo').entwine('bar', function($){return{onmatch: function(){b += 1;}}})
|
||||
$('#a.foo').entwine({onmatch: function(){this._super(); c = 1; this._super();}})
|
||||
$('#a.foo').entwine('bar', function($){return{onmatch: function(){this._super(); d = 1; this._super();}}})
|
||||
expect([a, b, c, d]).toEqual( [0, 0, 0, 0]);
|
||||
$('#a').addClass('foo');
|
||||
expect([a, b, c, d]).toEqual( [2, 2, 1, 1]);
|
||||
});
|
||||
|
||||
});
|
||||
describe('Ctors', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
$.entwine.synchronous_mode();
|
||||
$.entwine.clear_all_rules();
|
||||
$('#dom_test').html('<div id="a" class="a b c"></div>');
|
||||
});
|
||||
|
||||
it('calls onmatch when new element created', function(){
|
||||
var a = false;
|
||||
|
||||
$('#b').entwine({
|
||||
onmatch: function(){a = true;}
|
||||
});
|
||||
|
||||
expect(a).toBeFalsy();
|
||||
|
||||
$('#a').after('<div id="b"></div>');
|
||||
expect(a).toBeTruthy();
|
||||
});
|
||||
|
||||
it('calls onunmatch when new element deleted', function(){
|
||||
var a = 0;
|
||||
|
||||
$('#b').entwine({
|
||||
onmatch: function(){a = 1;},
|
||||
onunmatch: function(){a = 2;}
|
||||
});
|
||||
|
||||
expect(a).toEqual(0);
|
||||
|
||||
$('#a').after('<div id="b"></div>');
|
||||
expect(a).toEqual(1);
|
||||
|
||||
$('#b').remove();
|
||||
expect(a).toEqual(2);
|
||||
});
|
||||
|
||||
it('calls onmatch when ruleset matches after class added', function(){
|
||||
var a = 0;
|
||||
|
||||
$('#a.foo').entwine({
|
||||
onmatch: function(){a = 1;}
|
||||
});
|
||||
|
||||
expect(a).toEqual(0);
|
||||
|
||||
$('#a').addClass('foo');
|
||||
expect(a).toEqual(1);
|
||||
});
|
||||
|
||||
it('calls onmatch in both direct and namespaced onmatch, does not call less specific onmatch', function(){
|
||||
var a = 0, b = 0, c = 0, d = 0;
|
||||
|
||||
$('.foo').entwine({
|
||||
onmatch: function(){a = 1;}
|
||||
});
|
||||
$('.foo').entwine('bar', function($){return{
|
||||
onmatch: function(){b = 1;}
|
||||
};});
|
||||
$('#a.foo').entwine({
|
||||
onmatch: function(){c = 1;}
|
||||
});
|
||||
$('#a.foo').entwine('bar', function($){return{
|
||||
onmatch: function(){d = 1;}
|
||||
};});
|
||||
|
||||
expect([a, b, c, d]).toEqual([0, 0, 0, 0]);
|
||||
|
||||
$('#a').addClass('foo');
|
||||
expect([a, b, c, d]).toEqual([0, 0, 1, 1]);
|
||||
});
|
||||
|
||||
it('calls onmatch in both direct and namespaced onmatch, super works as expected', function(){
|
||||
var a = 0, b = 0, c = 0, d = 0;
|
||||
|
||||
$('.foo').entwine({
|
||||
onmatch: function(){a += 1;}
|
||||
});
|
||||
$('.foo').entwine('bar', function($){return{
|
||||
onmatch: function(){b += 1;}
|
||||
};});
|
||||
$('#a.foo').entwine({
|
||||
onmatch: function(){this._super(); c = 1; this._super();}
|
||||
});
|
||||
$('#a.foo').entwine('bar', function($){return{
|
||||
onmatch: function(){this._super(); d = 1; this._super();}
|
||||
};});
|
||||
|
||||
expect([a, b, c, d]).toEqual([0, 0, 0, 0]);
|
||||
|
||||
$('#a').addClass('foo');
|
||||
expect([a, b, c, d]).toEqual([2, 2, 1, 1]);
|
||||
});
|
||||
|
||||
it('handles onmatch rules being added post document.onready', function(){
|
||||
var a = 0, b = 0;
|
||||
|
||||
$('#a').entwine({
|
||||
onmatch: function(){a += 1;}
|
||||
});
|
||||
$('#a.a').entwine({
|
||||
onmatch: function(){b += 1;}
|
||||
});
|
||||
|
||||
// Rules are new, and no DOM change, so no triggers yet
|
||||
expect([a, b]).toEqual([0, 0]);
|
||||
|
||||
// New #a.a rule thinks it matches no nodes, and so removing .a would normally not release it. Check we handle
|
||||
$('#a').removeClass('a');
|
||||
expect([a, b]).toEqual([1, 0]);
|
||||
});
|
||||
|
||||
it('calls onmatch in less specific rule when more specific rule no longer matches', function(){
|
||||
var a = 0, b = 0, c = 0;
|
||||
|
||||
$('#a').entwine({
|
||||
onmatch: function(){a += 1;}
|
||||
});
|
||||
$('#a.a').entwine({
|
||||
onmatch: function(){b += 1;}
|
||||
});
|
||||
$('#a.a.b').entwine({
|
||||
onmatch: function(){c += 1;}
|
||||
});
|
||||
|
||||
$.entwine.triggerMatching();
|
||||
expect([a, b, c]).toEqual([0, 0, 1]);
|
||||
|
||||
$('#a').removeClass('b');
|
||||
expect([a, b, c]).toEqual([0, 1, 1]);
|
||||
|
||||
$('#a').removeClass('a');
|
||||
expect([a, b, c]).toEqual([1, 1, 1]);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,162 +1,223 @@
|
||||
describe( 'Entwine', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$('body').append('<div id="dom_test"></div>')
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
$('#dom_test').remove()
|
||||
});
|
||||
|
||||
describe( 'Events', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$.entwine.synchronous_mode();
|
||||
$.entwine.clear_all_rules()
|
||||
$('#dom_test').html('<div id="a" class="a b c"></div>')
|
||||
});
|
||||
|
||||
it( 'calls onfoo when foo triggered', function() {
|
||||
var a = 0;
|
||||
$('#a').entwine({onfoo: function(){a = 1;} });
|
||||
expect(a).toEqual(0);
|
||||
$('#a').trigger('foo');
|
||||
expect(a).toEqual( 1);
|
||||
});
|
||||
|
||||
it( 'only calls most specific onfoo when foo triggered', function() {
|
||||
var a = 0, b = 0;
|
||||
$('#a.a').entwine({onfoo: function(){a = 1;} });
|
||||
$('#a').entwine({onfoo: function(){b = 1;} });
|
||||
expect(a).toEqual( 0);
|
||||
expect(b).toEqual( 0);
|
||||
$('#a').trigger('foo');
|
||||
expect(a).toEqual( 1);
|
||||
expect(b).toEqual( 0);
|
||||
});
|
||||
|
||||
it( 'calls namespaced onfoo when foo triggered', function() {
|
||||
var a = 0;
|
||||
$('#a').entwine('bar', function($){return{onfoo: function(){a = 1;} }});
|
||||
expect(a).toEqual( 0);
|
||||
$('#a').trigger('foo');
|
||||
expect(a).toEqual( 1);
|
||||
});
|
||||
|
||||
it( 'calls most specific namespaced onfoo and most specific non-namespaced onfoo when foo triggered', function() {
|
||||
var a = 0, b = 0, c = 0, d = 0;
|
||||
$('#a.a').entwine({onfoo: function(){a = 1;} });
|
||||
$('#a').entwine({onfoo: function(){b = 1;} });
|
||||
$('#a.a').entwine('bar', function($){return{onfoo: function(){c = 1;} }});
|
||||
$('#a').entwine('bar', function($){return{onfoo: function(){d = 1;} }});
|
||||
expect([a, b, c, d]).toEqual( [0, 0, 0, 0] );
|
||||
describe('Entwine', function(){
|
||||
|
||||
$('#a').trigger('foo');
|
||||
expect([a, b, c, d]).toEqual( [1, 0, 1, 0]);
|
||||
});
|
||||
|
||||
it( 'calls up correctly on _super', function() {
|
||||
var a = 0, b = 0;
|
||||
$('#a').entwine({onfoo: function(){a += 1;} });
|
||||
$('#a.a').entwine({onfoo: function(){this._super(); b += 1; this._super();} });
|
||||
|
||||
expect([a, b]).toEqual( [0, 0]);
|
||||
$('#a').trigger('foo')
|
||||
expect([a, b]).toEqual( [2, 1]);
|
||||
});
|
||||
|
||||
it( 'passes event object', function() {
|
||||
var event;
|
||||
$('#a').entwine({onfoo: function(e){event = e;} });
|
||||
$('#a').trigger('foo');
|
||||
expect(event.type).toBeDefined();
|
||||
expect(event.type).toEqual('foo');
|
||||
expect(event.target).toHaveAttr( 'id', 'a');
|
||||
});
|
||||
|
||||
it( 'delegates submit events to forms', function() {
|
||||
var a = 0;
|
||||
$('<form class="foo" action="javascript:undefined">').appendTo('#dom_test');
|
||||
|
||||
$('.foo').entwine({onsubmit: function(e, d){a = 1;} });
|
||||
beforeEach(function(){
|
||||
$('body').append('<div id="dom_test"></div>');
|
||||
});
|
||||
|
||||
expect(a).toEqual( 0);
|
||||
$('.foo').trigger('submit');
|
||||
expect(a).toEqual( 1);
|
||||
});
|
||||
|
||||
describe( 'can pass event data', function() {
|
||||
it( 'on custom events', function() {
|
||||
var data;
|
||||
$('#a').entwine({onfoo: function(e, d){data = d;} });
|
||||
$('#a').trigger('foo', {cheese: 'burger'});
|
||||
expect(data.cheese).toEqual( 'burger');
|
||||
});
|
||||
|
||||
it( 'on normal events', function() {
|
||||
var data;
|
||||
$('#a').entwine({onclick: function(e, d){data = d;} });
|
||||
$('#a').trigger('click', {finger: 'left'});
|
||||
expect(data.finger).toEqual( 'left');
|
||||
});
|
||||
|
||||
it( 'on submit', function() {
|
||||
var data;
|
||||
afterEach(function(){
|
||||
$('#dom_test').remove();
|
||||
});
|
||||
|
||||
$('<form class="foo" action="javascript:undefined">').appendTo('#dom_test');
|
||||
$('.foo').entwine({onsubmit: function(e, d){data = d; return false;} })
|
||||
describe('Events', function(){
|
||||
|
||||
$('.foo').trigger('submit', {cheese: 'burger'});
|
||||
expect(data.cheese).toEqual( 'burger');
|
||||
});
|
||||
});
|
||||
beforeEach(function(){
|
||||
$.entwine.synchronous_mode();
|
||||
$.entwine.clear_all_rules();
|
||||
$('#dom_test').html('<div id="a" class="a b c"></div>');
|
||||
});
|
||||
|
||||
describe( 'calls onchange on checkboxes properly', function() {
|
||||
beforeEach(function() {
|
||||
$('#dom_test').html('<input id="i" type="checkbox" name="test_input_i" value="i" />');
|
||||
});
|
||||
it('calls onfoo when foo triggered', function(){
|
||||
var a = 0;
|
||||
|
||||
it( 'calls onchange', function() {
|
||||
var a = 0;
|
||||
$('#a').entwine({
|
||||
onfoo: function(){a = 1;}
|
||||
});
|
||||
|
||||
$('#i').entwine({onchange: function(){ a += 1; }});
|
||||
expect(a).toEqual(0);
|
||||
|
||||
// Can't just "click()" - it's not the same as an actual click event
|
||||
$('#i').trigger('focusin'); $('#i')[0].click();
|
||||
expect(a).toEqual(1);
|
||||
});
|
||||
$('#a').trigger('foo');
|
||||
expect(a).toEqual(1);
|
||||
});
|
||||
|
||||
it( 'calls onchange only once per change', function() {
|
||||
var a = 0;
|
||||
it('only calls most specific onfoo when foo triggered', function(){
|
||||
var a = 0, b = 0;
|
||||
|
||||
$('#i').entwine({onchange: function(){ a += 1; }});
|
||||
$('#a.a').entwine({
|
||||
onfoo: function(){a = 1;}
|
||||
});
|
||||
$('#a').entwine({
|
||||
onfoo: function(){b = 1;}
|
||||
});
|
||||
|
||||
$('#i').trigger('focusin'); $('#i')[0].click();
|
||||
expect(a).toEqual(1);
|
||||
expect(a).toEqual(0);
|
||||
expect(b).toEqual(0);
|
||||
|
||||
$('#i').trigger('focusout'); $('#i').trigger('focusin'); $('#i').trigger('focusout');
|
||||
expect(a).toEqual(1);
|
||||
$('#a').trigger('foo');
|
||||
expect(a).toEqual(1);
|
||||
expect(b).toEqual(0);
|
||||
});
|
||||
|
||||
$('#i')[0].click();
|
||||
expect(a).toEqual(2);
|
||||
it('calls namespaced onfoo when foo triggered', function(){
|
||||
var a = 0;
|
||||
|
||||
});
|
||||
$('#a').entwine('bar', function($){return{
|
||||
onfoo: function(){a = 1;}
|
||||
};});
|
||||
|
||||
it( 'calls onchange even if checked attribute altered in mean time', function() {
|
||||
var a = 0;
|
||||
expect(a).toEqual(0);
|
||||
|
||||
$('#i').entwine({onchange: function(){ a += 1; }});
|
||||
$('#a').trigger('foo');
|
||||
expect(a).toEqual(1);
|
||||
});
|
||||
|
||||
$('#i').trigger('focusin'); $('#i')[0].click();
|
||||
expect(a).toEqual(1);
|
||||
it('calls most specific namespaced onfoo and most specific non-namespaced onfoo when foo triggered', function(){
|
||||
var a = 0, b = 0, c = 0, d = 0;
|
||||
|
||||
$('#i').removeAttr('checked');
|
||||
$('#a.a').entwine({
|
||||
onfoo: function(){a = 1;}
|
||||
});
|
||||
$('#a').entwine({
|
||||
onfoo: function(){b = 1;}
|
||||
});
|
||||
$('#a.a').entwine('bar', function($){return{
|
||||
onfoo: function(){c = 1;}
|
||||
};});
|
||||
$('#a').entwine('bar', function($){return{
|
||||
onfoo: function(){d = 1;}
|
||||
};});
|
||||
|
||||
$('#i').trigger('focusin'); $('#i')[0].click();
|
||||
expect(a).toEqual(2);
|
||||
});
|
||||
});
|
||||
expect([a, b, c, d]).toEqual([0, 0, 0, 0]);
|
||||
|
||||
});
|
||||
|
||||
$('#a').trigger('foo');
|
||||
expect([a, b, c, d]).toEqual([1, 0, 1, 0]);
|
||||
});
|
||||
|
||||
it('calls up correctly on _super', function(){
|
||||
var a = 0, b = 0;
|
||||
|
||||
$('#a').entwine({
|
||||
onfoo: function(){a += 1;}
|
||||
});
|
||||
$('#a.a').entwine({
|
||||
onfoo: function(){this._super(); b += 1; this._super();}
|
||||
});
|
||||
|
||||
expect([a, b]).toEqual([0, 0]);
|
||||
|
||||
$('#a').trigger('foo')
|
||||
expect([a, b]).toEqual([2, 1]);
|
||||
});
|
||||
|
||||
it('passes event object', function(){
|
||||
var event;
|
||||
|
||||
$('#a').entwine({
|
||||
onfoo: function(e){event = e;}
|
||||
});
|
||||
|
||||
$('#a').trigger('foo');
|
||||
expect(event.type).toBeDefined();
|
||||
expect(event.type).toEqual('foo');
|
||||
expect(event.target).toHaveAttr('id', 'a');
|
||||
});
|
||||
|
||||
it('delegates submit events to forms', function(){
|
||||
var a = 0;
|
||||
$('<form class="foo" action="javascript:undefined">').appendTo('#dom_test');
|
||||
|
||||
$('.foo').entwine({
|
||||
onsubmit: function(e, d){a = 1;}
|
||||
});
|
||||
|
||||
expect(a).toEqual(0);
|
||||
|
||||
$('.foo').trigger('submit');
|
||||
expect(a).toEqual(1);
|
||||
});
|
||||
|
||||
describe('can pass event data', function(){
|
||||
|
||||
it('on custom events', function(){
|
||||
var data;
|
||||
|
||||
$('#a').entwine({
|
||||
onfoo: function(e, d){data = d;}
|
||||
});
|
||||
|
||||
$('#a').trigger('foo', {cheese: 'burger'});
|
||||
expect(data.cheese).toEqual('burger');
|
||||
});
|
||||
|
||||
it('on normal events', function(){
|
||||
var data;
|
||||
|
||||
$('#a').entwine({
|
||||
onclick: function(e, d){data = d;}
|
||||
});
|
||||
|
||||
$('#a').trigger('click', {finger: 'left'});
|
||||
expect(data.finger).toEqual('left');
|
||||
});
|
||||
|
||||
it('on submit', function(){
|
||||
var data;
|
||||
$('<form class="foo" action="javascript:undefined">').appendTo('#dom_test');
|
||||
|
||||
$('.foo').entwine({
|
||||
onsubmit: function(e, d){data = d; return false;}
|
||||
});
|
||||
|
||||
$('.foo').trigger('submit', {cheese: 'burger'});
|
||||
expect(data.cheese).toEqual('burger');
|
||||
});
|
||||
});
|
||||
|
||||
describe('calls onchange on checkboxes properly', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
$('#dom_test').html('<input id="i" type="checkbox" name="test_input_i" value="i" />');
|
||||
});
|
||||
|
||||
it('calls onchange', function(){
|
||||
var a = 0;
|
||||
|
||||
$('#i').entwine({
|
||||
onchange: function(){a += 1;}
|
||||
});
|
||||
|
||||
// Can't just "click()" - it's not the same as an actual click event
|
||||
$('#i').trigger('focusin');
|
||||
$('#i')[0].click();
|
||||
expect(a).toEqual(1);
|
||||
});
|
||||
|
||||
it('calls onchange only once per change', function(){
|
||||
var a = 0;
|
||||
|
||||
$('#i').entwine({
|
||||
onchange: function(){a += 1;}
|
||||
});
|
||||
|
||||
$('#i').trigger('focusin');
|
||||
$('#i')[0].click();
|
||||
expect(a).toEqual(1);
|
||||
|
||||
$('#i').trigger('focusout');
|
||||
$('#i').trigger('focusin');
|
||||
$('#i').trigger('focusout');
|
||||
expect(a).toEqual(1);
|
||||
|
||||
$('#i')[0].click();
|
||||
expect(a).toEqual(2);
|
||||
});
|
||||
|
||||
it('calls onchange even if checked attribute altered in mean time', function(){
|
||||
var a = 0;
|
||||
|
||||
$('#i').entwine({
|
||||
onchange: function(){a += 1;}
|
||||
});
|
||||
|
||||
$('#i').trigger('focusin');
|
||||
$('#i')[0].click();
|
||||
expect(a).toEqual(1);
|
||||
|
||||
$('#i').removeAttr('checked');
|
||||
|
||||
$('#i').trigger('focusin');
|
||||
$('#i')[0].click();
|
||||
expect(a).toEqual(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -1,249 +1,275 @@
|
||||
describe( 'Entwine', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$('body').append('<div id="dom_test"></div>')
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
$('#dom_test').remove()
|
||||
});
|
||||
|
||||
describe( 'Namespaces', function() {
|
||||
describe('Entwine', function(){
|
||||
|
||||
beforeEach(function() {
|
||||
$.entwine.synchronous_mode();
|
||||
$.entwine.clear_all_rules()
|
||||
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="c d e"></div>')
|
||||
});
|
||||
|
||||
it( 'namespaced functions work (single definition mode)', function() {
|
||||
$('#a').entwine('bar', function($){return{
|
||||
bar: function(){return 'a';}
|
||||
}})
|
||||
expect($('#a').entwine('bar').bar()).toEqual( 'a');
|
||||
});
|
||||
|
||||
it( 'namespaced functions work (block definition mode)', function() {
|
||||
$.entwine('zap', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){return 'a';}
|
||||
})
|
||||
});
|
||||
expect($('#a').entwine('zap').bar()).toEqual( 'a');
|
||||
});
|
||||
|
||||
it( 'double-namespaced functions work (block definition mode)', function() {
|
||||
$.entwine('zap', function($){
|
||||
$.entwine('pow', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){return 'a';}
|
||||
})
|
||||
})
|
||||
})
|
||||
expect($('#a').entwine('zap.pow').bar()).toEqual( 'a');
|
||||
})
|
||||
beforeEach(function(){
|
||||
$('body').append('<div id="dom_test"></div>');
|
||||
});
|
||||
|
||||
it( 'revert to base namespacing work (block definition mode)', function() {
|
||||
$.entwine('zap', function($){
|
||||
$.entwine('.pow', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){return 'a';}
|
||||
})
|
||||
})
|
||||
})
|
||||
expect($('#a').entwine('pow').bar()).toEqual( 'a');
|
||||
});
|
||||
|
||||
it( 'internal to namespace, will look up functions in namespace before in base', function() {
|
||||
var res = []
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);},
|
||||
bar: function(){res.push(2); this.foo();}
|
||||
})
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){res.push(3);},
|
||||
bar: function(){res.push(4); $(this).foo();}
|
||||
}})
|
||||
|
||||
$('#dom_test div').bar();
|
||||
expect(res).toEqual( [2, 1]);
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual( [2, 1, 4, 3]);
|
||||
});
|
||||
|
||||
it( 'internal to namespace, will look up functions in namespace before in base, even in closure', function() {
|
||||
var res = []
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);},
|
||||
bar: function(){res.push(2); this.foo();}
|
||||
})
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){res.push(3);},
|
||||
bar: function(){res.push(4); $('#a').each(function(){ $(this).foo(); })}
|
||||
}})
|
||||
|
||||
$('#dom_test div').bar();
|
||||
expect(res).toEqual( [2, 1]);
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual( [2, 1, 4, 3]);
|
||||
});
|
||||
|
||||
it( 'internal to namespace, will look up functions in namespace before in base, even in onmatch', function() {
|
||||
var res = []
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);},
|
||||
bar: function(){res.push(2); this.foo();}
|
||||
})
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){res.push(3);}
|
||||
}})
|
||||
$('#a.d').entwine('bar', function($){return{
|
||||
onmatch: function(){res.push(4); this.foo();}
|
||||
}})
|
||||
|
||||
$('#dom_test div').bar();
|
||||
expect(res).toEqual( [2, 1]);
|
||||
|
||||
$('#a').addClass('d');
|
||||
expect(res).toEqual( [2, 1, 4, 3]);
|
||||
});
|
||||
|
||||
it( 'internal to namespace, will look up functions in base when not present in namespace', function() {
|
||||
var res = []
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);}
|
||||
})
|
||||
$('#a').entwine('bar', function($){return{
|
||||
bar: function(){res.push(2); this.foo();}
|
||||
}})
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual( [2, 1]);
|
||||
});
|
||||
|
||||
it( 'internal to namespace, will not look up functions in base if present in namespace, even when not applicable to selector', function() {
|
||||
var res = []
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){this.bar();}
|
||||
}})
|
||||
$('#a').entwine({
|
||||
bar: function(){res.push(1);}
|
||||
})
|
||||
$('span').entwine('bar', function($){return{
|
||||
bar: function(){res.push(2);}
|
||||
}})
|
||||
|
||||
$('#a').entwine('bar').foo()
|
||||
expect(res).toEqual( []);
|
||||
});
|
||||
|
||||
it( 'internal to namespace, can be directed to base namespace', function() {
|
||||
var res = []
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);},
|
||||
bar: function(){res.push(2); this.foo();}
|
||||
})
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){res.push(3);},
|
||||
bar: function(){res.push(4); this.foo(); this.entwine('.').foo();}
|
||||
}})
|
||||
$('#dom_test div').bar();
|
||||
expect(res).toEqual( [2, 1]);
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual( [2, 1, 4, 3, 1]);
|
||||
});
|
||||
|
||||
it( 'internal to namespace, will look up functions in namespace called the same as a regular jQuery base function', function() {
|
||||
var res = []
|
||||
$('#a').entwine('bar', function($){return{
|
||||
load: function(){res.push(1);},
|
||||
bar: function(){res.push(2); this.load();}
|
||||
}})
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual( [2, 1]);
|
||||
});
|
||||
afterEach(function(){
|
||||
$('#dom_test').remove();
|
||||
});
|
||||
|
||||
it( 'internal to namespace, can be directed to regular jQuery base function', function() {
|
||||
var res = []
|
||||
$.fn.testy = function(){ res.push(1); }
|
||||
$('#a').entwine('bar', function($){return{
|
||||
testy: function(){res.push(3);},
|
||||
bar: function(){res.push(2); this.entwine('.').testy();}
|
||||
}})
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual( [2, 1]);
|
||||
});
|
||||
|
||||
it( 'internal to namespace, can be directed to sub namespace', function() {
|
||||
var res = []
|
||||
$.entwine('zap', function($){
|
||||
$('#a').entwine({
|
||||
foo: function(){ res.push(1); this.entwine('pow').bar(); }
|
||||
})
|
||||
|
||||
$.entwine('pow', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){ res.push(2); }
|
||||
})
|
||||
})
|
||||
})
|
||||
$('#dom_test div').entwine('zap').foo();
|
||||
expect(res).toEqual( [1, 2]);
|
||||
});
|
||||
describe('Namespaces', function(){
|
||||
|
||||
it( 'internal to namespace, can be directed to unrelated namespace', function() {
|
||||
var res = []
|
||||
$.entwine('zap', function($){
|
||||
$('#a').entwine({
|
||||
foo: function(){ res.push(1); this.entwine('.pow').bar(); }
|
||||
})
|
||||
|
||||
$.entwine('pow', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){ res.push(2); }
|
||||
})
|
||||
})
|
||||
})
|
||||
$.entwine('pow', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){ res.push(3); }
|
||||
})
|
||||
})
|
||||
|
||||
$('#dom_test div').entwine('zap').foo();
|
||||
expect(res).toEqual( [1, 3]);
|
||||
});
|
||||
beforeEach(function() {
|
||||
$.entwine.synchronous_mode();
|
||||
$.entwine.clear_all_rules();
|
||||
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="c d e"></div>');
|
||||
});
|
||||
|
||||
it( 'a function passed out of a namespace will remember its namespace', function() {
|
||||
var res = []
|
||||
var func = function(func) {
|
||||
func.call($('#a, #b'));
|
||||
}
|
||||
$('#a, #b').entwine('bar', function($){return{
|
||||
zap: function(){res.push($(this).attr('id'));},
|
||||
bar: function(){res.push(2); func(this.zap);}
|
||||
}})
|
||||
$('#dom_test #a').entwine('bar').bar();
|
||||
expect(res).toEqual( [2, 'b', 'a']);
|
||||
});
|
||||
it('namespaced functions work (single definition mode)', function(){
|
||||
$('#a').entwine('bar', function($){return{
|
||||
bar: function(){return 'a';}
|
||||
};});
|
||||
|
||||
expect($('#a').entwine('bar').bar()).toEqual('a');
|
||||
});
|
||||
|
||||
it('namespaced functions work (block definition mode)', function(){
|
||||
$.entwine('zap', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){return 'a';}
|
||||
});
|
||||
});
|
||||
|
||||
expect($('#a').entwine('zap').bar()).toEqual('a');
|
||||
});
|
||||
|
||||
it('double-namespaced functions work (block definition mode)', function(){
|
||||
$.entwine('zap', function($){
|
||||
$.entwine('pow', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){return 'a';}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
expect($('#a').entwine('zap.pow').bar()).toEqual('a');
|
||||
})
|
||||
|
||||
it('revert to base namespacing work (block definition mode)', function(){
|
||||
$.entwine('zap', function($){
|
||||
$.entwine('.pow', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){return 'a';}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
expect($('#a').entwine('pow').bar()).toEqual('a');
|
||||
});
|
||||
|
||||
it('internal to namespace, will look up functions in namespace before in base', function(){
|
||||
var res = [];
|
||||
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);},
|
||||
bar: function(){res.push(2); this.foo();}
|
||||
});
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){res.push(3);},
|
||||
bar: function(){res.push(4); $(this).foo();}
|
||||
};});
|
||||
|
||||
$('#dom_test div').bar();
|
||||
expect(res).toEqual([2, 1]);
|
||||
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual([2, 1, 4, 3]);
|
||||
});
|
||||
|
||||
it('internal to namespace, will look up functions in namespace before in base, even in closure', function(){
|
||||
var res = [];
|
||||
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);},
|
||||
bar: function(){res.push(2); this.foo();}
|
||||
});
|
||||
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){res.push(3);},
|
||||
bar: function(){
|
||||
res.push(4);
|
||||
$('#a').each(function(){$(this).foo();});
|
||||
}
|
||||
};});
|
||||
|
||||
$('#dom_test div').bar();
|
||||
expect(res).toEqual([2, 1]);
|
||||
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual([2, 1, 4, 3]);
|
||||
});
|
||||
|
||||
it('internal to namespace, will look up functions in namespace before in base, even in onmatch', function(){
|
||||
var res = [];
|
||||
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);},
|
||||
bar: function(){res.push(2); this.foo();}
|
||||
});
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){res.push(3);}
|
||||
};});
|
||||
$('#a.d').entwine('bar', function($){return{
|
||||
onmatch: function(){res.push(4); this.foo();}
|
||||
};});
|
||||
|
||||
$('#dom_test div').bar();
|
||||
expect(res).toEqual([2, 1]);
|
||||
|
||||
$('#a').addClass('d');
|
||||
expect(res).toEqual([2, 1, 4, 3]);
|
||||
});
|
||||
|
||||
it('internal to namespace, will look up functions in base when not present in namespace', function(){
|
||||
var res = [];
|
||||
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);}
|
||||
});
|
||||
$('#a').entwine('bar', function($){return{
|
||||
bar: function(){res.push(2); this.foo();}
|
||||
};});
|
||||
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual([2, 1]);
|
||||
});
|
||||
|
||||
it('internal to namespace, will not look up functions in base if present in namespace, even when not applicable to selector', function(){
|
||||
var res = [];
|
||||
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){this.bar();}
|
||||
};});
|
||||
$('#a').entwine({
|
||||
bar: function(){res.push(1);}
|
||||
});
|
||||
$('span').entwine('bar', function($){return{
|
||||
bar: function(){res.push(2);}
|
||||
};});
|
||||
|
||||
$('#a').entwine('bar').foo()
|
||||
expect(res).toEqual([]);
|
||||
});
|
||||
|
||||
it('internal to namespace, can be directed to base namespace', function(){
|
||||
var res = [];
|
||||
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);},
|
||||
bar: function(){res.push(2); this.foo();}
|
||||
});
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){res.push(3);},
|
||||
bar: function(){res.push(4); this.foo(); this.entwine('.').foo();}
|
||||
};});
|
||||
|
||||
$('#dom_test div').bar();
|
||||
expect(res).toEqual([2, 1]);
|
||||
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual([2, 1, 4, 3, 1]);
|
||||
});
|
||||
|
||||
it('internal to namespace, will look up functions in namespace called the same as a regular jQuery base function', function(){
|
||||
var res = [];
|
||||
|
||||
$('#a').entwine('bar', function($){return{
|
||||
load: function(){res.push(1);},
|
||||
bar: function(){res.push(2); this.load();}
|
||||
};});
|
||||
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual([2, 1]);
|
||||
});
|
||||
|
||||
it('internal to namespace, can be directed to regular jQuery base function', function(){
|
||||
var res = [];
|
||||
|
||||
$.fn.testy = function(){res.push(1);}
|
||||
|
||||
$('#a').entwine('bar', function($){return{
|
||||
testy: function(){res.push(3);},
|
||||
bar: function(){res.push(2); this.entwine('.').testy();}
|
||||
};});
|
||||
|
||||
$('#dom_test div').entwine('bar').bar();
|
||||
expect(res).toEqual([2, 1]);
|
||||
});
|
||||
|
||||
it('internal to namespace, can be directed to sub namespace', function(){
|
||||
var res = [];
|
||||
|
||||
$.entwine('zap', function($){
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1); this.entwine('pow').bar();}
|
||||
});
|
||||
$.entwine('pow', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){res.push(2);}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$('#dom_test div').entwine('zap').foo();
|
||||
expect(res).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('internal to namespace, can be directed to unrelated namespace', function(){
|
||||
var res = [];
|
||||
|
||||
$.entwine('zap', function($){
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1); this.entwine('.pow').bar();}
|
||||
});
|
||||
$.entwine('pow', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){res.push(2);}
|
||||
});
|
||||
});
|
||||
});
|
||||
$.entwine('pow', function($){
|
||||
$('#a').entwine({
|
||||
bar: function(){res.push(3);}
|
||||
});
|
||||
});
|
||||
|
||||
$('#dom_test div').entwine('zap').foo();
|
||||
expect(res).toEqual([1, 3]);
|
||||
});
|
||||
|
||||
it('a function passed out of a namespace will remember its namespace', function(){
|
||||
var res = [];
|
||||
var func = function(func){
|
||||
func.call($('#a, #b'));
|
||||
};
|
||||
|
||||
$('#a, #b').entwine('bar', function($){return{
|
||||
zap: function(){res.push($(this).attr('id'));},
|
||||
bar: function(){res.push(2); func(this.zap);}
|
||||
};});
|
||||
|
||||
$('#dom_test #a').entwine('bar').bar();
|
||||
expect(res).toEqual([2, 'b', 'a']);
|
||||
});
|
||||
|
||||
it('using block functions', function(){
|
||||
var res = [];
|
||||
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);}
|
||||
});
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){res.push(3);}
|
||||
};});
|
||||
|
||||
$('#dom_test div').foo();
|
||||
expect(res).toEqual([1]);
|
||||
|
||||
$('#dom_test div').entwine('bar', function($){$(this).foo();});
|
||||
expect(res).toEqual([1, 3]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it( 'using block functions', function() {
|
||||
var res = []
|
||||
$('#a').entwine({
|
||||
foo: function(){res.push(1);}
|
||||
})
|
||||
$('#a').entwine('bar', function($){return{
|
||||
foo: function(){res.push(3);}
|
||||
}})
|
||||
|
||||
$('#dom_test div').foo();
|
||||
expect(res).toEqual( [1]);
|
||||
|
||||
$('#dom_test div').entwine('bar', function($){
|
||||
$(this).foo();
|
||||
})
|
||||
expect(res).toEqual( [1, 3]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
@ -1,98 +1,126 @@
|
||||
describe('Entwine', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$('body').append('<div id="dom_test"></div>')
|
||||
});
|
||||
afterEach(function() {
|
||||
$('#dom_test').remove()
|
||||
});
|
||||
|
||||
describe('Properties', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$.entwine.clear_all_rules();
|
||||
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="b c"></div>');
|
||||
});
|
||||
|
||||
it('can define and get a basic property', function() {
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
expect($('.a').getFoo()).toBeNull();
|
||||
});
|
||||
it('can define and set a basic property', function() {
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
$('.a').setFoo(1);
|
||||
expect($('.a').getFoo()).toEqual(1)
|
||||
});
|
||||
it('can define a default value', function() {
|
||||
$('#a').entwine({
|
||||
Foo: 1
|
||||
});
|
||||
expect($('.a').getFoo()).toEqual(1);
|
||||
});
|
||||
it('can override a default value with a true-ish value', function() {
|
||||
$('#a').entwine({ Foo: 1 });
|
||||
$('#a').setFoo(2);
|
||||
expect($('.a').getFoo()).toEqual(2);
|
||||
});
|
||||
it('can override a default value with a false-ish value', function() {
|
||||
$('#a').entwine({ Foo: 1 });
|
||||
$('#a').setFoo(0);
|
||||
expect($('.a').getFoo()).toEqual(0);
|
||||
});
|
||||
it('should manage proprties in namespaces without clashing', function() {
|
||||
$('#a').entwine({
|
||||
Foo: 1
|
||||
});
|
||||
$.entwine('test', function($) {
|
||||
$('#a').entwine({
|
||||
Foo: 2
|
||||
});
|
||||
});
|
||||
expect($('.a').getFoo()).toEqual(1);
|
||||
expect($('.a').entwine('test').getFoo()).toEqual(2)
|
||||
$('.a').setFoo(4);
|
||||
$('.a').entwine('test').setFoo(8);
|
||||
expect($('.a').getFoo()).toEqual(4)
|
||||
expect($('.a').entwine('test').getFoo()).toEqual(8)
|
||||
});
|
||||
it('should manage directly setting proprties in namespaces without clashing', function() {
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
$.entwine('test', function($) {
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
});
|
||||
$('.a').entwineData('Foo', 4);
|
||||
$('.a').entwine('test').entwineData('Foo', 8);
|
||||
expect($('.a').entwineData('Foo')).toEqual(4) ;
|
||||
expect($('.a').entwine('test').entwineData('Foo')).toEqual(8);
|
||||
});
|
||||
describe('jQuery style accessors', function() {
|
||||
it('can define and get a basic property', function() {
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
expect($('.a').Foo()).toBeNull();
|
||||
});
|
||||
it('can define and set a basic property', function() {
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
$('.a').Foo(1);
|
||||
expect($('.a').Foo()).toEqual(1)
|
||||
});
|
||||
it('can define a default value', function() {
|
||||
$('#a').entwine({
|
||||
Foo: 1
|
||||
});
|
||||
expect($('.a').Foo()).toEqual(1)
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Entwine', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
$('body').append('<div id="dom_test"></div>');
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
$('#dom_test').remove();
|
||||
});
|
||||
|
||||
describe('Properties', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
$.entwine.clear_all_rules();
|
||||
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="b c"></div>');
|
||||
});
|
||||
|
||||
it('can define and get a basic property', function(){
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
|
||||
expect($('.a').getFoo()).toBeNull();
|
||||
});
|
||||
|
||||
it('can define and set a basic property', function(){
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
|
||||
$('.a').setFoo(1);
|
||||
expect($('.a').getFoo()).toEqual(1)
|
||||
});
|
||||
|
||||
it('can define a default value', function(){
|
||||
$('#a').entwine({
|
||||
Foo: 1
|
||||
});
|
||||
|
||||
expect($('.a').getFoo()).toEqual(1);
|
||||
});
|
||||
|
||||
it('can override a default value with a true-ish value', function(){
|
||||
$('#a').entwine({
|
||||
Foo: 1
|
||||
});
|
||||
|
||||
$('#a').setFoo(2);
|
||||
expect($('.a').getFoo()).toEqual(2);
|
||||
});
|
||||
|
||||
it('can override a default value with a false-ish value', function(){
|
||||
$('#a').entwine({
|
||||
Foo: 1
|
||||
});
|
||||
|
||||
$('#a').setFoo(0);
|
||||
expect($('.a').getFoo()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should manage proprties in namespaces without clashing', function(){
|
||||
$('#a').entwine({
|
||||
Foo: 1
|
||||
});
|
||||
|
||||
$.entwine('test', function($){
|
||||
$('#a').entwine({
|
||||
Foo: 2
|
||||
});
|
||||
});
|
||||
|
||||
expect($('.a').getFoo()).toEqual(1);
|
||||
expect($('.a').entwine('test').getFoo()).toEqual(2)
|
||||
|
||||
$('.a').setFoo(4);
|
||||
$('.a').entwine('test').setFoo(8);
|
||||
expect($('.a').getFoo()).toEqual(4)
|
||||
expect($('.a').entwine('test').getFoo()).toEqual(8)
|
||||
});
|
||||
|
||||
it('should manage directly setting properties in namespaces without clashing', function(){
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
|
||||
$.entwine('test', function($){
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
});
|
||||
|
||||
$('.a').entwineData('Foo', 4);
|
||||
$('.a').entwine('test').entwineData('Foo', 8);
|
||||
expect($('.a').entwineData('Foo')).toEqual(4);
|
||||
expect($('.a').entwine('test').entwineData('Foo')).toEqual(8);
|
||||
});
|
||||
|
||||
describe('jQuery style accessors', function(){
|
||||
|
||||
it('can define and get a basic property', function(){
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
|
||||
expect($('.a').Foo()).toBeNull();
|
||||
});
|
||||
|
||||
it('can define and set a basic property', function(){
|
||||
$('#a').entwine({
|
||||
Foo: null
|
||||
});
|
||||
|
||||
$('.a').Foo(1);
|
||||
expect($('.a').Foo()).toEqual(1)
|
||||
});
|
||||
|
||||
it('can define a default value', function(){
|
||||
$('#a').entwine({
|
||||
Foo: 1
|
||||
});
|
||||
|
||||
expect($('.a').Foo()).toEqual(1)
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
161
thirdparty/jquery-entwine/spec/spec.entwine.super.js
vendored
161
thirdparty/jquery-entwine/spec/spec.entwine.super.js
vendored
@ -1,88 +1,75 @@
|
||||
describe('Entwine', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$('body').append('<div id="dom_test"></div>')
|
||||
});
|
||||
afterEach(function() {
|
||||
$('#dom_test').remove()
|
||||
});
|
||||
|
||||
describe('Super', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$.entwine.clear_all_rules()
|
||||
$('#dom_test').html('<div id="a" class="a b c">Foo</div><div id="b" class="c d e">Bar</div>')
|
||||
});
|
||||
|
||||
it('can call the super function', function() {
|
||||
var a = 1;
|
||||
$('#a').entwine({
|
||||
foo: function() {
|
||||
a *= 2;
|
||||
}
|
||||
});
|
||||
$('#a.a').entwine({
|
||||
foo: function() {
|
||||
a += 2;
|
||||
this._super();
|
||||
}
|
||||
});
|
||||
$('#a').foo();
|
||||
expect(a).toEqual(6)
|
||||
});
|
||||
it('super to a non-existant class should be ignored', function() {
|
||||
var a = 1;
|
||||
$('#a').entwine({
|
||||
foo: function() {
|
||||
a *= 2;
|
||||
this._super();
|
||||
}
|
||||
});
|
||||
$('#a.a').entwine({
|
||||
foo: function() {
|
||||
a += 2;
|
||||
this._super();
|
||||
}
|
||||
});
|
||||
$('#a').foo();
|
||||
expect(a).toEqual(6)
|
||||
});
|
||||
it('can call super from two different functions without screwing up what super points to', function() {
|
||||
var list = [];
|
||||
$('#a').entwine({
|
||||
foo: function() {
|
||||
list.push('foo');
|
||||
this.bar();
|
||||
},
|
||||
bar: function() {
|
||||
list.push('bar');
|
||||
}
|
||||
});
|
||||
$('#a.a').entwine({
|
||||
foo: function() {
|
||||
list.push('foo2');
|
||||
this._super();
|
||||
list.push('foo2');
|
||||
this._super();
|
||||
},
|
||||
bar: function() {
|
||||
list.push('bar2');
|
||||
this._super();
|
||||
}
|
||||
});
|
||||
$('#a').foo();
|
||||
expect(list).toEqual(['foo2', 'foo', 'bar2', 'bar', 'foo2', 'foo', 'bar2', 'bar'])
|
||||
});
|
||||
it('can override (and call via super) a non-entwine jquery function', function() {
|
||||
var a = 1
|
||||
$('#a').entwine({
|
||||
text: function() {
|
||||
a = this._super();
|
||||
}
|
||||
});
|
||||
$('#a').text();
|
||||
expect(a).toEqual('Foo')
|
||||
expect($('#b').text()).toEqual('Bar')
|
||||
});
|
||||
});
|
||||
describe('Entwine', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
$('body').append('<div id="dom_test"></div>');
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
$('#dom_test').remove();
|
||||
});
|
||||
|
||||
describe('Super', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
$.entwine.clear_all_rules();
|
||||
$('#dom_test').html('<div id="a" class="a b c">Foo</div><div id="b" class="c d e">Bar</div>');
|
||||
});
|
||||
|
||||
it('can call the super function', function(){
|
||||
var a = 1;
|
||||
|
||||
$('#a').entwine({
|
||||
foo: function(){a *= 2;}
|
||||
});
|
||||
$('#a.a').entwine({
|
||||
foo: function(){a += 2; this._super();}
|
||||
});
|
||||
|
||||
$('#a').foo();
|
||||
expect(a).toEqual(6)
|
||||
});
|
||||
|
||||
it('super to a non-existant class should be ignored', function(){
|
||||
var a = 1;
|
||||
|
||||
$('#a').entwine({
|
||||
foo: function(){a *= 2; this._super();}
|
||||
});
|
||||
$('#a.a').entwine({
|
||||
foo: function(){a += 2; this._super();}
|
||||
});
|
||||
|
||||
$('#a').foo();
|
||||
expect(a).toEqual(6)
|
||||
});
|
||||
|
||||
it('can call super from two different functions without screwing up what super points to', function(){
|
||||
var list = [];
|
||||
|
||||
$('#a').entwine({
|
||||
foo: function(){list.push('foo'); this.bar();},
|
||||
bar: function(){list.push('bar');}
|
||||
});
|
||||
$('#a.a').entwine({
|
||||
foo: function(){list.push('foo2'); this._super(); list.push('foo2'); this._super();},
|
||||
bar: function(){list.push('bar2'); this._super();}
|
||||
});
|
||||
|
||||
$('#a').foo();
|
||||
expect(list).toEqual(['foo2', 'foo', 'bar2', 'bar', 'foo2', 'foo', 'bar2', 'bar'])
|
||||
});
|
||||
|
||||
it('can override (and call via super) a non-entwine jquery function', function(){
|
||||
var a = 1;
|
||||
|
||||
$('#a').entwine({
|
||||
text: function(){a = this._super();}
|
||||
});
|
||||
|
||||
expect($('#a').text()).toBeUndefined();
|
||||
expect(a).toEqual('Foo')
|
||||
|
||||
expect($('#b').text()).toEqual('Bar')
|
||||
});
|
||||
});
|
||||
});
|
@ -1,34 +1,41 @@
|
||||
describe( 'Entwine', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$.entwine.warningLevel = $.entwine.WARN_LEVEL_BESTPRACTISE;
|
||||
$.entwine.synchronous_mode(true);
|
||||
$('body').append('<div id="dom_test"></div>');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
$('#dom_test').remove();
|
||||
$.entwine.synchronous_mode(false);
|
||||
});
|
||||
describe('Entwine', function(){
|
||||
|
||||
describe( 'Synchronous Mode', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
beforeEach(function() {
|
||||
$.entwine.warningLevel = $.entwine.WARN_LEVEL_BESTPRACTISE;
|
||||
$.entwine.synchronous_mode(true);
|
||||
$('body').append('<div id="dom_test"></div>');
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
$('#dom_test').remove();
|
||||
$.entwine.synchronous_mode(false);
|
||||
});
|
||||
|
||||
describe('Synchronous Mode', function(){
|
||||
|
||||
beforeEach(function(){
|
||||
// $.entwine.clear_all_rules();
|
||||
});
|
||||
|
||||
it( 'can modify the DOM in onmatch', function() {
|
||||
$('#a').entwine({onmatch: function() {this.append('<div class="appended"></div>');}});
|
||||
|
||||
it('can modify the DOM in onmatch', function(){
|
||||
$('#a').entwine({
|
||||
onmatch: function(){this.append('<div class="appended"></div>');}
|
||||
});
|
||||
|
||||
$('#dom_test').append('<div id="a" class="a b c" data-fieldtype="foo"></div><div id="b" class="c d e"></div>');
|
||||
expect($('#a .appended').length).toEqual(1);
|
||||
expect($('#a .appended').length).toEqual(1);
|
||||
});
|
||||
|
||||
it( 'can modify the DOM in onunmatch', function() {
|
||||
$('#a').entwine({onunmatch: function() {$('#dom_test').append('<div class="appended"></div>');}});
|
||||
$('#dom_test').append('<div id="a" class="a b c" data-fieldtype="foo"></div><div id="b" class="c d e"></div>');
|
||||
|
||||
it('can modify the DOM in onunmatch', function(){
|
||||
$('#a').entwine({
|
||||
onmatch: function(){ /* NOP */ },
|
||||
onunmatch: function(){$('#dom_test').append('<div class="appended"></div>');}
|
||||
});
|
||||
|
||||
$('#dom_test').append('<div id="a" class="a b c" data-fieldtype="foo"></div><div id="b" class="c d e"></div>');
|
||||
$('#dom_test').find('#a').remove();
|
||||
expect($('#dom_test .appended').length).toEqual(1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
});
|
@ -45,6 +45,9 @@
|
||||
|
||||
bind: function(selector, k, v) {
|
||||
if ($.isFunction(v) && (k == 'onmatch' || k == 'onunmatch')) {
|
||||
// When we add new matchers we need to trigger a full global recalc once, regardless of the DOM changes that triggered the event
|
||||
this.matchersDirty = true;
|
||||
|
||||
this.bind_condesc(selector, k, v);
|
||||
return true;
|
||||
}
|
||||
@ -61,15 +64,39 @@
|
||||
* $('#foo').addClass('tabs'); $('#foo').tabFunctionBar();
|
||||
* won't work.
|
||||
*/
|
||||
$(document).bind('DOMMaybeChanged', function(){
|
||||
$(document).bind('DOMMaybeChanged', function(e){
|
||||
// Get the change delta. Can help stop us from doing heavy lifting if none of the changes could actually trigger an onmatch or onunmatch function
|
||||
var changes = e.changes;
|
||||
|
||||
// var start = (new Date).getTime();
|
||||
|
||||
// For every namespace
|
||||
for (var k in $.entwine.namespaces) {
|
||||
var namespace = $.entwine.namespaces[k];
|
||||
|
||||
// That has constructors or destructors
|
||||
var ctors = $.entwine.namespaces[k].store.ctors;
|
||||
var ctors = namespace.store.ctors;
|
||||
if (ctors) {
|
||||
|
||||
// Keep a record of elements that have matched already
|
||||
var matched = $([]), add, rem, res, rule, sel, ctor, dtor;
|
||||
// Keep a record of elements that have matched some previous more specific rule.
|
||||
// Not that we _don't_ actually do that until this is needed. If matched is null, it's not been calculated yet.
|
||||
// We also keep track of any elements that have newly been taken or released by a specific rule
|
||||
var matched = null, taken = $([]), released = $([]);
|
||||
|
||||
// Updates matched to contain all the previously matched elements as if we'd been keeping track all along
|
||||
var calcmatched = function(j){
|
||||
if (matched !== null) return;
|
||||
matched = $([]);
|
||||
|
||||
var cache, k = ctors.length;
|
||||
while ((--k) > j) {
|
||||
if (cache = ctors[k].cache) matched = matched.add(cache);
|
||||
}
|
||||
}
|
||||
|
||||
// Some declared variables used in the loop
|
||||
var add, rem, res, rule, sel, ctor, dtor, full;
|
||||
|
||||
// Stepping through each selector from most to least specific
|
||||
var j = ctors.length;
|
||||
while (j--) {
|
||||
@ -78,40 +105,140 @@
|
||||
sel = rule.selector.selector;
|
||||
ctor = rule.onmatch;
|
||||
dtor = rule.onunmatch;
|
||||
|
||||
// Get the list of elements that match this selector, that haven't yet matched a more specific selector
|
||||
res = add = $(sel).not(matched);
|
||||
|
||||
// If this selector has a list of elements it matched against last time
|
||||
if (rule.cache) {
|
||||
// Find the ones that are extra this time
|
||||
add = res.not(rule.cache);
|
||||
if (dtor) {
|
||||
// Find the ones that are gone this time
|
||||
rem = rule.cache.not(res);
|
||||
// And call the destructor on them
|
||||
if (rem.length && !rule.onunmatchRunning) {
|
||||
rule.onunmatchRunning = true;
|
||||
ctors.onunmatchproxy(rem, j, dtor);
|
||||
rule.onunmatchRunning = false;
|
||||
|
||||
/*
|
||||
Rule.cache might be stale or fresh. It'll be stale if
|
||||
- some more specific selector now has some of rule.cache in it
|
||||
- some change has happened that means new elements match this selector now
|
||||
- some change has happened that means elements no longer match this selector
|
||||
|
||||
The first we can just compare rules.cache with matched, removing anything that's there already.
|
||||
*/
|
||||
|
||||
// Reset the "elements that match this selector and no more specific selector with an onmatch rule" to null.
|
||||
// Staying null means this selector is fresh.
|
||||
res = null;
|
||||
|
||||
// If this gets changed to true, it's too hard to do a delta update, so do a full update
|
||||
full = false;
|
||||
|
||||
if (namespace.matchersDirty || changes.global) {
|
||||
// For now, just fall back to old version. We need to do something like changed.Subtree.find('*').andSelf().filter(sel), but that's _way_ slower on modern browsers than the below
|
||||
full = true;
|
||||
}
|
||||
else {
|
||||
// We don't deal with attributes yet, so any attribute change means we need to do a full recalc
|
||||
for (var k in e.changes.attrs) { full = true; break; }
|
||||
|
||||
/*
|
||||
If a class changes, but it isn't listed in our selector, we don't care - the change couldn't affect whether or not any element matches
|
||||
|
||||
If it is listed on our selector
|
||||
- If it is on the direct match part, it could have added or removed the node it changed on
|
||||
- If it is on the context part, it could have added or removed any node that were previously included or excluded because of a match or failure to match with the context required on that node
|
||||
- NOTE: It might be on _both_
|
||||
*/
|
||||
|
||||
var method = rule.selector.affectedBy(e.changes);
|
||||
|
||||
if (method.classes.context) {
|
||||
full = true;
|
||||
}
|
||||
else {
|
||||
for (var k in method.classes.direct) {
|
||||
calcmatched(j);
|
||||
var recheck = e.changes.classes[k].not(matched);
|
||||
|
||||
if (res === null) {
|
||||
res = rule.cache ? rule.cache.not(taken).add(released.filter(sel)) : $([]);
|
||||
}
|
||||
|
||||
res = res.not(recheck).add(recheck.filter(sel));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call the constructor on the newly matched ones
|
||||
if (add.length && ctor && !rule.onmatchRunning) {
|
||||
rule.onmatchRunning = true;
|
||||
ctors.onmatchproxy(add, j, ctor);
|
||||
rule.onmatchRunning = false;
|
||||
|
||||
if (full) {
|
||||
calcmatched(j);
|
||||
res = $(sel).not(matched);
|
||||
}
|
||||
else {
|
||||
if (!res) {
|
||||
// We weren't stale because of any changes to the DOM that affected this selector, but more specific
|
||||
// onmatches might have caused stale-ness
|
||||
|
||||
// Do any of the previous released elements match this selector?
|
||||
add = released.length && released.filter(sel);
|
||||
|
||||
if (add && add.length) {
|
||||
// Yes, so we're stale as we need to include them. Filter for any possible taken value at the same time
|
||||
res = rule.cache ? rule.cache.not(taken).add(add) : add;
|
||||
}
|
||||
else {
|
||||
// Do we think we own any of the elements now taken by more specific rules?
|
||||
rem = taken.length && rule.cache && rule.cache.filter(taken);
|
||||
|
||||
if (rem && rem.length) {
|
||||
// Yes, so we're stale as we need to exclude them.
|
||||
res = rule.cache.not(rem);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Res will be null if we know we are fresh (no full needed, selector not affectedBy changes)
|
||||
if (res === null) {
|
||||
// If we are tracking matched, add ourselves
|
||||
if (matched && rule.cache) matched = matched.add(rule.cache);
|
||||
}
|
||||
else {
|
||||
// If this selector has a list of elements it matched against last time
|
||||
if (rule.cache) {
|
||||
// Find the ones that are extra this time
|
||||
add = res.not(rule.cache);
|
||||
rem = rule.cache.not(res);
|
||||
}
|
||||
else {
|
||||
add = res; rem = null;
|
||||
}
|
||||
|
||||
if ((add && add.length) || (rem && rem.length)) {
|
||||
if (rem && rem.length) {
|
||||
released = released.add(rem);
|
||||
|
||||
if (dtor && !rule.onunmatchRunning) {
|
||||
rule.onunmatchRunning = true;
|
||||
ctors.onunmatchproxy(rem, j, dtor);
|
||||
rule.onunmatchRunning = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Call the constructor on the newly matched ones
|
||||
if (add && add.length) {
|
||||
taken = taken.add(add);
|
||||
released = released.not(add);
|
||||
|
||||
if (ctor && !rule.onmatchRunning) {
|
||||
rule.onmatchRunning = true;
|
||||
ctors.onmatchproxy(add, j, ctor);
|
||||
rule.onmatchRunning = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we are tracking matched, add ourselves
|
||||
if (matched) matched = matched.add(res);
|
||||
|
||||
// And remember this list of matching elements again this selector, so next matching we can find the unmatched ones
|
||||
rule.cache = res;
|
||||
}
|
||||
|
||||
// Add these matched ones to the list tracking all elements matched so far
|
||||
matched = matched.add(res);
|
||||
// And remember this list of matching elements again this selector, so next matching we can find the unmatched ones
|
||||
ctors[j].cache = res;
|
||||
}
|
||||
|
||||
namespace.matchersDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
// console.log((new Date).getTime() - start);
|
||||
});
|
||||
|
||||
|
||||
|
@ -1,17 +1,138 @@
|
||||
(function($){
|
||||
|
||||
|
||||
/** Utility function to monkey-patch a jQuery method */
|
||||
var monkey = function( /* method, method, ...., patch */){
|
||||
var methods = $.makeArray(arguments);
|
||||
var patch = methods.pop();
|
||||
|
||||
$.each(methods, function(i, method){
|
||||
var old = $.fn[method];
|
||||
|
||||
$.fn[method] = function() {
|
||||
var self = this, args = $.makeArray(arguments);
|
||||
|
||||
var rv = old.apply(self, args);
|
||||
patch.apply(self, args);
|
||||
return rv;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** What to call to run a function 'soon'. Normally setTimeout, but for syncronous mode we override so soon === now */
|
||||
var runSoon = window.setTimeout;
|
||||
|
||||
/** The timer handle for the asyncronous matching call */
|
||||
var check_id = null;
|
||||
|
||||
/** Fire the change event. Only fires on the document node, so bind to that */
|
||||
var triggerEvent = function() {
|
||||
$(document).triggerHandler('DOMMaybeChanged');
|
||||
check_id = null;
|
||||
};
|
||||
|
||||
var ChangeDetails = Base.extend({
|
||||
|
||||
init: function() {
|
||||
this.global = false;
|
||||
this.attrs = {};
|
||||
this.classes = {};
|
||||
},
|
||||
|
||||
/** Fire the change event. Only fires on the document node, so bind to that */
|
||||
triggerEvent: function() {
|
||||
// If we're not the active changes instance any more, don't trigger
|
||||
if (changes != this) return;
|
||||
|
||||
// Cancel any pending timeout (if we're directly called in the mean time)
|
||||
if (this.check_id) clearTimeout(this.check_id);
|
||||
|
||||
// Create a new event object
|
||||
var event = $.Event("DOMMaybeChanged");
|
||||
event.changes = this;
|
||||
|
||||
// Reset the global changes object to be a new instance (do before trigger, in case trigger fires changes itself)
|
||||
changes = new ChangeDetails();
|
||||
|
||||
// Fire event
|
||||
$(document).triggerHandler(event);
|
||||
},
|
||||
|
||||
changed: function() {
|
||||
if (!this.check_id) {
|
||||
var self = this;
|
||||
this.check_id = runSoon(function(){ self.check_id = null; self.triggerEvent(); }, 10);
|
||||
}
|
||||
},
|
||||
|
||||
addAll: function() {
|
||||
if (this.global) return this; // If we've already flagged as a global change, just skip
|
||||
|
||||
this.global = true;
|
||||
this.changed();
|
||||
return this;
|
||||
},
|
||||
|
||||
addSubtree: function(node) {
|
||||
return this.addAll();
|
||||
},
|
||||
|
||||
/* For now we don't do this. It's expensive, and jquery.entwine.ctors doesn't use this information anyway */
|
||||
addSubtreeFuture: function(node) {
|
||||
if (this.global) return this; // If we've already flagged as a global change, just skip
|
||||
|
||||
this.subtree = this.subtree ? this.subtree.add(node) : $(node);
|
||||
this.changed();
|
||||
return this;
|
||||
},
|
||||
|
||||
addAttr: function(attr, node) {
|
||||
if (this.global) return this;
|
||||
|
||||
this.attrs[attr] = (attr in this.attrs) ? this.attrs[attr].add(node) : $(node);
|
||||
this.changed();
|
||||
return this;
|
||||
},
|
||||
|
||||
addClass: function(klass, node) {
|
||||
if (this.global) return this;
|
||||
|
||||
this.classes[klass] = (klass in this.classes) ? this.classes[klass].add(node) : $(node);
|
||||
this.changed();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
||||
var changes = new ChangeDetails();
|
||||
|
||||
|
||||
monkey('append', 'prepend', 'empty', 'html', function(){
|
||||
changes.addSubtree(this);
|
||||
});
|
||||
|
||||
monkey('after', 'before', 'remove', 'detach', function(){
|
||||
changes.addSubtree(this.parent());
|
||||
})
|
||||
|
||||
monkey('removeAttr', function(attr){
|
||||
changes.addAttr(attr, this);
|
||||
});
|
||||
|
||||
monkey('addClass', 'removeClass', 'toggleClass', function(klass){
|
||||
if (typeof klass == 'string') changes.addClass(klass, this);
|
||||
});
|
||||
|
||||
monkey('attr', function(a, b){
|
||||
if (b !== undefined && typeof a == 'string') changes.addAttr(a, this);
|
||||
else if (typeof a != 'string') { for (var k in a) changes.addAttr(k, this); }
|
||||
});
|
||||
|
||||
/*
|
||||
These manipulation functions call one or more of the above to do the actual manipulation:
|
||||
appendTo -> append
|
||||
prependTo -> prepend
|
||||
insertBefore -> before
|
||||
insertAfter -> after
|
||||
replaceWith -> before || append
|
||||
replaceAll -> replaceWith
|
||||
text -> empty, appendWith
|
||||
wrapAll -> insertBefore, append
|
||||
wrapInner -> wrapAll || append
|
||||
wrap -> wrapAll
|
||||
unwrap -> replaceWith
|
||||
*/
|
||||
|
||||
$.extend($.entwine, {
|
||||
/**
|
||||
* Make onmatch and onunmatch work in synchronous mode - that is, new elements will be detected immediately after
|
||||
@ -19,46 +140,24 @@
|
||||
* (otherwise we'd make it the default).
|
||||
*/
|
||||
synchronous_mode: function() {
|
||||
if (check_id) clearTimeout(check_id); check_id = null;
|
||||
if (changes && changes.check_id) clearTimeout(changes.check_id);
|
||||
changes = new ChangeDetails();
|
||||
|
||||
runSoon = function(func, delay){ func.call(this); return null; };
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Trigger onmatch and onunmatch now - usefull for after DOM manipulation by methods other than through jQuery.
|
||||
* Called automatically on document.ready
|
||||
*/
|
||||
triggerMatching: function() {
|
||||
matching();
|
||||
changes.addAll(); //.triggerEvent();
|
||||
}
|
||||
});
|
||||
|
||||
function registerMutateFunction() {
|
||||
$.each(arguments, function(i,func){
|
||||
var old = $.fn[func];
|
||||
$.fn[func] = function() {
|
||||
var rv = old.apply(this, arguments);
|
||||
if (!check_id) check_id = runSoon(triggerEvent, 100);
|
||||
return rv;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function registerSetterGetterFunction() {
|
||||
$.each(arguments, function(i,func){
|
||||
var old = $.fn[func];
|
||||
$.fn[func] = function(a, b) {
|
||||
var rv = old.apply(this, arguments);
|
||||
if (!check_id && (b !== undefined || typeof a != 'string')) check_id = runSoon(triggerEvent, 100);
|
||||
return rv;
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// Register core DOM manipulation methods
|
||||
registerMutateFunction('append', 'prepend', 'after', 'before', 'wrap', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove');
|
||||
registerSetterGetterFunction('attr');
|
||||
|
||||
// And on DOM ready, trigger matching once
|
||||
$(function(){ triggerEvent(); });
|
||||
|
||||
$(function(){
|
||||
$.entwine.triggerMatching();
|
||||
});
|
||||
|
||||
})(jQuery);
|
226
thirdparty/jquery-entwine/src/jquery.entwine.inspector.js
vendored
Normal file
226
thirdparty/jquery-entwine/src/jquery.entwine.inspector.js
vendored
Normal file
@ -0,0 +1,226 @@
|
||||
|
||||
document.write([
|
||||
'<style type="text/css">',
|
||||
'#entwine-inspector { position: fixed; z-index: 1000001; left: 0; right: 0; height: 400px; background: white; -webkit-box-shadow: 0 5px 40px 0 black; -moz-box-shadow: 0 5px 40px 0 black; }',
|
||||
'#entwine-inspector li { list-style: none; margin: 2px 0; padding: 2px 0; }',
|
||||
'#entwine-inspector li:hover { background: #eee; }',
|
||||
'#entwine-inspector li.selected { background: #ddd; }',
|
||||
|
||||
'#ei-columns { overflow: hidden; display: -webkit-box; display: -moz-box; width: 100%; height: 380px; }',
|
||||
|
||||
'.ei-column { height: 380px; width: 1px; -webkit-box-flex: 1; -moz-box-flex: 1; }',
|
||||
'#entwine-inspector .ei-column h1 { display: block; margin: 0; padding: 5px 2px; height: 20px; text-align: center; background: #444; color: #eee; font-size: 14px; font-weight: bold; }',
|
||||
'#entwine-inspector .ei-column ul { overflow-y: scroll; height: 350px; }',
|
||||
|
||||
'#ei-options { overflow: hidden; height: 20px; background: #444; color: #eee; }',
|
||||
'#ei-options label { padding-right: 5px; border-right: 1px solid #eee; }',
|
||||
|
||||
'.ei-entwined:hover, .ei-selected { background: rgba(128,0,0,0.2); }',
|
||||
'.ei-hovernode { position: absolute; z-index: 1000000; background: rgba(0,0,0,0.3); border: 1px solid white; outline: 1px solid white; }',
|
||||
|
||||
'#ei-selectors li { color: #aaa; display: none; }',
|
||||
'#ei-selectors li.matching, #entwine-inspector.show-unmatched #ei-selectors li { display: block; }',
|
||||
'#ei-selectors li.matching { color: black; }',
|
||||
'</style>'
|
||||
].join("\n"));
|
||||
|
||||
jQuery(function($){
|
||||
|
||||
var inspectorPanel = $('<div id="entwine-inspector" class="show-unmatched"></div>').appendTo('body');
|
||||
var columnHolder = $('<div id="ei-columns"></div>').appendTo(inspectorPanel);
|
||||
var optionsHolder = $('<div id="ei-options"></div>').appendTo(inspectorPanel);
|
||||
|
||||
inspectorPanel.css({
|
||||
top: -400,
|
||||
visibility: 'hidden'
|
||||
});
|
||||
|
||||
$('body').bind('keypress', function(e){
|
||||
if ((e.ctrlKey || e.metaKey) && e.which == 96) {
|
||||
if (inspectorPanel.css('visibility') != 'visible') {
|
||||
inspectorPanel.css({top: 0, visibility: 'visible'});
|
||||
$('body').css({marginTop: 400});
|
||||
initialise();
|
||||
}
|
||||
else {
|
||||
inspectorPanel.css({top: -400, visibility: 'hidden'});
|
||||
$('body').css({marginTop: 0});
|
||||
reset();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
var showUnmatching = $('<input id="ei-option-showunmatching" type="checkbox" checked="checked" />').appendTo(optionsHolder);
|
||||
var showUnmatchingLabel = $('<label>Show selectors that dont match</label>').appendTo(optionsHolder);
|
||||
|
||||
showUnmatching.bind('click', function(){
|
||||
inspectorPanel.toggleClass('show-unmatched', $(this).val());
|
||||
});
|
||||
|
||||
var hovernode;
|
||||
|
||||
var reset = function() {
|
||||
$('.ei-entwined').unbind('.entwine-inspector').removeClass('ei-entwined');
|
||||
if (hovernode) hovernode.remove();
|
||||
}
|
||||
|
||||
var initialise = function(){
|
||||
reset();
|
||||
|
||||
$.each($.entwine.namespaces, function(name, namespace){
|
||||
$.each(namespace.store, function(name, list){
|
||||
$.each(list, function(i, rule){
|
||||
var match = $(rule.selector.selector);
|
||||
match.addClass('ei-entwined').bind('click.entwine-inspector', displaydetails);
|
||||
})
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var displaydetails = function(e){
|
||||
e.preventDefault(); e.stopPropagation();
|
||||
|
||||
columnHolder.empty();
|
||||
|
||||
var columns = {};
|
||||
$.each(['elements', 'namespaces', 'methods', 'selectors'], function(i, col){
|
||||
columns[col] = $('<div id="ei-'+col+'" class="ei-column"><h1>'+col+'</h1></div>').appendTo(columnHolder);
|
||||
})
|
||||
|
||||
var lists = {};
|
||||
|
||||
var ctr = 0;
|
||||
|
||||
lists.elements = $('<ul></ul>').appendTo(columns.elements);
|
||||
|
||||
var displayelement = function(){
|
||||
var target = $(this);
|
||||
|
||||
var li = $('<li></li>');
|
||||
var clone = $(this.cloneNode(false)); clone.removeClass('ei-entwined'); clone.removeAttr('style');
|
||||
|
||||
|
||||
var i = clone[0].attributes.length;
|
||||
while (i--) {
|
||||
var attr = clone[0].attributes.item(i);
|
||||
if (attr.name != 'class' && attr.name != 'id' && attr.value.length > 20) attr.value = attr.value.substr(0, 18)+'..'+attr.value.substr(-2);
|
||||
}
|
||||
|
||||
li.append(clone);
|
||||
|
||||
li.text(li.html()).attr('data-id', ++ctr).data('el', target).prependTo(lists.elements);
|
||||
|
||||
var namespaces = $('<ul data-element="'+ctr+'"></ul>').appendTo(columns.namespaces);
|
||||
|
||||
$.each($.entwine.namespaces, function(name, namespace){
|
||||
var methods = $('<ul data-namespace="'+ctr+'-'+name+'"></ul>');
|
||||
|
||||
$.each(namespace.store, function(method, list){
|
||||
|
||||
if (method == 'ctors') {
|
||||
var matchselectors = $('<ul data-method="'+ctr+'-'+name+'-onmatch"></ul>');
|
||||
var unmatchselectors = $('<ul data-method="'+ctr+'-'+name+'-onunmatch"></ul>');
|
||||
|
||||
$.each(list, function(i, rule){
|
||||
var matchitem = $('<li>'+rule.selector.selector+'</li>').prependTo(matchselectors);
|
||||
var unmatchitem = rule.onunmatch ? $('<li>'+rule.selector.selector+'</li>').prependTo(unmatchselectors) : null;
|
||||
|
||||
if (target.is(rule.selector.selector)) {
|
||||
matchitem.addClass('matching'); unmatchitem && unmatchitem.addClass('matching');
|
||||
|
||||
if (!methods.parent().length) {
|
||||
$('<li data-namespace="'+ctr+'-'+name+'">'+name+'</li>').prependTo(namespaces);
|
||||
methods.appendTo(columns.methods);
|
||||
}
|
||||
|
||||
if (!matchselectors.parent().length) {
|
||||
$('<li data-method="'+ctr+'-'+name+'-onmatch">onmatch</li>').prependTo(methods);
|
||||
matchselectors.appendTo(columns.selectors);
|
||||
}
|
||||
|
||||
if (rule.onunmatch && !unmatchselectors.parent().length) {
|
||||
$('<li data-method="'+ctr+'-'+name+'-onunmatch">onunmatch</li>').prependTo(methods);
|
||||
unmatchselectors.appendTo(columns.selectors);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
var selectors = $('<ul data-method="'+ctr+'-'+name+'-'+method+'"></ul>');
|
||||
|
||||
$.each(list, function(i, rule){
|
||||
var ruleitem = $('<li>'+rule.selector.selector+'</li>').prependTo(selectors);
|
||||
|
||||
if (target.is(rule.selector.selector)){
|
||||
ruleitem.addClass('matching');
|
||||
|
||||
if (!methods.parent().length) {
|
||||
$('<li data-namespace="'+ctr+'-'+name+'">'+name+'</li>').prependTo(namespaces);
|
||||
methods.appendTo(columns.methods);
|
||||
}
|
||||
|
||||
if (!selectors.parent().length) {
|
||||
$('<li data-method="'+ctr+'-'+name+'-'+method+'">'+method+'</li>').prependTo(methods);
|
||||
selectors.appendTo(columns.selectors);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$.each($(e.target).parents().andSelf().filter('.ei-entwined'), displayelement);
|
||||
$('#ei-elements > ul:first > li:first').click();
|
||||
}
|
||||
|
||||
var activatelist = function(list) {
|
||||
list = $(list);
|
||||
|
||||
list.siblings('ul').css('display', 'none');
|
||||
|
||||
list.css('display', 'block');
|
||||
list.children().first().click();
|
||||
}
|
||||
|
||||
$('#entwine-inspector').live('mouseleave', function(){
|
||||
if (hovernode) hovernode.hide();
|
||||
})
|
||||
|
||||
$('#entwine-inspector').live('mouseenter', function(){
|
||||
if (hovernode) hovernode.show();
|
||||
})
|
||||
|
||||
$('#ei-elements > ul > li').live('click', function(e){
|
||||
var target = $(e.target), id = target.attr('data-id');
|
||||
target.addClass('selected').siblings().removeClass('selected');
|
||||
|
||||
if (!hovernode) {
|
||||
hovernode = $('<div class="ei-hovernode"></div>').appendTo('body');
|
||||
}
|
||||
|
||||
var hover = target.data('el');
|
||||
hovernode.css({width: hover.outerWidth()-2, height: hover.outerHeight()-2, top: hover.offset().top, left: hover.offset().left});
|
||||
|
||||
$('.ei-selected').removeClass('ei-selected');
|
||||
|
||||
activatelist('#ei-namespaces ul[data-element='+id+']');
|
||||
});
|
||||
|
||||
$('#ei-namespaces > ul > li').live('click', function(e){
|
||||
var target = $(e.target), namespace = target.attr('data-namespace');
|
||||
target.addClass('selected').siblings().removeClass('selected');
|
||||
|
||||
activatelist('#ei-methods ul[data-namespace='+namespace+']');
|
||||
});
|
||||
|
||||
$('#ei-methods > ul > li').live('click', function(e){
|
||||
var target = $(e.target), method = target.attr('data-method');
|
||||
target.addClass('selected').siblings().removeClass('selected');
|
||||
|
||||
activatelist('#ei-selectors ul[data-method='+method+']');
|
||||
});
|
||||
|
||||
});
|
58
thirdparty/jquery-entwine/src/jquery.selector.affectedby.js
vendored
Normal file
58
thirdparty/jquery-entwine/src/jquery.selector.affectedby.js
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
(function($) {
|
||||
|
||||
// TODO:
|
||||
// Make attributes & IDs work
|
||||
|
||||
var DIRECT = /DIRECT/g;
|
||||
var CONTEXT = /CONTEXT/g;
|
||||
var EITHER = /DIRECT|CONTEXT/g;
|
||||
|
||||
$.selector.SelectorBase.addMethod('affectedBy', function(props) {
|
||||
this.affectedBy = new Function('props', ([
|
||||
'var direct_classes, context_classes, direct_attrs, context_attrs, t;',
|
||||
this.ABC_compile().replace(DIRECT, 'direct').replace(CONTEXT, 'context'),
|
||||
'return {classes: {context: context_classes, direct: direct_classes}, attrs: {context: context_attrs, direct: direct_attrs}};'
|
||||
]).join("\n"));
|
||||
|
||||
// DEBUG: Print out the compiled funciton
|
||||
// console.log(this.selector, ''+this.affectedBy);
|
||||
|
||||
return this.affectedBy(props);
|
||||
});
|
||||
|
||||
$.selector.SimpleSelector.addMethod('ABC_compile', function() {
|
||||
var parts = [];
|
||||
|
||||
$.each(this.classes, function(i, cls){
|
||||
parts[parts.length] = "if (t = props.classes['"+cls+"']) (DIRECT_classes || (DIRECT_classes = {}))['"+cls+"'] = t;";
|
||||
});
|
||||
|
||||
$.each(this.nots, function(i, not){
|
||||
parts[parts.length] = not.ABC_compile();
|
||||
});
|
||||
|
||||
return parts.join("\n");
|
||||
});
|
||||
|
||||
$.selector.Selector.addMethod('ABC_compile', function(arg){
|
||||
var parts = [];
|
||||
var i = this.parts.length-1;
|
||||
|
||||
parts[parts.length] = this.parts[i].ABC_compile();
|
||||
while ((i = i - 2) >= 0) parts[parts.length] = this.parts[i].ABC_compile().replace(EITHER, 'CONTEXT');
|
||||
|
||||
return parts.join("\n");
|
||||
});
|
||||
|
||||
$.selector.SelectorsGroup.addMethod('ABC_compile', function(){
|
||||
var parts = [];
|
||||
|
||||
$.each(this.parts, function(i,part){
|
||||
parts[parts.length] = part.ABC_compile();
|
||||
});
|
||||
|
||||
return parts.join("\n");
|
||||
});
|
||||
|
||||
|
||||
})(jQuery);
|
Loading…
x
Reference in New Issue
Block a user