diff --git a/thirdparty/jquery-entwine/src/domevents/jquery.entwine.domevents.addrem.js b/thirdparty/jquery-entwine/src/domevents/jquery.entwine.domevents.addrem.js new file mode 100755 index 000000000..72befb11b --- /dev/null +++ b/thirdparty/jquery-entwine/src/domevents/jquery.entwine.domevents.addrem.js @@ -0,0 +1,138 @@ +(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; + + var patchDomManipCallback = function(original) { + var patched = 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; + } + patched.patched = true; + + return patched; + } + + var version = $.prototype.jquery.split('.'); + var callbackIdx = (version[0] > 1 || version[1] >= 10 ? 1 : 2); + + // Monkey patch $.fn.domManip to catch all regular jQuery add element calls + var _domManip = $.prototype.domManip; + $.prototype.domManip = function() { + if (!arguments[callbackIdx].patched) arguments[callbackIdx] = patchDomManipCallback(arguments[callbackIdx]); + 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); \ No newline at end of file diff --git a/thirdparty/jquery-entwine/src/domevents/jquery.entwine.domevents.maybechanged.js b/thirdparty/jquery-entwine/src/domevents/jquery.entwine.domevents.maybechanged.js new file mode 100755 index 000000000..9e96c9b27 --- /dev/null +++ b/thirdparty/jquery-entwine/src/domevents/jquery.entwine.domevents.maybechanged.js @@ -0,0 +1,149 @@ +(function($){ + + /** Utility function to monkey-patch a jQuery method */ + var monkey = function( /* method, method, ...., patch */){ + var methods = $.makeArray(arguments); + var patch = methods.pop(); + + $.each(methods, function(i, method){ + var old = $.fn[method]; + + $.fn[method] = function() { + var self = this, args = $.makeArray(arguments); + + var rv = old.apply(self, args); + patch.apply(self, args); + return rv; + } + }); + } + + /** What to call to run a function 'soon'. Normally setTimeout, but for syncronous mode we override so soon === now */ + var runSoon = window.setTimeout; + + /** The timer handle for the asyncronous matching call */ + var ChangeDetails = Base.extend({ + + init: function() { + this.global = false; + this.attrs = {}; + this.classes = {}; + }, + + /** Fire the change event. Only fires on the document node, so bind to that */ + triggerEvent: function() { + // If we're not the active changes instance any more, don't trigger + if (changes != this) return; + + // Cancel any pending timeout (if we're directly called in the mean time) + if (this.check_id) clearTimeout(this.check_id); + + // 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("EntwineSubtreeMaybeChanged", [this]); + }, + + 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(); + + // 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 + + 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); + }); + + // 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); + }); + + 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); } + }); + + // Add some usefull accessors to $.entwine + + $.extend($.entwine, { + /** + * Make onmatch and onunmatch work in synchronous mode - that is, new elements will be detected immediately after + * the DOM manipulation that made them match. This is only really useful for during testing, since it's pretty slow + * (otherwise we'd make it the default). + */ + synchronous_mode: function() { + if (changes && changes.check_id) clearTimeout(changes.check_id); + changes = new ChangeDetails(); + + runSoon = function(func, delay){ func.call(this); return null; }; + }, + + /** + * Trigger onmatch and onunmatch now - usefull for after DOM manipulation by methods other than through jQuery. + * Called automatically on document.ready + */ + triggerMatching: function() { + changes.addAll(); + } + }); + +})(jQuery); \ No newline at end of file diff --git a/thirdparty/jquery-entwine/src/jquery.entwine.addrem.js b/thirdparty/jquery-entwine/src/jquery.entwine.addrem.js new file mode 100755 index 000000000..79568cf8d --- /dev/null +++ b/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); diff --git a/thirdparty/jquery-entwine/src/jquery.entwine.ctors.js b/thirdparty/jquery-entwine/src/jquery.entwine.ctors.js new file mode 100755 index 000000000..2d2ae9933 --- /dev/null +++ b/thirdparty/jquery-entwine/src/jquery.entwine.ctors.js @@ -0,0 +1,242 @@ +(function($) { + + /* Add the methods to handle constructor & destructor binding to the Namespace class */ + $.entwine.Namespace.addMethods({ + bind_condesc: function(selector, name, func) { + var ctors = this.store.ctors || (this.store.ctors = $.entwine.RuleList()) ; + + var rule; + for (var i = 0 ; i < ctors.length; i++) { + if (ctors[i].selector.selector == selector.selector) { + rule = ctors[i]; break; + } + } + if (!rule) { + rule = ctors.addRule(selector, 'ctors'); + } + + rule[name] = func; + + if (!ctors[name+'proxy']) { + var one = this.one('ctors', name); + var namespace = this; + + var proxy = function(els, i, func) { + var j = els.length; + while (j--) { + var el = els[j]; + + var tmp_i = el.i, tmp_f = el.f; + el.i = i; el.f = one; + + try { func.call(namespace.$(el)); } + catch(e) { $.entwine.warn_exception(name, el, e); } + finally { el.i = tmp_i; el.f = tmp_f; } + } + }; + + ctors[name+'proxy'] = proxy; + } + } + }); + + $.entwine.Namespace.addHandler({ + order: 30, + + bind: function(selector, k, v) { + if ($.isFunction(v) && (k == 'onmatch' || k == 'onunmatch')) { + // When we add new matchers we need to trigger a full global recalc once, regardless of the DOM changes that triggered the event + this.matchersDirty = true; + + this.bind_condesc(selector, k, v); + return true; + } + } + }); + + /** + * Finds all the elements that now match a different rule (or have been removed) and call onmatch on onunmatch as appropriate + * + * Because this has to scan the DOM, and is therefore fairly slow, this is normally triggered off a short timeout, so that + * a series of DOM manipulations will only trigger this once. + * + * The downside of this is that things like: + * $('#foo').addClass('tabs'); $('#foo').tabFunctionBar(); + * won't work. + */ + $(document).bind('EntwineSubtreeMaybeChanged', function(e, changes){ + // var start = (new Date).getTime(); + + // For every namespace + for (var k in $.entwine.namespaces) { + var namespace = $.entwine.namespaces[k]; + + // That has constructors or destructors + var ctors = namespace.store.ctors; + if (ctors) { + + // Keep a record of elements that have matched some previous more specific rule. + // Not that we _don't_ actually do that until this is needed. If matched is null, it's not been calculated yet. + // We also keep track of any elements that have newly been taken or released by a specific rule + var matched = null, taken = $([]), released = $([]); + + // Updates matched to contain all the previously matched elements as if we'd been keeping track all along + var calcmatched = function(j){ + if (matched !== null) return; + matched = $([]); + + var cache, k = ctors.length; + while ((--k) > j) { + if (cache = ctors[k].cache) matched = matched.add(cache); + } + } + + // Some declared variables used in the loop + var add, rem, res, rule, sel, ctor, dtor, full; + + // Stepping through each selector from most to least specific + var j = ctors.length; + while (j--) { + // Build some quick-access variables + rule = ctors[j]; + sel = rule.selector.selector; + ctor = rule.onmatch; + dtor = rule.onunmatch; + + /* + Rule.cache might be stale or fresh. It'll be stale if + - some more specific selector now has some of rule.cache in it + - some change has happened that means new elements match this selector now + - some change has happened that means elements no longer match this selector + + The first we can just compare rules.cache with matched, removing anything that's there already. + */ + + // Reset the "elements that match this selector and no more specific selector with an onmatch rule" to null. + // Staying null means this selector is fresh. + res = null; + + // If this gets changed to true, it's too hard to do a delta update, so do a full update + full = false; + + if (namespace.matchersDirty || changes.global) { + // For now, just fall back to old version. We need to do something like changed.Subtree.find('*').andSelf().filter(sel), but that's _way_ slower on modern browsers than the below + full = true; + } + else { + // We don't deal with attributes yet, so any attribute change means we need to do a full recalc + for (var k in 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(changes); + + if (method.classes.context) { + full = true; + } + else { + for (var k in method.classes.direct) { + calcmatched(j); + var recheck = changes.classes[k].not(matched); + + if (res === null) { + res = rule.cache ? rule.cache.not(taken).add(released.filter(sel)) : $([]); + } + + res = res.not(recheck).add(recheck.filter(sel)); + } + } + } + + if (full) { + calcmatched(j); + res = $(sel).not(matched); + } + else { + if (!res) { + // We weren't stale because of any changes to the DOM that affected this selector, but more specific + // onmatches might have caused stale-ness + + // Do any of the previous released elements match this selector? + add = released.length && released.filter(sel); + + if (add && add.length) { + // Yes, so we're stale as we need to include them. Filter for any possible taken value at the same time + res = rule.cache ? rule.cache.not(taken).add(add) : add; + } + else { + // Do we think we own any of the elements now taken by more specific rules? + rem = taken.length && rule.cache && rule.cache.filter(taken); + + if (rem && rem.length) { + // Yes, so we're stale as we need to exclude them. + res = rule.cache.not(rem); + } + } + } + } + + // Res will be null if we know we are fresh (no full needed, selector not affectedBy changes) + if (res === null) { + // If we are tracking matched, add ourselves + if (matched && rule.cache) matched = matched.add(rule.cache); + } + else { + // If this selector has a list of elements it matched against last time + if (rule.cache) { + // Find the ones that are extra this time + add = res.not(rule.cache); + rem = rule.cache.not(res); + } + else { + add = res; rem = null; + } + + if ((add && add.length) || (rem && rem.length)) { + if (rem && rem.length) { + released = released.add(rem); + + if (dtor && !rule.onunmatchRunning) { + rule.onunmatchRunning = true; + ctors.onunmatchproxy(rem, j, dtor); + rule.onunmatchRunning = false; + } + } + + // Call the constructor on the newly matched ones + if (add && add.length) { + taken = taken.add(add); + released = released.not(add); + + if (ctor && !rule.onmatchRunning) { + rule.onmatchRunning = true; + ctors.onmatchproxy(add, j, ctor); + rule.onmatchRunning = false; + } + } + } + + // If we are tracking matched, add ourselves + if (matched) matched = matched.add(res); + + // And remember this list of matching elements again this selector, so next matching we can find the unmatched ones + rule.cache = res; + } + } + + namespace.matchersDirty = false; + } + } + + // console.log((new Date).getTime() - start); + }); + + +})(jQuery); diff --git a/thirdparty/jquery-entwine/src/jquery.entwine.eventcapture.js b/thirdparty/jquery-entwine/src/jquery.entwine.eventcapture.js new file mode 100755 index 000000000..e089f685b --- /dev/null +++ b/thirdparty/jquery-entwine/src/jquery.entwine.eventcapture.js @@ -0,0 +1,118 @@ +(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 document_proxy = function(selector, handler, includechildren) { + return function(e){ + if (e.target === document) 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 if (from == 'Document' || from == 'document') proxyGen = document_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); diff --git a/thirdparty/jquery-entwine/src/jquery.entwine.events.js b/thirdparty/jquery-entwine/src/jquery.entwine.events.js new file mode 100755 index 000000000..2f21e1997 --- /dev/null +++ b/thirdparty/jquery-entwine/src/jquery.entwine.events.js @@ -0,0 +1,249 @@ +(function($) { + + /** Taken from jQuery 1.5.2 for backwards compatibility */ + if ($.support.changeBubbles == undefined) { + $.support.changeBubbles = true; + + var el = document.createElement("div"); + eventName = "onchange"; + + if (el.attachEvent) { + var isSupported = (eventName in el); + if (!isSupported) { + el.setAttribute(eventName, "return;"); + isSupported = typeof el[eventName] === "function"; + } + + $.support.changeBubbles = isSupported; + } + } + + /* Return true if node b is the same as, or is a descendant of, node a */ + if (document.compareDocumentPosition) { + var is_or_contains = function(a, b) { + return a && b && (a == b || !!(a.compareDocumentPosition(b) & 16)); + }; + } + else { + var is_or_contains = function(a, b) { + return a && b && (a == b || (a.contains ? a.contains(b) : true)); + }; + } + + /* Add the methods to handle event binding to the Namespace class */ + $.entwine.Namespace.addMethods({ + build_event_proxy: function(name) { + var one = this.one(name, 'func'); + + var prxy = function(e, data) { + // For events that do not bubble we manually trigger delegation (see delegate_submit below) + // If this event is a manual trigger, the event we actually want to bubble is attached as a property of the passed event + e = e.delegatedEvent || e; + + var el = e.target; + while (el && el.nodeType == 1 && !e.isPropagationStopped()) { + var ret = one(el, arguments); + if (ret !== undefined) e.result = ret; + if (ret === false) { e.preventDefault(); e.stopPropagation(); } + + el = el.parentNode; + } + }; + + return prxy; + }, + + build_mouseenterleave_proxy: function(name) { + var one = this.one(name, 'func'); + + var prxy = function(e) { + var el = e.target; + var rel = e.relatedTarget; + + while (el && el.nodeType == 1 && !e.isPropagationStopped()) { + /* We know el contained target. If it also contains relatedTarget then we didn't mouseenter / leave. What's more, every ancestor will also + contan el and rel, and so we can just stop bubbling */ + if (is_or_contains(el, rel)) break; + + var ret = one(el, arguments); + if (ret !== undefined) e.result = ret; + if (ret === false) { e.preventDefault(); e.stopPropagation(); } + + el = el.parentNode; + } + }; + + return prxy; + }, + + build_change_proxy: function(name) { + var one = this.one(name, 'func'); + + /* + This change bubble emulation code is taken mostly from jQuery 1.6 - unfortunately we can't easily reuse any of + it without duplication, so we'll have to re-migrate any bugfixes + */ + + // Get the value of an item. Isn't supposed to be interpretable, just stable for some value, and different + // once the value changes + var getVal = function( elem ) { + var type = elem.type, val = elem.value; + + if (type === "radio" || type === "checkbox") { + val = elem.checked; + } + else if (type === "select-multiple") { + val = ""; + if (elem.selectedIndex > -1) { + val = jQuery.map(elem.options, function(elem){ return elem.selected; }).join("-"); + } + } + else if (jQuery.nodeName(elem, "select")) { + val = elem.selectedIndex; + } + + return val; + }; + + // Test if a node name is a form input + var rformElems = /^(?:textarea|input|select)$/i; + + // Check if this event is a change, and bubble the change event if it is + var testChange = function(e) { + var elem = e.target, data, val; + + if (!rformElems.test(elem.nodeName) || elem.readOnly) return; + + data = jQuery.data(elem, "_entwine_change_data"); + val = getVal(elem); + + // the current data will be also retrieved by beforeactivate + if (e.type !== "focusout" || elem.type !== "radio") { + jQuery.data(elem, "_entwine_change_data", val); + } + + if (data === undefined || val === data) return; + + if (data != null || val) { + e.type = "change"; + + while (elem && elem.nodeType == 1 && !e.isPropagationStopped()) { + var ret = one(elem, arguments); + if (ret !== undefined) e.result = ret; + if (ret === false) { e.preventDefault(); e.stopPropagation(); } + + elem = elem.parentNode; + } + } + }; + + // The actual proxy - responds to several events, some of which triger a change check, some + // of which just store the value for future change checks + var prxy = function(e) { + var event = e.type, elem = e.target, type = jQuery.nodeName( elem, "input" ) ? elem.type : ""; + + switch (event) { + case 'focusout': + case 'beforedeactivate': + testChange.apply(this, arguments); + break; + + case 'click': + if ( type === "radio" || type === "checkbox" || jQuery.nodeName( elem, "select" ) ) { + testChange.apply(this, arguments); + } + break; + + // Change has to be called before submit + // Keydown will be called before keypress, which is used in submit-event delegation + case 'keydown': + if ( + (e.keyCode === 13 && !jQuery.nodeName( elem, "textarea" ) ) || + (e.keyCode === 32 && (type === "checkbox" || type === "radio")) || + type === "select-multiple" + ) { + testChange.apply(this, arguments); + } + break; + + // Beforeactivate happens also before the previous element is blurred + // with this event you can't trigger a change event, but you can store + // information + case 'focusin': + case 'beforeactivate': + jQuery.data( elem, "_entwine_change_data", getVal(elem) ); + break; + } + } + + return prxy; + }, + + bind_event: function(selector, name, func, event) { + var funcs = this.store[name] || (this.store[name] = $.entwine.RuleList()) ; + var proxies = funcs.proxies || (funcs.proxies = {}); + + var rule = funcs.addRule(selector, name); rule.func = func; + + if (!proxies[name]) { + switch (name) { + case 'onmouseenter': + proxies[name] = this.build_mouseenterleave_proxy(name); + event = 'mouseover'; + break; + case 'onmouseleave': + proxies[name] = this.build_mouseenterleave_proxy(name); + event = 'mouseout'; + break; + case 'onchange': + if (!$.support.changeBubbles) { + proxies[name] = this.build_change_proxy(name); + event = 'click keydown focusin focusout beforeactivate beforedeactivate'; + } + break; + case 'onsubmit': + event = 'delegatedSubmit'; + break; + case 'onfocus': + case 'onblur': + $.entwine.warn('Event '+event+' not supported - using focusin / focusout instead', $.entwine.WARN_LEVEL_IMPORTANT); + } + + // If none of the special handlers created a proxy, use the generic proxy + if (!proxies[name]) proxies[name] = this.build_event_proxy(name); + + $(document).bind(event.replace(/(\s+|$)/g, '.entwine$1'), proxies[name]); + } + } + }); + + $.entwine.Namespace.addHandler({ + order: 40, + + bind: function(selector, k, v){ + var match, event; + if ($.isFunction(v) && (match = k.match(/^on(.*)/))) { + event = match[1]; + this.bind_event(selector, k, v, event); + return true; + } + } + }); + + // Find all forms and bind onsubmit to trigger on the document too. + // This is the only event that can't be grabbed via delegation + + var delegate_submit = function(e, data){ + var delegationEvent = $.Event('delegatedSubmit'); delegationEvent.delegatedEvent = e; + return $(document).trigger(delegationEvent, data); + }; + + $(document).bind('EntwineElementsAdded', function(e){ + var forms = $(e.targets).filter('form'); + if (!forms.length) return; + + forms.bind('submit.entwine_delegate_submit', delegate_submit); + }); + +})(jQuery); + \ No newline at end of file diff --git a/thirdparty/jquery-entwine/src/jquery.entwine.inspector.js b/thirdparty/jquery-entwine/src/jquery.entwine.inspector.js new file mode 100755 index 000000000..588a535dd --- /dev/null +++ b/thirdparty/jquery-entwine/src/jquery.entwine.inspector.js @@ -0,0 +1,240 @@ + +jQuery(function($){ + // Create a new style element + var styleEl = document.createElement('style'); + styleEl.setAttribute('type', 'text/css'); + (document.head || document.getElementsByTagName('head')[0]).appendChild(styleEl); + + var inspectorCSS = [ + '#entwine-inspector { position: fixed; z-index: 1000001; left: 0; right: 0; height: 400px; background: white; -webkit-box-shadow: 0 5px 40px 0 black; -moz-box-shadow: 0 5px 40px 0 black; }', + '#entwine-inspector li { list-style: none; margin: 2px 0; padding: 2px 0; }', + '#entwine-inspector li:hover { background: #eee; }', + '#entwine-inspector li.selected { background: #ddd; }', + + '#ei-columns { overflow: hidden; display: -webkit-box; display: -moz-box; width: 100%; height: 380px; }', + + '.ei-column { height: 380px; width: 1px; -webkit-box-flex: 1; -moz-box-flex: 1; }', + '#entwine-inspector .ei-column h1 { display: block; margin: 0; padding: 5px 2px; height: 20px; text-align: center; background: #444; color: #eee; font-size: 14px; font-weight: bold; }', + '#entwine-inspector .ei-column ul { overflow-y: scroll; height: 350px; }', + + '#ei-options { overflow: hidden; height: 20px; background: #444; color: #eee; }', + '#ei-options label { padding-right: 5px; border-right: 1px solid #eee; }', + + '.ei-entwined:hover, .ei-selected { background: rgba(128,0,0,0.2); }', + '.ei-hovernode { position: absolute; z-index: 1000000; background: rgba(0,0,0,0.3); border: 1px solid white; outline: 1px solid white; }', + + '#ei-selectors li { color: #aaa; display: none; }', + '#ei-selectors li.matching, #entwine-inspector.show-unmatched #ei-selectors li { display: block; }', + '#ei-selectors li.matching { color: black; }' + ].join("\n"); + + // Set the style element to style up the inspector panel + if(styleEl.styleSheet){ + styleEl.styleSheet.cssText = inspectorCSS; + }else{ + styleEl.appendChild(document.createTextNode(inspectorCSS)); + } + + var inspectorPanel = $('
').appendTo('body'); + var columnHolder = $('
').appendTo(inspectorPanel); + var optionsHolder = $('
').appendTo(inspectorPanel); + + inspectorPanel.css({ + top: -400, + visibility: 'hidden' + }); + + $('body').bind('keypress', function(e){ + if (e.ctrlKey && e.which == 96) { + if (inspectorPanel.css('visibility') != 'visible') { + inspectorPanel.css({top: 0, visibility: 'visible'}); + $('body').css({marginTop: 400}); + initialise(); + } + else { + inspectorPanel.css({top: -400, visibility: 'hidden'}); + $('body').css({marginTop: 0}); + reset(); + } + + return false; + } + }); + + var showUnmatching = $('').appendTo(optionsHolder); + var showUnmatchingLabel = $('').appendTo(optionsHolder); + + showUnmatching.bind('click', function(){ + inspectorPanel.toggleClass('show-unmatched', $(this).val()); + }); + + var hovernode; + + var reset = function() { + $('.ei-entwined').unbind('.entwine-inspector').removeClass('ei-entwined'); + if (hovernode) hovernode.remove(); + } + + var initialise = function(){ + reset(); + + $.each($.entwine.namespaces, function(name, namespace){ + $.each(namespace.store, function(name, list){ + $.each(list, function(i, rule){ + var match = $(rule.selector.selector); + match.addClass('ei-entwined').bind('click.entwine-inspector', displaydetails); + }) + }); + }); + }; + + var dumpElement = function(el) { + var frag = document.createDocumentFragment(); + var div = document.createElement('div'); frag.appendChild(div); + + var clone = el.cloneNode(false); $(clone).removeClass('ei-entwined').removeAttr('style'); + + var i = clone.attributes.length; + while (i--) { + var attr = clone.attributes.item(i); + if (attr.name != 'class' && attr.name != 'id' && attr.value.length > 20) attr.value = attr.value.substr(0, 18)+'..'+attr.value.substr(-2); + } + + div.appendChild(clone); + return div.innerHTML; + }; + + var displaydetails = function(e){ + e.preventDefault(); e.stopPropagation(); + + columnHolder.empty(); + + var columns = {}; + $.each(['elements', 'namespaces', 'methods', 'selectors'], function(i, col){ + columns[col] = $('

