Skip to content
Browse files

MINOR: Update jquery.entwine to latest version. Brings speed improvem…

…ents in onmatch, and an inspector.
  • Loading branch information...
1 parent a53cca8 commit 7b8e25467efa6e528b9629bb8c5ae28cef0f6c5d @hafriedlander hafriedlander committed
View
8 thirdparty/jquery-entwine/.piston.yml
@@ -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
View
1 thirdparty/jquery-entwine/build.sh
@@ -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 \
View
1,971 thirdparty/jquery-entwine/dist/jquery.concrete-dist.js
1,971 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
431 thirdparty/jquery-entwine/dist/jquery.entwine-dist.js
@@ -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);
});
View
10 thirdparty/jquery-entwine/spec/SpecRunner.html
@@ -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;
View
179 thirdparty/jquery-entwine/spec/spec.entwine.basics.js
@@ -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( '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']);
- });
- });
+describe('Entwine', function(){
+
+ beforeEach(function(){
+ $.entwine.warningLevel = $.entwine.WARN_LEVEL_BESTPRACTISE;
+ $('body').append('<div id="dom_test"></div>');
+ });
+
+ 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']);
+ });
+ });
});
View
213 thirdparty/jquery-entwine/spec/spec.entwine.ctors.js
@@ -1,68 +1,147 @@
-describe( 'Entwine', function() {
-
- beforeEach(function() {
- $('body').append('<div id="dom_test"></div>')
- });
-
- afterEach(function() {
- $('#dom_test').remove()
- });
-
- 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]);
- });
-
- });
+describe('Entwine', function(){
+
+ beforeEach(function(){
+ $('body').append('<div id="dom_test"></div>');
+ });
+
+ afterEach(function(){
+ $('#dom_test').remove();
+ });
+
+ 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]);
+ });
+ });
});
View
383 thirdparty/jquery-entwine/spec/spec.entwine.events.js
@@ -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] );
-
- $('#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);
- });
- });
-
- });
-
+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]);
+
+ $('#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);
+ });
+ });
+ });
});
View
522 thirdparty/jquery-entwine/spec/spec.entwine.namespaces.js
@@ -1,249 +1,275 @@
-describe( 'Entwine', function() {
-
- beforeEach(function() {
- $('body').append('<div id="dom_test"></div>')
- });
-
- afterEach(function() {
- $('#dom_test').remove()
- });
-
- describe( 'Namespaces', 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');
- })
-
- 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]);
- });
-
- });
-
+describe('Entwine', function(){
+
+ beforeEach(function(){
+ $('body').append('<div id="dom_test"></div>');
+ });
+
+ afterEach(function(){
+ $('#dom_test').remove();
+ });
+
+ describe('Namespaces', 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');
+ })
+
+ 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]);
+ });
+
+ });
+
});
View
222 thirdparty/jquery-entwine/spec/spec.entwine.properties.js
@@ -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)
+ });
+ });
+ });
});
View
161 thirdparty/jquery-entwine/spec/spec.entwine.super.js
@@ -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() {