Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

API CHANGE: Upgrade jquery entwine

  • Loading branch information...
commit bbd1bb7495719f763ee4af77746f87b27c16f45c 1 parent 8177c93
@hafriedlander hafriedlander authored chillu committed
Showing with 5,048 additions and 11,296 deletions.
  1. +3 −3 thirdparty/jquery-entwine/.piston.yml
  2. +4 −1 thirdparty/jquery-entwine/build.sh
  3. +361 −69 thirdparty/jquery-entwine/dist/jquery.concrete-dist.js
  4. +361 −69 thirdparty/jquery-entwine/dist/jquery.entwine-dist.js
  5. +12 −5 thirdparty/jquery-entwine/spec/SpecRunner.html
  6. +115 −0 thirdparty/jquery-entwine/spec/spec.entwine.addrem.js
  7. +8 −0 thirdparty/jquery-entwine/spec/spec.entwine.basics.js
  8. +141 −0 thirdparty/jquery-entwine/spec/spec.entwine.eventcapture.js
  9. +133 −0 thirdparty/jquery-entwine/src/domevents/jquery.entwine.domevents.addrem.js
  10. +16 −30 ...y-entwine/src/{jquery.entwine.dommaybechanged.js → domevents/jquery.entwine.domevents.maybechanged.js}
  11. +63 −0 thirdparty/jquery-entwine/src/jquery.entwine.addrem.js
  12. +4 −7 thirdparty/jquery-entwine/src/jquery.entwine.ctors.js
  13. +111 −0 thirdparty/jquery-entwine/src/jquery.entwine.eventcapture.js
  14. +6 −8 thirdparty/jquery-entwine/src/jquery.entwine.events.js
  15. +1 −1  thirdparty/jquery-entwine/src/jquery.entwine.inspector.js
  16. +4 −2 thirdparty/jquery-entwine/src/jquery.entwine.js
  17. +0 −188 thirdparty/jquery-entwine/vendor/jasmine-1.0.1/jasmine-html.js
  18. +0 −166 thirdparty/jquery-entwine/vendor/jasmine-1.0.1/jasmine.css
  19. +1 −1  thirdparty/jquery-entwine/vendor/{jasmine-1.0.1 → jasmine}/MIT.LICENSE
  20. +616 −0 thirdparty/jquery-entwine/vendor/jasmine/jasmine-html.js
  21. +81 −0 thirdparty/jquery-entwine/vendor/jasmine/jasmine.css
  22. +346 −238 thirdparty/jquery-entwine/vendor/{jasmine-1.0.1 → jasmine}/jasmine.js
  23. +0 −8,374 thirdparty/jquery-entwine/vendor/jquery-1.5.2.js
  24. +2,652 −2,113 thirdparty/jquery-entwine/vendor/{jquery-1.6.js → jquery-1.7.2.js}
  25. +9 −21 thirdparty/jquery-entwine/vendor/jquery.selector/jquery.selector.matches.js