'+col+'

').appendTo(columnHolder); + }) + + var lists = {}; + + var ctr = 0; + + lists.elements = $('').appendTo(columns.elements); + + var displayelement = function(){ + var target = $(this); + + var li = $('
  • '); + li.text(dumpElement(this)).attr('data-id', ++ctr).data('el', target).prependTo(lists.elements); + + var namespaces = $('').appendTo(columns.namespaces); + + $.each($.entwine.namespaces, function(name, namespace){ + var methods = $(''); + + $.each(namespace.store, function(method, list){ + + if (method == 'ctors') { + var matchselectors = $(''); + var unmatchselectors = $(''); + + $.each(list, function(i, rule){ + var matchitem = $('
  • '+rule.selector.selector+'
  • ').prependTo(matchselectors); + var unmatchitem = rule.onunmatch ? $('
  • '+rule.selector.selector+'
  • ').prependTo(unmatchselectors) : null; + + if (target.is(rule.selector.selector)) { + matchitem.addClass('matching'); unmatchitem && unmatchitem.addClass('matching'); + + if (!methods.parent().length) { + $('
  • '+name+'
  • ').prependTo(namespaces); + methods.appendTo(columns.methods); + } + + if (!matchselectors.parent().length) { + $('
  • onmatch
  • ').prependTo(methods); + matchselectors.appendTo(columns.selectors); + } + + if (rule.onunmatch && !unmatchselectors.parent().length) { + $('
  • onunmatch
  • ').prependTo(methods); + unmatchselectors.appendTo(columns.selectors); + } + } + }); + } + else { + var selectors = $(''); + + $.each(list, function(i, rule){ + var ruleitem = $('
  • '+rule.selector.selector+'
  • ').prependTo(selectors); + + if (target.is(rule.selector.selector)){ + ruleitem.addClass('matching'); + + if (!methods.parent().length) { + $('
  • '+name+'
  • ').prependTo(namespaces); + methods.appendTo(columns.methods); + } + + if (!selectors.parent().length) { + $('
  • '+method+'
  • ').prependTo(methods); + selectors.appendTo(columns.selectors); + } + } + }) + } + }); + }); + }; + + $.each($(e.target).parents().andSelf().filter('.ei-entwined'), displayelement); + $('#ei-elements > ul:first > li:first').click(); + } + + var activatelist = function(list) { + list = $(list); + + list.siblings('ul').css('display', 'none'); + + list.css('display', 'block'); + list.children().first().click(); + } + + $('#entwine-inspector').live('mouseleave', function(){ + if (hovernode) hovernode.hide(); + }) + + $('#entwine-inspector').live('mouseenter', function(){ + if (hovernode) hovernode.show(); + }) + + $('#ei-elements > ul > li').live('click', function(e){ + var target = $(e.target), id = target.attr('data-id'); + target.addClass('selected').siblings().removeClass('selected'); + + if (!hovernode) { + hovernode = $('
    ').appendTo('body'); + } + + var hover = target.data('el'); + hovernode.css({width: hover.outerWidth()-2, height: hover.outerHeight()-2, top: hover.offset().top, left: hover.offset().left}); + + $('.ei-selected').removeClass('ei-selected'); + + activatelist('#ei-namespaces ul[data-element="'+id+'"]'); + }); + + $('#ei-namespaces > ul > li').live('click', function(e){ + var target = $(e.target), namespace = target.attr('data-namespace'); + target.addClass('selected').siblings().removeClass('selected'); + + activatelist('#ei-methods ul[data-namespace="'+namespace+'"]'); + }); + + $('#ei-methods > ul > li').live('click', function(e){ + var target = $(e.target), method = target.attr('data-method'); + target.addClass('selected').siblings().removeClass('selected'); + + activatelist('#ei-selectors ul[data-method="'+method+'"]'); + }); + +}); \ No newline at end of file diff --git a/thirdparty/jquery-entwine/src/jquery.entwine.js b/thirdparty/jquery-entwine/src/jquery.entwine.js new file mode 100755 index 000000000..202aca00b --- /dev/null +++ b/thirdparty/jquery-entwine/src/jquery.entwine.js @@ -0,0 +1,354 @@ +try { + console.log; +} +catch (e) { + window.console = undefined; +} + +(function($) { + + /* Create a subclass of the jQuery object. This was introduced in jQuery 1.5, but removed again in 1.9 */ + var sub = function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + + jQuery.extend( true, jQuerySub, $ ); + jQuerySub.superclass = $; + jQuerySub.fn = jQuerySub.prototype = $(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }; + + var namespaces = {}; + + $.entwine = function() { + $.fn.entwine.apply(null, arguments); + }; + + /** + * A couple of utility functions for accessing the store outside of this closure, and for making things + * operate in a little more easy-to-test manner + */ + $.extend($.entwine, { + /** + * Get all the namespaces. Useful for introspection? Internal interface of Namespace not guaranteed consistant + */ + namespaces: namespaces, + + /** + * Remove all entwine rules + */ + clear_all_rules: function() { + // Remove proxy functions + for (var k in $.fn) { if ($.fn[k].isentwinemethod) delete $.fn[k]; } + // Remove bound events - TODO: Make this pluggable, so this code can be moved to jquery.entwine.events.js + $(document).unbind('.entwine'); + $(window).unbind('.entwine'); + // Remove namespaces, and start over again + for (var k in namespaces) delete namespaces[k]; + for (var k in $.entwine.capture_bindings) delete $.entwine.capture_bindings[k]; + }, + + WARN_LEVEL_NONE: 0, + WARN_LEVEL_IMPORTANT: 1, + WARN_LEVEL_BESTPRACTISE: 2, + + /** + * Warning level. Set to a higher level to get warnings dumped to console. + */ + warningLevel: 0, + + /** Utility to optionally display warning messages depending on level */ + warn: function(message, level) { + if (level <= $.entwine.warningLevel && console && console.warn) { + console.warn(message); + if (console.trace) console.trace(); + } + }, + + warn_exception: function(where, /* optional: */ on, e) { + if ($.entwine.WARN_LEVEL_IMPORTANT <= $.entwine.warningLevel && console && console.warn) { + if (arguments.length == 2) { e = on; on = null; } + + if (on) console.warn('Uncaught exception',e,'in',where,'on',on); + else console.warn('Uncaught exception',e,'in',where); + + if (e.stack) console.warn("Stack Trace:\n" + e.stack); + } + } + }); + + + /** Stores a count of definitions, so that we can sort identical selectors by definition order */ + var rulecount = 0; + + var Rule = Base.extend({ + init: function(selector, name) { + this.selector = selector; + this.specifity = selector.specifity(); + this.important = 0; + this.name = name; + this.rulecount = rulecount++; + } + }); + + Rule.compare = function(a, b) { + var as = a.specifity, bs = b.specifity; + + return (a.important - b.important) || + (as[0] - bs[0]) || + (as[1] - bs[1]) || + (as[2] - bs[2]) || + (a.rulecount - b.rulecount) ; + }; + + $.entwine.RuleList = function() { + var list = []; + + list.addRule = function(selector, name){ + var rule = Rule(selector, name); + + list[list.length] = rule; + list.sort(Rule.compare); + + return rule; + }; + + return list; + }; + + var handlers = []; + + /** + * A Namespace holds all the information needed for adding entwine methods to a namespace (including the _null_ namespace) + */ + $.entwine.Namespace = Base.extend({ + init: function(name){ + if (name && !name.match(/^[A-Za-z0-9.]+$/)) $.entwine.warn('Entwine namespace '+name+' is not formatted as period seperated identifiers', $.entwine.WARN_LEVEL_BESTPRACTISE); + name = name || '__base'; + + this.name = name; + this.store = {}; + + namespaces[name] = this; + + if (name == "__base") { + this.injectee = $.fn; + this.$ = $; + } + else { + // We're in a namespace, so we build a Class that subclasses the jQuery Object Class to inject namespace functions into + this.$ = $.sub ? $.sub() : sub(); + // Work around bug in sub() - subclass must share cache with root or data won't get cleared by cleanData + this.$.cache = $.cache; + + this.injectee = this.$.prototype; + + // We override entwine to inject the name of this namespace when defining blocks inside this namespace + var entwine_wrapper = this.injectee.entwine = function(spacename) { + var args = arguments; + + if (!spacename || typeof spacename != 'string') { args = $.makeArray(args); args.unshift(name); } + else if (spacename.charAt(0) != '.') args[0] = name+'.'+spacename; + + return $.fn.entwine.apply(this, args); + }; + + this.$.entwine = function() { + entwine_wrapper.apply(null, arguments); + }; + + for (var i = 0; i < handlers.length; i++) { + var handler = handlers[i], builder; + + // Inject jQuery object method overrides + if (builder = handler.namespaceMethodOverrides) { + var overrides = builder(this); + for (var k in overrides) this.injectee[k] = overrides[k]; + } + + // Inject $.entwine function overrides + if (builder = handler.namespaceStaticOverrides) { + var overrides = builder(this); + for (var k in overrides) this.$.entwine[k] = overrides[k]; + } + } + } + }, + + /** + * Returns a function that does selector matching against the function list for a function name + * Used by proxy for all calls, and by ctorProxy to handle _super calls + * @param {String} name - name of the function as passed in the construction object + * @param {String} funcprop - the property on the Rule object that gives the actual function to call + * @param {function} basefunc - the non-entwine function to use as the catch-all function at the bottom of the stack + */ + one: function(name, funcprop, basefunc) { + var namespace = this; + var funcs = this.store[name]; + + var one = function(el, args, i){ + if (i === undefined) i = funcs.length; + while (i--) { + if (funcs[i].selector.matches(el)) { + var ret, tmp_i = el.i, tmp_f = el.f; + el.i = i; el.f = one; + try { ret = funcs[i][funcprop].apply(namespace.$(el), args); } + finally { el.i = tmp_i; el.f = tmp_f; } + return ret; + } + } + // If we didn't find a entwine-defined function, but there is a non-entwine function to use as a base, try that + if (basefunc) return basefunc.apply(namespace.$(el), args); + }; + + return one; + }, + + /** + * A proxy is a function attached to a callable object (either the base jQuery.fn or a subspace object) which handles + * finding and calling the correct function for each member of the current jQuery context + * @param {String} name - name of the function as passed in the construction object + * @param {function} basefunc - the non-entwine function to use as the catch-all function at the bottom of the stack + */ + build_proxy: function(name, basefunc) { + var one = this.one(name, 'func', basefunc); + + var prxy = function() { + var rv, ctx = $(this); + + var i = ctx.length; + while (i--) rv = one(ctx[i], arguments); + return rv; + }; + + return prxy; + }, + + bind_proxy: function(selector, name, func) { + var rulelist = this.store[name] || (this.store[name] = $.entwine.RuleList()); + + var rule = rulelist.addRule(selector, name); rule.func = func; + + if (!this.injectee.hasOwnProperty(name) || !this.injectee[name].isentwinemethod) { + this.injectee[name] = this.build_proxy(name, this.injectee.hasOwnProperty(name) ? this.injectee[name] : null); + this.injectee[name].isentwinemethod = true; + } + + if (!this.injectee[name].isentwinemethod) { + $.entwine.warn('Warning: Entwine function '+name+' clashes with regular jQuery function - entwine function will not be callable directly on jQuery object', $.entwine.WARN_LEVEL_IMPORTANT); + } + }, + + add: function(selector, data) { + // For every item in the hash, try ever method handler, until one returns true + for (var k in data) { + var v = data[k]; + + for (var i = 0; i < handlers.length; i++) { + if (handlers[i].bind && handlers[i].bind.call(this, selector, k, v)) break; + } + } + }, + + has: function(ctx, name) { + var rulelist = this.store[name]; + if (!rulelist) return false; + + /* We go forward this time, since low specifity is likely to knock out a bunch of elements quickly */ + for (var i = 0 ; i < rulelist.length; i++) { + ctx = ctx.not(rulelist[i].selector); + if (!ctx.length) return true; + } + return false; + } + }); + + /** + * A handler is some javascript code that adds support for some time of key / value pair passed in the hash to the Namespace add method. + * The default handlers provided (and included by default) are event, ctor and properties + */ + $.entwine.Namespace.addHandler = function(handler) { + for (var i = 0; i < handlers.length && handlers[i].order < handler.order; i++) { /* Pass */ } + handlers.splice(i, 0, handler); + }; + + $.entwine.Namespace.addHandler({ + order: 50, + + bind: function(selector, k, v){ + if ($.isFunction(v)) { + this.bind_proxy(selector, k, v); + return true; + } + } + }); + + $.extend($.fn, { + /** + * Main entwine function. Used for new definitions, calling into a namespace (or forcing the base namespace) and entering a using block + * + */ + entwine: function(spacename) { + var i = 0; + /* Don't actually work out selector until we try and define something on it - we might be opening a namespace on an function-traveresed object + which have non-standard selectors like .parents(.foo).slice(0,1) */ + var selector = null; + + /* By default we operator on the base namespace */ + var namespace = namespaces.__base || $.entwine.Namespace(); + + /* If the first argument is a string, then it's the name of a namespace. Look it up */ + if (typeof spacename == 'string') { + if (spacename.charAt('0') == '.') spacename = spacename.substr(1); + if (spacename) namespace = namespaces[spacename] || $.entwine.Namespace(spacename); + i=1; + } + + /* All remaining arguments should either be using blocks or definition hashs */ + while (i < arguments.length) { + var res = arguments[i++]; + + // If it's a function, call it - either it's a using block or it's a namespaced entwine definition + if ($.isFunction(res)) { + if (res.length != 1) $.entwine.warn('Function block inside entwine definition does not take $ argument properly', $.entwine.WARN_LEVEL_IMPORTANT); + res = res.call(namespace.$(this), namespace.$); + } + + // If we have a entwine definition hash, inject it into namespace + if (res) { + if (selector === null) selector = this.selector ? $.selector(this.selector) : false; + + if (selector) namespace.add(selector, res); + else $.entwine.warn('Entwine block given to entwine call without selector. Make sure you call $(selector).entwine when defining blocks', $.entwine.WARN_LEVEL_IMPORTANT); + } + } + + /* Finally, return the jQuery object 'this' refers to, wrapped in the new namespace */ + return namespace.$(this); + }, + + /** + * Calls the next most specific version of the current entwine method + */ + _super: function(){ + var rv, i = this.length; + while (i--) { + var el = this[0]; + rv = el.f(el, arguments, el.i); + } + return rv; + } + }); + +})(jQuery); diff --git a/thirdparty/jquery-entwine/src/jquery.entwine.legacy.js b/thirdparty/jquery-entwine/src/jquery.entwine.legacy.js new file mode 100755 index 000000000..b2399fdd6 --- /dev/null +++ b/thirdparty/jquery-entwine/src/jquery.entwine.legacy.js @@ -0,0 +1,21 @@ +(function($) { + + // Adds back concrete methods for backwards compatibility + $.concrete = $.entwine; + $.fn.concrete = $.fn.entwine; + $.fn.concreteData = $.fn.entwineData; + + // Use addHandler to hack in the namespace.$.concrete equivilent to the namespace.$.entwine namespace-injection + $.entwine.Namespace.addHandler({ + order: 100, + bind: function(selector, k, v) { return false; }, + + namespaceMethodOverrides: function(namespace){ + namespace.$.concrete = namespace.$.entwine; + namespace.injectee.concrete = namespace.injectee.entwine; + namespace.injectee.concreteData = namespace.injectee.entwineData; + return {}; + } + }); + +})(jQuery); diff --git a/thirdparty/jquery-entwine/src/jquery.entwine.properties.js b/thirdparty/jquery-entwine/src/jquery.entwine.properties.js new file mode 100755 index 000000000..eaffce4be --- /dev/null +++ b/thirdparty/jquery-entwine/src/jquery.entwine.properties.js @@ -0,0 +1,85 @@ +(function($) { + + var entwine_prepend = '__entwine!'; + + var getEntwineData = function(el, namespace, property) { + return el.data(entwine_prepend + namespace + '!' + property); + }; + + var setEntwineData = function(el, namespace, property, value) { + return el.data(entwine_prepend + namespace + '!' + property, value); + }; + + var getEntwineDataAsHash = function(el, namespace) { + var hash = {}; + var id = jQuery.data(el[0]); + + var matchstr = entwine_prepend + namespace + '!'; + var matchlen = matchstr.length; + + var cache = jQuery.cache[id]; + for (var k in cache) { + if (k.substr(0,matchlen) == matchstr) hash[k.substr(matchlen)] = cache[k]; + } + + return hash; + }; + + var setEntwineDataFromHash = function(el, namespace, hash) { + for (var k in hash) setEntwineData(namespace, k, hash[k]); + }; + + var entwineData = function(el, namespace, args) { + switch (args.length) { + case 0: + return getEntwineDataAsHash(el, namespace); + case 1: + if (typeof args[0] == 'string') return getEntwineData(el, namespace, args[0]); + else return setEntwineDataFromHash(el, namespace, args[0]); + default: + return setEntwineData(el, namespace, args[0], args[1]); + } + }; + + $.extend($.fn, { + entwineData: function() { + return entwineData(this, '__base', arguments); + } + }); + + $.entwine.Namespace.addHandler({ + order: 60, + + bind: function(selector, k, v) { + if (k.charAt(0) != k.charAt(0).toUpperCase()) $.entwine.warn('Entwine property '+k+' does not start with a capital letter', $.entwine.WARN_LEVEL_BESTPRACTISE); + + // Create the getters and setters + + var getterName = 'get'+k; + var setterName = 'set'+k; + + this.bind_proxy(selector, getterName, function() { var r = this.entwineData(k); return r === undefined ? v : r; }); + this.bind_proxy(selector, setterName, function(v){ return this.entwineData(k, v); }); + + // Get the get and set proxies we just created + + var getter = this.injectee[getterName]; + var setter = this.injectee[setterName]; + + // And bind in the jQuery-style accessor + + this.bind_proxy(selector, k, function(v){ return (arguments.length == 1 ? setter : getter).call(this, v) ; }); + + return true; + }, + + namespaceMethodOverrides: function(namespace){ + return { + entwineData: function() { + return entwineData(this, namespace.name, arguments); + } + }; + } + }); + +})(jQuery); diff --git a/thirdparty/jquery-entwine/src/jquery.focusinout.js b/thirdparty/jquery-entwine/src/jquery.focusinout.js new file mode 100755 index 000000000..444afb13c --- /dev/null +++ b/thirdparty/jquery-entwine/src/jquery.focusinout.js @@ -0,0 +1,52 @@ +(function($){ + + /** + * Add focusin and focusout support to bind and live for browers other than IE. Designed to be usable in a delegated fashion (like $.live) + * Copyright (c) 2007 Jörn Zaefferer + */ + if ($.support.focusinBubbles === undefined) { + $.support.focusinBubbles = !!($.browser.msie); + } + + if (!$.support.focusinBubbles && !$.event.special.focusin) { + // Emulate focusin and focusout by binding focus and blur in capturing mode + $.each({focus: 'focusin', blur: 'focusout'}, function(original, fix){ + $.event.special[fix] = { + setup: function(){ + if (!this.addEventListener) return false; + this.addEventListener(original, $.event.special[fix].handler, true); + }, + teardown: function(){ + if (!this.removeEventListener) return false; + this.removeEventListener(original, $.event.special[fix].handler, true); + }, + handler: function(e){ + arguments[0] = $.event.fix(e); + arguments[0].type = fix; + return $.event.handle.apply(this, arguments); + } + }; + }); + } + + (function(){ + //IE has some trouble with focusout with select and keyboard navigation + var activeFocus = null; + + $(document) + .bind('focusin', function(e){ + var target = e.realTarget || e.target; + if (activeFocus && activeFocus !== target) { + e.type = 'focusout'; + $(activeFocus).trigger(e); + e.type = 'focusin'; + e.target = target; + } + activeFocus = target; + }) + .bind('focusout', function(e){ + activeFocus = null; + }); + })(); + +})(jQuery); \ No newline at end of file diff --git a/thirdparty/jquery-entwine/src/jquery.selector.affectedby.js b/thirdparty/jquery-entwine/src/jquery.selector.affectedby.js new file mode 100755 index 000000000..0744faef7 --- /dev/null +++ b/thirdparty/jquery-entwine/src/jquery.selector.affectedby.js @@ -0,0 +1,58 @@ +(function($) { + + // TODO: + // Make attributes & IDs work + + var DIRECT = /DIRECT/g; + var CONTEXT = /CONTEXT/g; + var EITHER = /DIRECT|CONTEXT/g; + + $.selector.SelectorBase.addMethod('affectedBy', function(props) { + this.affectedBy = new Function('props', ([ + 'var direct_classes, context_classes, direct_attrs, context_attrs, t;', + this.ABC_compile().replace(DIRECT, 'direct').replace(CONTEXT, 'context'), + 'return {classes: {context: context_classes, direct: direct_classes}, attrs: {context: context_attrs, direct: direct_attrs}};' + ]).join("\n")); + + // DEBUG: Print out the compiled funciton + // console.log(this.selector, ''+this.affectedBy); + + return this.affectedBy(props); + }); + + $.selector.SimpleSelector.addMethod('ABC_compile', function() { + var parts = []; + + $.each(this.classes, function(i, cls){ + parts[parts.length] = "if (t = props.classes['"+cls+"']) (DIRECT_classes || (DIRECT_classes = {}))['"+cls+"'] = t;"; + }); + + $.each(this.nots, function(i, not){ + parts[parts.length] = not.ABC_compile(); + }); + + return parts.join("\n"); + }); + + $.selector.Selector.addMethod('ABC_compile', function(arg){ + var parts = []; + var i = this.parts.length-1; + + parts[parts.length] = this.parts[i].ABC_compile(); + while ((i = i - 2) >= 0) parts[parts.length] = this.parts[i].ABC_compile().replace(EITHER, 'CONTEXT'); + + return parts.join("\n"); + }); + + $.selector.SelectorsGroup.addMethod('ABC_compile', function(){ + var parts = []; + + $.each(this.parts, function(i,part){ + parts[parts.length] = part.ABC_compile(); + }); + + return parts.join("\n"); + }); + + +})(jQuery);