View
6 thirdparty/jquery-entwine/.piston.yml
@@ -1,8 +1,8 @@
---
repository_url: https://github.com/hafriedlander/jquery.entwine.git
-repository_class: Piston::Git::Repository
+format: 1
handler:
- commit: 860ffe280044c3f88e64ba400f8f17b5d2616c39
branch: master
+ commit: ca22d2b88771cf3c65e65288c4991050a42883a3
+repository_class: Piston::Git::Repository
lock: false
-format: 1
View
5 thirdparty/jquery-entwine/build.sh
@@ -16,9 +16,12 @@ for x in \
src/jquery.selector.affectedby.js \
src/jquery.focusinout.js \
src/jquery.entwine.js \
- src/jquery.entwine.dommaybechanged.js \
+ src/domevents/jquery.entwine.domevents.addrem.js \
+ src/domevents/jquery.entwine.domevents.maybechanged.js \
src/jquery.entwine.events.js \
+ src/jquery.entwine.eventcapture.js \
src/jquery.entwine.ctors.js \
+ src/jquery.entwine.addrem.js \
src/jquery.entwine.properties.js \
src/jquery.entwine.legacy.js
do \
View
430 thirdparty/jquery-entwine/dist/jquery.concrete-dist.js
@@ -353,20 +353,6 @@ Sizzle is good for finding elements for a selector, but not so good for telling
// 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;
@@ -472,7 +458,13 @@ Sizzle is good for finding elements for a selector, but not so good for telling
this.wsattrs[attr] = true;
return join([this.uses_attr(attr), 'var _WS_'+varForAttr(attr)+' = " "+'+varForAttr(attr)+'+" ";']);
},
-
+
+ uses_jqueryFilters: function() {
+ if (this.jqueryFiltersAdded) return;
+ this.jqueryFiltersAdded = true;
+ return 'var _$filters = jQuery.find.selectors.filters;';
+ },
+
save: function(lbl) {
return 'var el'+lbl+' = el;';
},
@@ -584,12 +576,8 @@ Sizzle is good for finding elements for a selector, but not so good for telling
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] = el.uses_jqueryFilters();
+ js[js.length] = 'if (!_$filters.'+pscls[0]+'(el)) BAD;';
}
});
@@ -845,8 +833,10 @@ catch (e) {
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');
+ $(window).unbind('.entwine');
// Remove namespaces, and start over again
- namespaces = $.entwine.namespaces = {};
+ for (var k in namespaces) delete namespaces[k];
+ for (var k in $.entwine.capture_bindings) delete $.entwine.capture_bindings[k];
},
WARN_LEVEL_NONE: 0,
@@ -997,7 +987,7 @@ catch (e) {
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);
@@ -1182,7 +1172,144 @@ catch (e) {
;
-/* src/jquery.entwine.dommaybechanged.js */
+/* src/domevents/jquery.entwine.domevents.addrem.js */
+
+(function($){
+
+ // Gets all the child elements of a particular elements, stores it in an array
+ function getElements(store, original) {
+ var node, i = store.length, next = original.firstChild;
+
+ while ((node = next)) {
+ if (node.nodeType === 1) store[i++] = node;
+ next = node.firstChild || node.nextSibling;
+ while (!next && (node = node.parentNode) && node !== original) next = node.nextSibling;
+ }
+ }
+
+ // This might be faster? Or slower? @todo: benchmark.
+ function getElementsAlt(store, node) {
+ if (node.getElementsByTagName) {
+ var els = node.getElementsByTagName('*'), len = els.length, i = 0, j = store.length;
+ for(; i < len; i++, j++) {
+ store[j] = els[i];
+ }
+ }
+ else if (node.childNodes) {
+ var els = node.childNodes, len = els.length, i = 0;
+ for(; i < len; i++) {
+ getElements(store, els[i]);
+ }
+ }
+ }
+
+ var dontTrigger = false;
+
+ // Monkey patch $.fn.domManip to catch all regular jQuery add element calls
+ var _domManip = $.prototype.domManip;
+ $.prototype.domManip = function(args, table, callback) {
+ if (!callback.patched) {
+ var original = callback;
+ arguments[2] = function(elem){
+ var added = [];
+
+ if (!dontTrigger) {
+ if (elem.nodeType == 1) added[added.length] = elem;
+ getElements(added, elem);
+ }
+
+ var rv = original.apply(this, arguments);
+
+ if (!dontTrigger && added.length) {
+ var event = $.Event('EntwineElementsAdded');
+ event.targets = added;
+ $(document).triggerHandler(event);
+ }
+
+ return rv;
+ }
+ arguments[2].patched = true;
+ }
+
+ return _domManip.apply(this, arguments);
+ }
+
+ // Monkey patch $.fn.html to catch when jQuery sets innerHTML directly
+ var _html = $.prototype.html;
+ $.prototype.html = function(value) {
+ if (value === undefined) return _html.apply(this, arguments);
+
+ dontTrigger = true;
+ var res = _html.apply(this, arguments);
+ dontTrigger = false;
+
+ var added = [];
+
+ var i = 0, length = this.length;
+ for (; i < length; i++ ) getElements(added, this[i]);
+
+ var event = $.Event('EntwineElementsAdded');
+ event.targets = added;
+ $(document).triggerHandler(event);
+
+ return res;
+ }
+
+ // If this is true, we've changed something to call cleanData so that we can catch the elements, but we don't
+ // want to call the underlying original $.cleanData
+ var supressActualClean = false;
+
+ // Monkey patch $.cleanData to catch element removal
+ var _cleanData = $.cleanData;
+ $.cleanData = function( elems ) {
+ // By default we can assume all elements passed are legitimately being removeed
+ var removed = elems;
+
+ // Except if we're supressing actual clean - we might be being called by jQuery "being careful" about detaching nodes
+ // before attaching them. So we need to check to make sure these nodes currently are in a document
+ if (supressActualClean) {
+ var i = 0, len = elems.length, removed = [], ri = 0;
+ for(; i < len; i++) {
+ var node = elems[i], current = node;
+ while (current = current.parentNode) {
+ if (current.nodeType == 9) { removed[ri++] = node; break; }
+ }
+ }
+ }
+
+ if (removed.length) {
+ var event = $.Event('EntwineElementsRemoved');
+ event.targets = removed;
+ $(document).triggerHandler(event);
+ }
+
+ if (!supressActualClean) _cleanData.apply(this, arguments);
+ }
+
+ // Monkey patch $.fn.remove to catch when we're just detaching (keepdata == 1) -
+ // this doesn't call cleanData but still needs to trigger event
+ var _remove = $.prototype.remove;
+ $.prototype.remove = function(selector, keepdata) {
+ supressActualClean = keepdata;
+ var rv = _remove.call(this, selector);
+ supressActualClean = false;
+ return rv;
+ }
+
+ // And on DOM ready, trigger adding once
+ $(function(){
+ var added = []; getElements(added, document);
+
+ var event = $.Event('EntwineElementsAdded');
+ event.targets = added;
+ $(document).triggerHandler(event);
+ });
+
+
+})(jQuery);;
+
+
+/* src/domevents/jquery.entwine.domevents.maybechanged.js */
(function($){
@@ -1224,15 +1351,11 @@ catch (e) {
// 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);
+ $(document).triggerHandler("EntwineSubtreeMaybeChanged", [this]);
},
changed: function() {
@@ -1282,14 +1405,22 @@ catch (e) {
var changes = new ChangeDetails();
+ // Element add events trigger maybechanged events
+
+ $(document).bind('EntwineElementsAdded', function(e){ changes.addSubtree(e.targets); });
- monkey('append', 'prepend', 'empty', 'html', function(){
- changes.addSubtree(this);
+ // Element remove events trigger maybechanged events, but we have to wait until after the nodes are actually removed
+ // (EntwineElementsRemoved fires _just before_ the elements are removed so the data still exists), especially in syncronous mode
+
+ var removed = null;
+ $(document).bind('EntwineElementsRemoved', function(e){ removed = e.targets; });
+
+ monkey('remove', 'html', 'empty', function(){
+ var subtree = removed; removed = null;
+ if (subtree) changes.addSubtree(subtree);
});
- monkey('after', 'before', 'remove', 'detach', function(){
- changes.addSubtree(this.parent());
- })
+ // We also need to know when an attribute, class, etc changes. Patch the relevant jQuery methods here
monkey('removeAttr', function(attr){
changes.addAttr(attr, this);
@@ -1304,20 +1435,7 @@ catch (e) {
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
- */
+ // Add some usefull accessors to $.entwine
$.extend($.entwine, {
/**
@@ -1337,15 +1455,10 @@ catch (e) {
* Called automatically on document.ready
*/
triggerMatching: function() {
- changes.addAll(); //.triggerEvent();
+ changes.addAll();
}
});
- // And on DOM ready, trigger matching once
- $(function(){
- $.entwine.triggerMatching();
- });
-
})(jQuery);;
@@ -1586,24 +1699,138 @@ catch (e) {
// 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 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;
+ $(document).bind('EntwineElementsAdded', function(e){
+ var forms = $(e.targets).filter('form');
+ if (!forms.length) return;
+
+ forms.bind('submit.entwine_delegate_submit', delegate_submit);
});
})(jQuery);
;
+/* src/jquery.entwine.eventcapture.js */
+
+(function($) {
+
+ $.entwine.Namespace.addMethods({
+ bind_capture: function(selector, event, name, capture) {
+ var store = this.captures || (this.captures = {});
+ var rulelists = store[event] || (store[event] = {});
+ var rulelist = rulelists[name] || (rulelists[name] = $.entwine.RuleList());
+
+ rule = rulelist.addRule(selector, event);
+ rule.handler = name;
+
+ this.bind_proxy(selector, name, capture);
+ }
+ });
+
+ var bindings = $.entwine.capture_bindings = {};
+
+ var event_proxy = function(event) {
+ return function(e) {
+ var namespace, capturelists, forevent, capturelist, rule, handler, sel;
+
+ for (var k in $.entwine.namespaces) {
+ namespace = $.entwine.namespaces[k];
+ capturelists = namespace.captures;
+
+ if (capturelists && (forevent = capturelists[event])) {
+ for (var k in forevent) {
+ var capturelist = forevent[k];
+ var triggered = namespace.$([]);
+
+ // Stepping through each selector from most to least specific
+ var j = capturelist.length;
+ while (j--) {
+ rule = capturelist[j];
+ handler = rule.handler;
+ sel = rule.selector.selector;
+
+ var matching = namespace.$(sel).not(triggered);
+ matching[handler].apply(matching, arguments);
+
+ triggered = triggered.add(matching);
+ }
+ }
+ }
+ }
+ }
+ };
+
+ var selector_proxy = function(selector, handler, includechildren) {
+ var matcher = $.selector(selector);
+ return function(e){
+ if (matcher.matches(e.target)) return handler.apply(this, arguments);
+ }
+ };
+
+ var window_proxy = function(selector, handler, includechildren) {
+ return function(e){
+ if (e.target === window) return handler.apply(this, arguments);
+ }
+ };
+
+ var property_proxy = function(property, handler, includechildren) {
+ var matcher;
+
+ return function(e){
+ var match = this['get'+property]();
+
+ if (typeof(match) == 'string') {
+ var matcher = (matcher && match == matcher.selector) ? matcher : $.selector(match);
+ if (matcher.matches(e.target)) return handler.apply(this, arguments);
+ }
+ else {
+ if ($.inArray(e.target, match) !== -1) return handler.apply(this, arguments);
+ }
+ }
+ };
+
+ $.entwine.Namespace.addHandler({
+ order: 10,
+
+ bind: function(selector, k, v) {
+ var match;
+ if ($.isPlainObject(v) && (match = k.match(/^from\s*(.*)/))) {
+ var from = match[1];
+ var proxyGen;
+
+ if (from.match(/[^\w]/)) proxyGen = selector_proxy;
+ else if (from == 'Window' || from == 'window') proxyGen = window_proxy;
+ else proxyGen = property_proxy;
+
+ for (var onevent in v) {
+ var handler = v[onevent];
+ match = onevent.match(/^on(.*)/);
+ var event = match[1];
+
+ this.bind_capture(selector, event, k + '_' + event, proxyGen(from, handler));
+
+ if (!bindings[event]) {
+ var namespaced = event.replace(/(\s+|$)/g, '.entwine$1');
+ bindings[event] = event_proxy(event);
+
+ $(proxyGen == window_proxy ? window : document).bind(namespaced, bindings[event]);
+ }
+ }
+
+ return true;
+ }
+ }
+ });
+
+})(jQuery);
+;
+
+
/* src/jquery.entwine.ctors.js */
(function($) {
@@ -1672,10 +1899,7 @@ catch (e) {
* $('#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;
-
+ $(document).bind('EntwineSubtreeMaybeChanged', function(e, changes){
// var start = (new Date).getTime();
// For every namespace
@@ -1736,7 +1960,7 @@ catch (e) {
}
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; }
+ for (var k in 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
@@ -1747,7 +1971,7 @@ catch (e) {
- NOTE: It might be on _both_
*/
- var method = rule.selector.affectedBy(e.changes);
+ var method = rule.selector.affectedBy(changes);
if (method.classes.context) {
full = true;
@@ -1755,7 +1979,7 @@ catch (e) {
else {
for (var k in method.classes.direct) {
calcmatched(j);
- var recheck = e.changes.classes[k].not(matched);
+ var recheck = changes.classes[k].not(matched);
if (res === null) {
res = rule.cache ? rule.cache.not(taken).add(released.filter(sel)) : $([]);
@@ -1854,6 +2078,74 @@ catch (e) {
;
+/* src/jquery.entwine.addrem.js */
+
+(function($) {
+
+ $.entwine.Namespace.addMethods({
+ build_addrem_proxy: function(name) {
+ var one = this.one(name, 'func');
+
+ return function() {
+ if (this.length === 0){
+ return;
+ }
+ else if (this.length) {
+ var rv, i = this.length;
+ while (i--) rv = one(this[i], arguments);
+ return rv;
+ }
+ else {
+ return one(this, arguments);
+ }
+ };
+ },
+
+ bind_addrem_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] = this.build_addrem_proxy(name);
+ this.injectee[name].isentwinemethod = true;
+ }
+ }
+ });
+
+ $.entwine.Namespace.addHandler({
+ order: 30,
+
+ bind: function(selector, k, v) {
+ if ($.isFunction(v) && (k == 'onadd' || k == 'onremove')) {
+ this.bind_addrem_proxy(selector, k, v);
+ return true;
+ }
+ }
+ });
+
+ $(document).bind('EntwineElementsAdded', function(e){
+ // For every namespace
+ for (var k in $.entwine.namespaces) {
+ var namespace = $.entwine.namespaces[k];
+ if (namespace.injectee.onadd) namespace.injectee.onadd.call(e.targets);
+ }
+ });
+
+ $(document).bind('EntwineElementsRemoved', function(e){
+ for (var k in $.entwine.namespaces) {
+ var namespace = $.entwine.namespaces[k];
+ if (namespace.injectee.onremove) namespace.injectee.onremove.call(e.targets);
+ }
+ });
+
+
+
+
+})(jQuery);
+;
+
+
/* src/jquery.entwine.properties.js */
(function($) {
View
430 thirdparty/jquery-entwine/dist/jquery.entwine-dist.js
@@ -353,20 +353,6 @@ Sizzle is good for finding elements for a selector, but not so good for telling
// 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;
@@ -472,7 +458,13 @@ Sizzle is good for finding elements for a selector, but not so good for telling
this.wsattrs[attr] = true;
return join([this.uses_attr(attr), 'var _WS_'+varForAttr(attr)+' = " "+'+varForAttr(attr)+'+" ";']);
},
-
+
+ uses_jqueryFilters: function() {
+ if (this.jqueryFiltersAdded) return;
+ this.jqueryFiltersAdded = true;
+ return 'var _$filters = jQuery.find.selectors.filters;';
+ },
+
save: function(lbl) {
return 'var el'+lbl+' = el;';
},
@@ -584,12 +576,8 @@ Sizzle is good for finding elements for a selector, but not so good for telling
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] = el.uses_jqueryFilters();
+ js[js.length] = 'if (!_$filters.'+pscls[0]+'(el)) BAD;';
}
});
@@ -845,8 +833,10 @@ catch (e) {
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');
+ $(window).unbind('.entwine');
// Remove namespaces, and start over again
- namespaces = $.entwine.namespaces = {};
+ for (var k in namespaces) delete namespaces[k];
+ for (var k in $.entwine.capture_bindings) delete $.entwine.capture_bindings[k];
},
WARN_LEVEL_NONE: 0,
@@ -997,7 +987,7 @@ catch (e) {
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);
@@ -1182,7 +1172,144 @@ catch (e) {
;
-/* src/jquery.entwine.dommaybechanged.js */
+/* src/domevents/jquery.entwine.domevents.addrem.js */
+
+(function($){
+
+ // Gets all the child elements of a particular elements, stores it in an array
+ function getElements(store, original) {
+ var node, i = store.length, next = original.firstChild;
+
+ while ((node = next)) {
+ if (node.nodeType === 1) store[i++] = node;
+ next = node.firstChild || node.nextSibling;
+ while (!next && (node = node.parentNode) && node !== original) next = node.nextSibling;
+ }
+ }
+
+ // This might be faster? Or slower? @todo: benchmark.
+ function getElementsAlt(store, node) {
+ if (node.getElementsByTagName) {
+ var els = node.getElementsByTagName('*'), len = els.length, i = 0, j = store.length;
+ for(; i < len; i++, j++) {
+ store[j] = els[i];
+ }
+ }
+ else if (node.childNodes) {
+ var els = node.childNodes, len = els.length, i = 0;
+ for(; i < len; i++) {
+ getElements(store, els[i]);
+ }
+ }
+ }
+
+ var dontTrigger = false;
+
+ // Monkey patch $.fn.domManip to catch all regular jQuery add element calls
+ var _domManip = $.prototype.domManip;
+ $.prototype.domManip = function(args, table, callback) {
+ if (!callback.patched) {
+ var original = callback;
+ arguments[2] = function(elem){
+ var added = [];
+
+ if (!dontTrigger) {
+ if (elem.nodeType == 1) added[added.length] = elem;
+ getElements(added, elem);
+ }
+
+ var rv = original.apply(this, arguments);
+
+ if (!dontTrigger && added.length) {
+ var event = $.Event('EntwineElementsAdded');
+ event.targets = added;
+ $(document).triggerHandler(event);
+ }
+
+ return rv;
+ }
+ arguments[2].patched = true;
+ }
+
+ return _domManip.apply(this, arguments);
+ }
+
+ // Monkey patch $.fn.html to catch when jQuery sets innerHTML directly
+ var _html = $.prototype.html;
+ $.prototype.html = function(value) {
+ if (value === undefined) return _html.apply(this, arguments);
+
+ dontTrigger = true;
+ var res = _html.apply(this, arguments);
+ dontTrigger = false;
+
+ var added = [];
+
+ var i = 0, length = this.length;
+ for (; i < length; i++ ) getElements(added, this[i]);
+
+ var event = $.Event('EntwineElementsAdded');
+ event.targets = added;
+ $(document).triggerHandler(event);
+
+ return res;
+ }
+
+ // If this is true, we've changed something to call cleanData so that we can catch the elements, but we don't
+ // want to call the underlying original $.cleanData
+ var supressActualClean = false;
+
+ // Monkey patch $.cleanData to catch element removal
+ var _cleanData = $.cleanData;
+ $.cleanData = function( elems ) {
+ // By default we can assume all elements passed are legitimately being removeed
+ var removed = elems;
+
+ // Except if we're supressing actual clean - we might be being called by jQuery "being careful" about detaching nodes
+ // before attaching them. So we need to check to make sure these nodes currently are in a document
+ if (supressActualClean) {
+ var i = 0, len = elems.length, removed = [], ri = 0;
+ for(; i < len; i++) {
+ var node = elems[i], current = node;
+ while (current = current.parentNode) {
+ if (current.nodeType == 9) { removed[ri++] = node; break; }
+ }
+ }
+ }
+
+ if (removed.length) {
+ var event = $.Event('EntwineElementsRemoved');
+ event.targets = removed;
+ $(document).triggerHandler(event);
+ }
+
+ if (!supressActualClean) _cleanData.apply(this, arguments);
+ }
+
+ // Monkey patch $.fn.remove to catch when we're just detaching (keepdata == 1) -
+ // this doesn't call cleanData but still needs to trigger event
+ var _remove = $.prototype.remove;
+ $.prototype.remove = function(selector, keepdata) {
+ supressActualClean = keepdata;
+ var rv = _remove.call(this, selector);
+ supressActualClean = false;
+ return rv;
+ }
+
+ // And on DOM ready, trigger adding once
+ $(function(){
+ var added = []; getElements(added, document);
+
+ var event = $.Event('EntwineElementsAdded');
+ event.targets = added;
+ $(document).triggerHandler(event);
+ });
+
+
+})(jQuery);;
+
+
+/* src/domevents/jquery.entwine.domevents.maybechanged.js */
(function($){
@@ -1224,15 +1351,11 @@ catch (e) {
// 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);
+ $(document).triggerHandler("EntwineSubtreeMaybeChanged", [this]);
},
changed: function() {
@@ -1282,14 +1405,22 @@ catch (e) {
var changes = new ChangeDetails();
+ // Element add events trigger maybechanged events
+
+ $(document).bind('EntwineElementsAdded', function(e){ changes.addSubtree(e.targets); });
- monkey('append', 'prepend', 'empty', 'html', function(){
- changes.addSubtree(this);
+ // Element remove events trigger maybechanged events, but we have to wait until after the nodes are actually removed
+ // (EntwineElementsRemoved fires _just before_ the elements are removed so the data still exists), especially in syncronous mode
+
+ var removed = null;
+ $(document).bind('EntwineElementsRemoved', function(e){ removed = e.targets; });
+
+ monkey('remove', 'html', 'empty', function(){
+ var subtree = removed; removed = null;
+ if (subtree) changes.addSubtree(subtree);
});
- monkey('after', 'before', 'remove', 'detach', function(){
- changes.addSubtree(this.parent());
- })
+ // We also need to know when an attribute, class, etc changes. Patch the relevant jQuery methods here
monkey('removeAttr', function(attr){
changes.addAttr(attr, this);
@@ -1304,20 +1435,7 @@ catch (e) {
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
- */
+ // Add some usefull accessors to $.entwine
$.extend($.entwine, {
/**
@@ -1337,15 +1455,10 @@ catch (e) {
* Called automatically on document.ready
*/
triggerMatching: function() {
- changes.addAll(); //.triggerEvent();
+ changes.addAll();
}
});
- // And on DOM ready, trigger matching once
- $(function(){
- $.entwine.triggerMatching();
- });
-
})(jQuery);;
@@ -1586,24 +1699,138 @@ catch (e) {
// 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 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;
+ $(document).bind('EntwineElementsAdded', function(e){
+ var forms = $(e.targets).filter('form');
+ if (!forms.length) return;
+
+ forms.bind('submit.entwine_delegate_submit', delegate_submit);
});
})(jQuery);
;
+/* src/jquery.entwine.eventcapture.js */
+
+(function($) {
+
+ $.entwine.Namespace.addMethods({
+ bind_capture: function(selector, event, name, capture) {
+ var store = this.captures || (this.captures = {});
+ var rulelists = store[event] || (store[event] = {});
+ var rulelist = rulelists[name] || (rulelists[name] = $.entwine.RuleList());
+
+ rule = rulelist.addRule(selector, event);
+ rule.handler = name;
+
+ this.bind_proxy(selector, name, capture);
+ }
+ });
+
+ var bindings = $.entwine.capture_bindings = {};
+
+ var event_proxy = function(event) {
+ return function(e) {
+ var namespace, capturelists, forevent, capturelist, rule, handler, sel;
+
+ for (var k in $.entwine.namespaces) {
+ namespace = $.entwine.namespaces[k];
+ capturelists = namespace.captures;
+
+ if (capturelists && (forevent = capturelists[event])) {
+ for (var k in forevent) {
+ var capturelist = forevent[k];
+ var triggered = namespace.$([]);
+
+ // Stepping through each selector from most to least specific
+ var j = capturelist.length;
+ while (j--) {
+ rule = capturelist[j];
+ handler = rule.handler;
+ sel = rule.selector.selector;
+
+ var matching = namespace.$(sel).not(triggered);
+ matching[handler].apply(matching, arguments);
+
+ triggered = triggered.add(matching);
+ }
+ }
+ }
+ }
+ }
+ };
+
+ var selector_proxy = function(selector, handler, includechildren) {
+ var matcher = $.selector(selector);
+ return function(e){
+ if (matcher.matches(e.target)) return handler.apply(this, arguments);
+ }
+ };
+
+ var window_proxy = function(selector, handler, includechildren) {
+ return function(e){
+ if (e.target === window) return handler.apply(this, arguments);
+ }
+ };
+
+ var property_proxy = function(property, handler, includechildren) {
+ var matcher;
+
+ return function(e){
+ var match = this['get'+property]();
+
+ if (typeof(match) == 'string') {
+ var matcher = (matcher && match == matcher.selector) ? matcher : $.selector(match);
+ if (matcher.matches(e.target)) return handler.apply(this, arguments);
+ }
+ else {
+ if ($.inArray(e.target, match) !== -1) return handler.apply(this, arguments);
+ }
+ }
+ };
+
+ $.entwine.Namespace.addHandler({
+ order: 10,
+
+ bind: function(selector, k, v) {
+ var match;
+ if ($.isPlainObject(v) && (match = k.match(/^from\s*(.*)/))) {
+ var from = match[1];
+ var proxyGen;
+
+ if (from.match(/[^\w]/)) proxyGen = selector_proxy;
+ else if (from == 'Window' || from == 'window') proxyGen = window_proxy;
+ else proxyGen = property_proxy;
+
+ for (var onevent in v) {
+ var handler = v[onevent];
+ match = onevent.match(/^on(.*)/);
+ var event = match[1];
+
+ this.bind_capture(selector, event, k + '_' + event, proxyGen(from, handler));
+
+ if (!bindings[event]) {
+ var namespaced = event.replace(/(\s+|$)/g, '.entwine$1');
+ bindings[event] = event_proxy(event);
+
+ $(proxyGen == window_proxy ? window : document).bind(namespaced, bindings[event]);
+ }
+ }
+
+ return true;
+ }
+ }
+ });
+
+})(jQuery);
+;
+
+
/* src/jquery.entwine.ctors.js */
(function($) {
@@ -1672,10 +1899,7 @@ catch (e) {
* $('#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;
-
+ $(document).bind('EntwineSubtreeMaybeChanged', function(e, changes){
// var start = (new Date).getTime();
// For every namespace
@@ -1736,7 +1960,7 @@ catch (e) {
}
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; }
+ for (var k in 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
@@ -1747,7 +1971,7 @@ catch (e) {
- NOTE: It might be on _both_
*/
- var method = rule.selector.affectedBy(e.changes);
+ var method = rule.selector.affectedBy(changes);
if (method.classes.context) {
full = true;
@@ -1755,7 +1979,7 @@ catch (e) {
else {
for (var k in method.classes.direct) {
calcmatched(j);
- var recheck = e.changes.classes[k].not(matched);
+ var recheck = changes.classes[k].not(matched);
if (res === null) {
res = rule.cache ? rule.cache.not(taken).add(released.filter(sel)) : $([]);
@@ -1854,6 +2078,74 @@ catch (e) {
;
+/* src/jquery.entwine.addrem.js */
+
+(function($) {
+
+ $.entwine.Namespace.addMethods({
+ build_addrem_proxy: function(name) {
+ var one = this.one(name, 'func');
+
+ return function() {
+ if (this.length === 0){
+ return;
+ }
+ else if (this.length) {
+ var rv, i = this.length;
+ while (i--) rv = one(this[i], arguments);
+ return rv;
+ }
+ else {
+ return one(this, arguments);
+ }
+ };
+ },
+
+ bind_addrem_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] = this.build_addrem_proxy(name);
+ this.injectee[name].isentwinemethod = true;
+ }
+ }
+ });
+
+ $.entwine.Namespace.addHandler({
+ order: 30,
+
+ bind: function(selector, k, v) {
+ if ($.isFunction(v) && (k == 'onadd' || k == 'onremove')) {
+ this.bind_addrem_proxy(selector, k, v);
+ return true;
+ }
+ }
+ });
+
+ $(document).bind('EntwineElementsAdded', function(e){
+ // For every namespace
+ for (var k in $.entwine.namespaces) {
+ var namespace = $.entwine.namespaces[k];
+ if (namespace.injectee.onadd) namespace.injectee.onadd.call(e.targets);
+ }
+ });
+
+ $(document).bind('EntwineElementsRemoved', function(e){
+ for (var k in $.entwine.namespaces) {
+ var namespace = $.entwine.namespaces[k];
+ if (namespace.injectee.onremove) namespace.injectee.onremove.call(e.targets);
+ }
+ });
+
+
+
+
+})(jQuery);
+;
+
+
/* src/jquery.entwine.properties.js */
(function($) {
View
17 thirdparty/jquery-entwine/spec/SpecRunner.html
@@ -3,16 +3,16 @@
<html>
<head>
<title>Jasmine Test Runner</title>
- <link rel="stylesheet" type="text/css" href="../vendor/jasmine-1.0.1/jasmine.css">
+ <link rel="stylesheet" type="text/css" href="../vendor/jasmine/jasmine.css">
- <script type="text/javascript" src="../vendor/jasmine-1.0.1/jasmine.js"></script>
- <script type="text/javascript" src="../vendor/jasmine-1.0.1/jasmine-html.js"></script>
+ <script type="text/javascript" src="../vendor/jasmine/jasmine.js"></script>
+ <script type="text/javascript" src="../vendor/jasmine/jasmine-html.js"></script>
<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>
<script type="text/javascript">
versionarg = window.location.search.match(/version=([^&]+)/);
- jQueryVersion = versionarg ? versionarg[1] : '1.6';
+ jQueryVersion = versionarg ? versionarg[1] : '1.7.2';
jQuerySource = 'local';
var jQuery;
@@ -34,14 +34,21 @@
<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/domevents/jquery.entwine.domevents.addrem.js"></script>
+ <script src="../src/domevents/jquery.entwine.domevents.maybechanged.js"></script>
+
<script src="../src/jquery.entwine.events.js"></script>
+ <script src="../src/jquery.entwine.eventcapture.js"></script>
<script src="../src/jquery.entwine.ctors.js"></script>
+ <script src="../src/jquery.entwine.addrem.js"></script>
<script src="../src/jquery.entwine.properties.js"></script>
<!-- include spec files here... -->
<script type="text/javascript" src="spec.entwine.basics.js"></script>
+ <script type="text/javascript" src="spec.entwine.eventcapture.js"></script>
<script type="text/javascript" src="spec.entwine.ctors.js"></script>
+ <script type="text/javascript" src="spec.entwine.addrem.js"></script>
<script type="text/javascript" src="spec.entwine.events.js"></script>
<script type="text/javascript" src="spec.entwine.namespaces.js"></script>
<script type="text/javascript" src="spec.entwine.properties.js"></script>
View
115 thirdparty/jquery-entwine/spec/spec.entwine.addrem.js
@@ -0,0 +1,115 @@
+describe('Entwine', function(){
+
+ beforeEach(function(){
+ $('body').append('<div id="dom_test"></div>');
+ });
+
+ afterEach(function(){
+ $('#dom_test').remove();
+ });
+
+ describe('onadd and onremove', function(){
+
+ beforeEach(function(){
+ $.entwine.synchronous_mode();
+ $.entwine.clear_all_rules();
+ $('#dom_test').html('<div id="a" class="a b c"></div>');
+ });
+
+ describe('onremove', function(){
+ it('calls onremove on a removed element', function(){
+ var called = false;
+
+ $('#a').entwine({
+ onremove: function(){called = true;}
+ });
+
+ $('#a').remove();
+ expect(called).toBeTruthy();
+ });
+
+ it('calls onremove only on elements that match selectors, and does so when multiple elements are removed', function(){
+ var removed = [];
+
+ $('#a').html('<div id="b" class="catchremove"></div><div id="c"></div><div id="d" class="catchremove"></div>');
+
+ $('.catchremove').entwine({
+ onremove: function(){removed.push(this.attr('id'));}
+ });
+
+ $('#a').remove();
+ expect(removed).toEqual(['d', 'b']);
+ });
+
+ it('allows access to data in an onremove handler', function(){
+ var val = 0;
+
+ $('#a').data('Bam', 1);
+
+ $('#a').entwine({
+ onremove: function(){val = this.data('Bam');}
+ });
+
+ $('#a').remove();
+ expect(val).toEqual(1);
+ });
+ });
+
+ describe('onadd', function(){
+ it('calls onadd on an appended', function(){
+ var called = false;
+
+ $('#b').entwine({
+ onadd: function(){called = true;}
+ });
+
+ $('#a').append('<div id="b"></div>');
+ expect(called).toBeTruthy();
+ });
+
+ it('calls onadd on an child thats nested when appended', function(){
+ var called = false;
+
+ $('#b').entwine({
+ onadd: function(){called = true;}
+ });
+
+ $('#a').append('<div><div id="b"></div></div>');
+ expect(called).toBeTruthy();
+ });
+
+ it('calls onadd on an item added via html', function(){
+ var called = false;
+
+ $('#b').entwine({
+ onadd: function(){called = true;}
+ });
+
+ $('#a').html('<div></div><div><div id="b"></div></div>');
+ expect(called).toBeTruthy();
+ });
+
+ it('calls onadd and onremove correctly via replaceWith', function(){
+ var added = [], removed = [], sequence = [];
+
+ $('#a,#b').entwine({
+ onadd: function(){
+ console.debug('added', this);
+ added.push(this.attr('id'));
+ sequence.push(this.attr('id'));
+ },
+ onremove: function(){
+ console.debug('added', this);
+ removed.push(this.attr('id'));
+ sequence.push(this.attr('id'));
+ }
+ });
+
+ $('#a').replaceWith('<div></div><div><div id="b"></div></div>');
+ expect(added).toEqual(['b']);
+ expect(removed).toEqual(['a']);
+ expect(sequence).toEqual(['a', 'b']);
+ });
+ });
+ });
+});
View
8 thirdparty/jquery-entwine/spec/spec.entwine.basics.js
@@ -32,6 +32,14 @@ describe('Entwine', function(){
expect($('.a').foo()).toEqual('a');
});
+ it('can attach and call a base function on a selector using a psuedo-selector taken from jquery', function(){
+ $('#a:visible').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');},
View
141 thirdparty/jquery-entwine/spec/spec.entwine.eventcapture.js
@@ -0,0 +1,141 @@
+describe('Entwine', function(){
+
+ beforeEach(function(){
+ $('body').append('<div id="dom_test"></div>');
+ });
+
+ afterEach(function(){
+ $('#dom_test').remove();
+ });
+
+ describe('Event Capture', function(){
+
+ beforeEach(function(){
+ $.entwine.synchronous_mode();
+ $.entwine.clear_all_rules();
+ $('#dom_test').html('<div id="a" class="a b c"><div id="d" class="d e f"></div></div>');
+ });
+
+ it('can capture by direct selector', function(){
+ var triggers = [];
+
+ $('#d').entwine({
+ 'from#a': {
+ onsynthetic: function(){ triggers.push(this.attr('id')); }
+ }
+ });
+
+ $('#a').trigger('synthetic');
+ expect(triggers).toEqual(['d'])
+
+ $('#a').trigger('synthetic');
+ expect(triggers).toEqual(['d', 'd']);
+
+ $('#dom_test').trigger('synthetic');
+ expect(triggers).toEqual(['d', 'd']);
+ });
+
+ it('can capture by indirect selector', function(){
+ var triggercount = 0;
+
+ $('#d').entwine({
+ A: '#a',
+ fromA: {
+ onsynthetic: function(){ triggercount += 1;}
+ }
+ });
+
+ $('#a').trigger('synthetic');
+ expect(triggercount).toEqual(1);
+
+ $('#a').trigger('synthetic');
+ expect(triggercount).toEqual(2);
+
+ $('#dom_test').trigger('synthetic');
+ expect(triggercount).toEqual(2);
+ });
+
+ it('can capture by returning element set', function(){
+ var triggercount = 0;
+
+ $('#d').entwine({
+ getA: function(){ return this.parent(); },
+ fromA: {
+ onsynthetic: function(){ triggercount += 1;}
+ }
+ });
+
+ $('#a').trigger('synthetic');
+ expect(triggercount).toEqual(1);
+
+ $('#a').trigger('synthetic');
+ expect(triggercount).toEqual(2);
+
+ $('#dom_test').trigger('synthetic');
+ expect(triggercount).toEqual(2);
+ });
+
+ it('can capture by selector, more specific rules override correct', function(){
+ var triggerlist = [];
+
+ $('.d').entwine({
+ 'from#a': {
+ onsynthetic: function(){ triggerlist.push(1); }
+ }
+ });
+ $('#d').entwine({
+ 'from#a': {
+ onsynthetic: function(){ triggerlist.push(2); }
+ }
+ });
+
+ $('#a').trigger('synthetic');
+ expect(triggerlist).toEqual([2]);
+ });
+
+ it('gets events and data passed through correctly', function(){
+ var a, b;
+
+ $('#d').entwine({
+ 'from#a': {
+ onsynthetic: function(e, data){ a = e.passthrough; b = data; }
+ }
+ });
+
+ var e = $.Event('synthetic'); e.passthrough = 'foo';
+ $('#a').trigger(e, ['bam']);
+
+ expect(a).toEqual('foo');
+ expect(b).toEqual('bam');
+ });
+
+ it('can handle different from endings', function(){
+ var triggercount = 0;
+
+ $('#d').entwine({
+ 'from#a': {
+ onsynthetic: function(){ triggercount += 1;}
+ },
+ 'from.b': {
+ onsynthetic: function(){ triggercount += 1;}
+ }
+ });
+
+ $('#a').trigger('synthetic');
+ expect(triggercount).toEqual(2);
+ });
+
+ it('can capture from window', function(){
+ var triggercount = 0;
+
+ $('#d').entwine({
+ fromWindow: {
+ onsynthetic: function(){ triggercount += 1;}
+ }
+ });
+
+ $(window).trigger('synthetic');
+ expect(triggercount).toEqual(1);
+ });
+ });
+});
View
133 thirdparty/jquery-entwine/src/domevents/jquery.entwine.domevents.addrem.js
@@ -0,0 +1,133 @@
+(function($){
+
+ // Gets all the child elements of a particular elements, stores it in an array
+ function getElements(store, original) {
+ var node, i = store.length, next = original.firstChild;
+
+ while ((node = next)) {
+ if (node.nodeType === 1) store[i++] = node;
+ next = node.firstChild || node.nextSibling;
+ while (!next && (node = node.parentNode) && node !== original) next = node.nextSibling;
+ }
+ }
+
+ // This might be faster? Or slower? @todo: benchmark.
+ function getElementsAlt(store, node) {
+ if (node.getElementsByTagName) {
+ var els = node.getElementsByTagName('*'), len = els.length, i = 0, j = store.length;
+ for(; i < len; i++, j++) {
+ store[j] = els[i];
+ }
+ }
+ else if (node.childNodes) {
+ var els = node.childNodes, len = els.length, i = 0;
+ for(; i < len; i++) {
+ getElements(store, els[i]);
+ }
+ }
+ }
+
+ var dontTrigger = false;
+
+ // Monkey patch $.fn.domManip to catch all regular jQuery add element calls
+ var _domManip = $.prototype.domManip;
+ $.prototype.domManip = function(args, table, callback) {
+ if (!callback.patched) {
+ var original = callback;
+ arguments[2] = function(elem){
+ var added = [];
+
+ if (!dontTrigger) {
+ if (elem.nodeType == 1) added[added.length] = elem;
+ getElements(added, elem);
+ }
+
+ var rv = original.apply(this, arguments);
+
+ if (!dontTrigger && added.length) {
+ var event = $.Event('EntwineElementsAdded');
+ event.targets = added;
+ $(document).triggerHandler(event);
+ }
+
+ return rv;
+ }
+ arguments[2].patched = true;
+ }
+
+ return _domManip.apply(this, arguments);
+ }
+
+ // Monkey patch $.fn.html to catch when jQuery sets innerHTML directly
+ var _html = $.prototype.html;
+ $.prototype.html = function(value) {
+ if (value === undefined) return _html.apply(this, arguments);
+
+ dontTrigger = true;
+ var res = _html.apply(this, arguments);
+ dontTrigger = false;
+
+ var added = [];
+
+ var i = 0, length = this.length;
+ for (; i < length; i++ ) getElements(added, this[i]);
+
+ var event = $.Event('EntwineElementsAdded');
+ event.targets = added;
+ $(document).triggerHandler(event);
+
+ return res;
+ }
+
+ // If this is true, we've changed something to call cleanData so that we can catch the elements, but we don't
+ // want to call the underlying original $.cleanData
+ var supressActualClean = false;
+
+ // Monkey patch $.cleanData to catch element removal
+ var _cleanData = $.cleanData;
+ $.cleanData = function( elems ) {
+ // By default we can assume all elements passed are legitimately being removeed
+ var removed = elems;
+
+ // Except if we're supressing actual clean - we might be being called by jQuery "being careful" about detaching nodes
+ // before attaching them. So we need to check to make sure these nodes currently are in a document
+ if (supressActualClean) {
+ var i = 0, len = elems.length, removed = [], ri = 0;
+ for(; i < len; i++) {
+ var node = elems[i], current = node;
+ while (current = current.parentNode) {
+ if (current.nodeType == 9) { removed[ri++] = node; break; }
+ }
+ }
+ }
+
+ if (removed.length) {
+ var event = $.Event('EntwineElementsRemoved');
+ event.targets = removed;
+ $(document).triggerHandler(event);
+ }
+
+ if (!supressActualClean) _cleanData.apply(this, arguments);
+ }
+
+ // Monkey patch $.fn.remove to catch when we're just detaching (keepdata == 1) -
+ // this doesn't call cleanData but still needs to trigger event
+ var _remove = $.prototype.remove;
+ $.prototype.remove = function(selector, keepdata) {
+ supressActualClean = keepdata;
+ var rv = _remove.call(this, selector);
+ supressActualClean = false;
+ return rv;
+ }
+
+ // And on DOM ready, trigger adding once
+ $(function(){
+ var added = []; getElements(added, document);
+
+ var event = $.Event('EntwineElementsAdded');
+ event.targets = added;
+ $(document).triggerHandler(event);
+ });
+
+
+})(jQuery);
View
46 ...ery-entwine/src/jquery.entwine.dommaybechanged.js → ...omevents/jquery.entwine.domevents.maybechanged.js
@@ -38,15 +38,11 @@
// 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);
+ $(document).triggerHandler("EntwineSubtreeMaybeChanged", [this]);
},
changed: function() {
@@ -96,14 +92,22 @@
var changes = new ChangeDetails();
+ // Element add events trigger maybechanged events
+
+ $(document).bind('EntwineElementsAdded', function(e){ changes.addSubtree(e.targets); });
+
+ // Element remove events trigger maybechanged events, but we have to wait until after the nodes are actually removed
+ // (EntwineElementsRemoved fires _just before_ the elements are removed so the data still exists), especially in syncronous mode
- monkey('append', 'prepend', 'empty', 'html', function(){
- changes.addSubtree(this);
+ var removed = null;
+ $(document).bind('EntwineElementsRemoved', function(e){ removed = e.targets; });
+
+ monkey('remove', 'html', 'empty', function(){
+ var subtree = removed; removed = null;
+ if (subtree) changes.addSubtree(subtree);
});
- monkey('after', 'before', 'remove', 'detach', function(){
- changes.addSubtree(this.parent());
- })
+ // We also need to know when an attribute, class, etc changes. Patch the relevant jQuery methods here
monkey('removeAttr', function(attr){
changes.addAttr(attr, this);
@@ -118,20 +122,7 @@
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
- */
+ // Add some usefull accessors to $.entwine
$.extend($.entwine, {
/**
@@ -151,13 +142,8 @@
* Called automatically on document.ready
*/
triggerMatching: function() {
- changes.addAll(); //.triggerEvent();
+ changes.addAll();
}
});
- // And on DOM ready, trigger matching once
- $(function(){
- $.entwine.triggerMatching();
- });
-
})(jQuery);
View
63 thirdparty/jquery-entwine/src/jquery.entwine.addrem.js
@@ -0,0 +1,63 @@
+(function($) {
+
+ $.entwine.Namespace.addMethods({
+ build_addrem_proxy: function(name) {
+ var one = this.one(name, 'func');
+
+ return function() {
+ if (this.length === 0){
+ return;
+ }
+ else if (this.length) {
+ var rv, i = this.length;
+ while (i--) rv = one(this[i], arguments);
+ return rv;
+ }
+ else {
+ return one(this, arguments);
+ }
+ };
+ },
+
+ bind_addrem_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] = this.build_addrem_proxy(name);
+ this.injectee[name].isentwinemethod = true;
+ }
+ }
+ });
+
+ $.entwine.Namespace.addHandler({
+ order: 30,
+
+ bind: function(selector, k, v) {
+ if ($.isFunction(v) && (k == 'onadd' || k == 'onremove')) {
+ this.bind_addrem_proxy(selector, k, v);
+ return true;
+ }
+ }
+ });
+
+ $(document).bind('EntwineElementsAdded', function(e){
+ // For every namespace
+ for (var k in $.entwine.namespaces) {
+ var namespace = $.entwine.namespaces[k];
+ if (namespace.injectee.onadd) namespace.injectee.onadd.call(e.targets);
+ }
+ });
+
+ $(document).bind('EntwineElementsRemoved', function(e){
+ for (var k in $.entwine.namespaces) {
+ var namespace = $.entwine.namespaces[k];
+ if (namespace.injectee.onremove) namespace.injectee.onremove.call(e.targets);
+ }
+ });
+
+
+
+
+})(jQuery);
View
11 thirdparty/jquery-entwine/src/jquery.entwine.ctors.js
@@ -64,10 +64,7 @@
* $('#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;
-
+ $(document).bind('EntwineSubtreeMaybeChanged', function(e, changes){
// var start = (new Date).getTime();
// For every namespace
@@ -128,7 +125,7 @@
}
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; }
+ for (var k in 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
@@ -139,7 +136,7 @@
- NOTE: It might be on _both_
*/
- var method = rule.selector.affectedBy(e.changes);
+ var method = rule.selector.affectedBy(changes);
if (method.classes.context) {
full = true;
@@ -147,7 +144,7 @@
else {
for (var k in method.classes.direct) {
calcmatched(j);
- var recheck = e.changes.classes[k].not(matched);
+ var recheck = changes.classes[k].not(matched);
if (res === null) {
res = rule.cache ? rule.cache.not(taken).add(released.filter(sel)) : $([]);
View
111 thirdparty/jquery-entwine/src/jquery.entwine.eventcapture.js
@@ -0,0 +1,111 @@
+(function($) {
+
+ $.entwine.Namespace.addMethods({
+ bind_capture: function(selector, event, name, capture) {
+ var store = this.captures || (this.captures = {});
+ var rulelists = store[event] || (store[event] = {});
+ var rulelist = rulelists[name] || (rulelists[name] = $.entwine.RuleList());
+
+ rule = rulelist.addRule(selector, event);
+ rule.handler = name;
+
+ this.bind_proxy(selector, name, capture);
+ }
+ });
+
+ var bindings = $.entwine.capture_bindings = {};
+
+ var event_proxy = function(event) {
+ return function(e) {
+ var namespace, capturelists, forevent, capturelist, rule, handler, sel;
+
+ for (var k in $.entwine.namespaces) {
+ namespace = $.entwine.namespaces[k];
+ capturelists = namespace.captures;
+
+ if (capturelists && (forevent = capturelists[event])) {
+ for (var k in forevent) {
+ var capturelist = forevent[k];
+ var triggered = namespace.$([]);
+
+ // Stepping through each selector from most to least specific
+ var j = capturelist.length;
+ while (j--) {
+ rule = capturelist[j];
+ handler = rule.handler;
+ sel = rule.selector.selector;
+
+ var matching = namespace.$(sel).not(triggered);
+ matching[handler].apply(matching, arguments);
+
+ triggered = triggered.add(matching);
+ }
+ }
+ }
+ }
+ }
+ };
+
+ var selector_proxy = function(selector, handler, includechildren) {
+ var matcher = $.selector(selector);
+ return function(e){
+ if (matcher.matches(e.target)) return handler.apply(this, arguments);
+ }
+ };
+
+ var window_proxy = function(selector, handler, includechildren) {
+ return function(e){
+ if (e.target === window) return handler.apply(this, arguments);
+ }
+ };
+
+ var property_proxy = function(property, handler, includechildren) {
+ var matcher;
+
+ return function(e){
+ var match = this['get'+property]();
+
+ if (typeof(match) == 'string') {
+ var matcher = (matcher && match == matcher.selector) ? matcher : $.selector(match);
+ if (matcher.matches(e.target)) return handler.apply(this, arguments);
+ }
+ else {
+ if ($.inArray(e.target, match) !== -1) return handler.apply(this, arguments);
+ }
+ }
+ };
+
+ $.entwine.Namespace.addHandler({
+ order: 10,
+
+ bind: function(selector, k, v) {
+ var match;
+ if ($.isPlainObject(v) && (match = k.match(/^from\s*(.*)/))) {
+ var from = match[1];
+ var proxyGen;
+
+ if (from.match(/[^\w]/)) proxyGen = selector_proxy;
+ else if (from == 'Window' || from == 'window') proxyGen = window_proxy;
+ else proxyGen = property_proxy;
+
+ for (var onevent in v) {
+ var handler = v[onevent];
+ match = onevent.match(/^on(.*)/);
+ var event = match[1];
+
+ this.bind_capture(selector, event, k + '_' + event, proxyGen(from, handler));
+
+ if (!bindings[event]) {
+ var namespaced = event.replace(/(\s+|$)/g, '.entwine$1');
+ bindings[event] = event_proxy(event);
+
+ $(proxyGen == window_proxy ? window : document).bind(namespaced, bindings[event]);
+ }
+ }
+
+ return true;
+ }
+ }
+ });
+
+})(jQuery);
View
14 thirdparty/jquery-entwine/src/jquery.entwine.events.js
@@ -233,18 +233,16 @@
// 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 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;
+ $(document).bind('EntwineElementsAdded', function(e){
+ var forms = $(e.targets).filter('form');
+ if (!forms.length) return;
+
+ forms.bind('submit.entwine_delegate_submit', delegate_submit);
});
})(jQuery);
View
2  thirdparty/jquery-entwine/src/jquery.entwine.inspector.js
@@ -36,7 +36,7 @@ jQuery(function($){
});
$('body').bind('keypress', function(e){
- if ((e.ctrlKey || e.metaKey) && e.which == 96) {
+ if (e.ctrlKey && e.which == 96) {
if (inspectorPanel.css('visibility') != 'visible') {
inspectorPanel.css({top: 0, visibility: 'visible'});
$('body').css({marginTop: 400});
View
6 thirdparty/jquery-entwine/src/jquery.entwine.js
@@ -31,8 +31,10 @@ catch (e) {
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');
+ $(window).unbind('.entwine');
// Remove namespaces, and start over again
- namespaces = $.entwine.namespaces = {};
+ for (var k in namespaces) delete namespaces[k];
+ for (var k in $.entwine.capture_bindings) delete $.entwine.capture_bindings[k];
},
WARN_LEVEL_NONE: 0,
@@ -183,7 +185,7 @@ catch (e) {
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);
View
188 thirdparty/jquery-entwine/vendor/jasmine-1.0.1/jasmine-html.js
@@ -1,188 +0,0 @@
-jasmine.TrivialReporter = function(doc) {
- this.document = doc || document;
- this.suiteDivs = {};
- this.logRunningSpecs = false;
-};
-
-jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
- var el = document.createElement(type);
-
- for (var i = 2; i < arguments.length; i++) {
- var child = arguments[i];
-
- if (typeof child === 'string') {
- el.appendChild(document.createTextNode(child));
- } else {
- if (child) { el.appendChild(child); }
- }
- }
-
- for (var attr in attrs) {
- if (attr == "className") {
- el[attr] = attrs[attr];
- } else {
- el.setAttribute(attr, attrs[attr]);
- }
- }
-
- return el;
-};
-
-jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
- var showPassed, showSkipped;
-
- this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
- this.createDom('div', { className: 'banner' },
- this.createDom('div', { className: 'logo' },
- this.createDom('a', { href: 'http://pivotal.github.com/jasmine/', target: "_blank" }, "Jasmine"),
- this.createDom('span', { className: 'version' }, runner.env.versionString())),
- this.createDom('div', { className: 'options' },
- "Show ",
- showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
- this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
- showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
- this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
- )
- ),
-
- this.runnerDiv = this.createDom('div', { className: 'runner running' },
- this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
- this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
- this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
- );
-
- this.document.body.appendChild(this.outerDiv);
-
- var suites = runner.suites();
- for (var i = 0; i < suites.length; i++) {
- var suite = suites[i];
- var suiteDiv = this.createDom('div', { className: 'suite' },
- this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
- this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
- this.suiteDivs[suite.id] = suiteDiv;
- var parentDiv = this.outerDiv;
- if (suite.parentSuite) {
- parentDiv = this.suiteDivs[suite.parentSuite.id];
- }
- parentDiv.appendChild(suiteDiv);
- }
-
- this.startedAt = new Date();
-
- var self = this;
- showPassed.onclick = function(evt) {
- if (showPassed.checked) {
- self.outerDiv.className += ' show-passed';
- } else {
- self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
- }
- };
-
- showSkipped.onclick = function(evt) {
- if (showSkipped.checked) {
- self.outerDiv.className += ' show-skipped';
- } else {
- self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
- }
- };
-};
-
-jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
- var results = runner.results();
- var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
- this.runnerDiv.setAttribute("class", className);
- //do it twice for IE
- this.runnerDiv.setAttribute("className", className);
- var specs = runner.specs();
- var specCount = 0;
- for (var i = 0; i < specs.length; i++) {
- if (this.specFilter(specs[i])) {
- specCount++;
- }
- }
- var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
- message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
- this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
-
- this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
-};
-
-jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
- var results = suite.results();
- var status = results.passed() ? 'passed' : 'failed';
- if (results.totalCount == 0) { // todo: change this to check results.skipped
- status = 'skipped';
- }
- this.suiteDivs[suite.id].className += " " + status;
-};
-
-jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
- if (this.logRunningSpecs) {
- this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
- }
-};
-
-jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
- var results = spec.results();
- var status = results.passed() ? 'passed' : 'failed';
- if (results.skipped) {
- status = 'skipped';
- }
- var specDiv = this.createDom('div', { className: 'spec ' + status },
- this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
- this.createDom('a', {
- className: 'description',
- href: '?spec=' + encodeURIComponent(spec.getFullName()),
- title: spec.getFullName()
- }, spec.description));
-
-
- var resultItems = results.getItems();
- var messagesDiv = this.createDom('div', { className: 'messages' });
- for (var i = 0; i < resultItems.length; i++) {
- var result = resultItems[i];
-
- if (result.type == 'log') {
- messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
- } else if (result.type == 'expect' && result.passed && !result.passed()) {
- messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
-
- if (result.trace.stack) {
- messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
- }
- }
- }
-
- if (messagesDiv.childNodes.length > 0) {
- specDiv.appendChild(messagesDiv);
- }
-
- this.suiteDivs[spec.suite.id].appendChild(specDiv);
-};
-
-jasmine.TrivialReporter.prototype.log = function() {
- var console = jasmine.getGlobal().console;
- if (console && console.log) {
- if (console.log.apply) {
- console.log.apply(console, arguments);
- } else {
- console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
- }
- }
-};
-
-jasmine.TrivialReporter.prototype.getLocation = function() {
- return this.document.location;
-};
-
-jasmine.TrivialReporter.prototype.specFilter = function(spec) {
- var paramMap = {};
- var params = this.getLocation().search.substring(1).split('&');
- for (var i = 0; i < params.length; i++) {
- var p = params[i].split('=');
- paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
- }
-
- if (!paramMap["spec"]) return true;
- return spec.getFullName().indexOf(paramMap["spec"]) == 0;
-};
View
166 thirdparty/jquery-entwine/vendor/jasmine-1.0.1/jasmine.css
@@ -1,166 +0,0 @@
-body {
- font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
-}
-
-
-.jasmine_reporter a:visited, .jasmine_reporter a {
- color: #303;
-}
-
-.jasmine_reporter a:hover, .jasmine_reporter a:active {
- color: blue;
-}
-
-.run_spec {
- float:right;
- padding-right: 5px;
- font-size: .8em;
- text-decoration: none;
-}
-
-.jasmine_reporter {
- margin: 0 5px;
-}
-
-.banner {
- color: #303;
- background-color: #fef;
- padding: 5px;
-}
-
-.logo {
- float: left;
- font-size: 1.1em;
- padding-left: 5px;
-}
-
-.logo .version {
- font-size: .6em;
- padding-left: 1em;
-}