MINOR Upgraded jQuery.entwine (formerly known as jQuery.concrete) to the latest trunk

MINOR Updated jQuery.concrete references to point to the new "entwine" name

git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@102695 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
Ingo Schommer 2010-04-13 05:45:29 +00:00
parent ecc73e0cfc
commit 8256228e69
114 changed files with 3134 additions and 623 deletions

View File

@ -28,7 +28,7 @@ class CalendarDateField extends DateField {
}
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/dist/jquery.concrete-dist.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js');
// javascript: custom
Requirements::javascript(SAPPHIRE_DIR . '/javascript/CalendarDateField.js');

View File

@ -58,7 +58,7 @@ class TabSet extends CompositeField {
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/ui.all.css');
Requirements::css(SAPPHIRE_DIR . '/thirdparty/jquery-ui-themes/smoothness/ui.tabs.css');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/dist/jquery.concrete-dist.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js');
Requirements::javascript(SAPPHIRE_DIR . '/javascript/TabSet.js');

View File

@ -1,5 +1,5 @@
(function($) {
$.concrete('ss', function($){
$.entwine('ss', function($){
/**
* Formats a <input type="text"> field with a jQuery UI datepicker.
*
@ -7,7 +7,7 @@
*
* @author Ingo Schommer, SilverStripe Ltd.
*/
$('.calendardate').concrete({
$('.calendardate').entwine({
onmatch: function() {
this.find('input').each(function() {
var conf = $(this).metadata();

View File

@ -1,5 +1,5 @@
(function($){
$.concrete('ss', function($){
$.entwine('ss', function($){
/**
* Lightweight wrapper around jQuery UI tabs.
* Ensures that anchor links are set properly,
@ -7,7 +7,7 @@
* their height explicitly set. This is important
* for forms inside the CMS layout.
*/
$('.ss-tabset').concrete({
$('.ss-tabset').entwine({
onmatch: function() {
this.rewriteHashlinks();

View File

@ -349,7 +349,7 @@ class Security extends Controller {
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/ui.core.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-ui/ui.tabs.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-concrete/dist/jquery.concrete-dist.js');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery-entwine/dist/jquery.entwine-dist.js');
Requirements::css(THIRDPARTY_DIR . '/jquery/themes/smoothness/ui.all.css');
Requirements::css(THIRDPARTY_DIR . '/jquery/themes/smoothness/ui.tabs.css');

Binary file not shown.

View File

@ -1,2 +0,0 @@
vendor/
spec/

View File

@ -1,8 +0,0 @@
---
format: 1
handler:
commit: bae4684bd5ab659bd78ebd990cc68ecba8a36669
branch: master
lock: false
repository_class: Piston::Git::Repository
repository_url: git://github.com/hafriedlander/jquery.concrete.git

View File

@ -1,77 +0,0 @@
(function($) {
var concrete_prepend = '__concrete!';
var getConcreteData = function(el, namespace, property) {
return el.data(concrete_prepend + namespace + '!' + property);
}
var setConcreteData = function(el, namespace, property, value) {
return el.data(concrete_prepend + namespace + '!' + property, value);
}
var getConcreteDataAsHash = function(el, namespace) {
var hash = {};
var id = jQuery.data(el[0]);
var matchstr = concrete_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 setConcreteDataFromHash = function(el, namespace, hash) {
for (var k in hash) setConcreteData(namespace, k, hash[k]);
}
var concreteData = function(el, namespace, args) {
switch (args.length) {
case 0:
return getConcreteDataAsHash(el, namespace);
case 1:
if (typeof args[0] == 'string') return getConcreteData(el, namespace, args[0]);
else return setConcreteDataFromHash(el, namespace, args[0]);
default:
return setConcreteData(el, namespace, args[0], args[1]);
}
}
$.extend($.fn, {
concreteData: function() {
return concreteData(this, '__base', arguments);
}
});
$.concrete.Namespace.addHandler({
order: 60,
bind: function(selector, k, v) {
if (k.charAt(0) != k.charAt(0).toUpperCase()) $.concrete.warn('Concrete property '+k+' does not start with a capital letter', $.concrete.WARN_LEVEL_BESTPRACTISE);
var namespace = this;
g = function() { return this.concreteData(k) || v ; }
s = function(v){ return this.concreteData(k, v); }
g.pname = s.pname = k;
this.bind_proxy(selector, 'get'+k, g);
this.bind_proxy(selector, 'set'+k, s);
return true;
},
namespaceMethodOverrides: function(namespace){
return {
concreteData: function() {
return concreteData(this, namespace.name, arguments);
}
};
}
});
})(jQuery);

View File

@ -1,114 +0,0 @@
/*
* Provides a per-node data store that is automatically cloned when dom node is clone() or cloneNode()'d.
*/
(function($){
var data_store = {};
var check_name = 'com.silverstripe:check_id';
var expando = 'data_id:';
var id = 0;
/**
* Clone a data object. Currently uses jQuery's deep copy routine
*/
var cloneData = function(data) {
return $.extend(true, {}, data);
}
/**
* Set the data object for an element.
* Picks a new ID, sets the tracking attribute and value on the DOM node and stores data in the data_store
*
* @param {jQuery selection} el - The element to store this data on
* @param {Object} data - The object to use as data, or undefined / null for an empty object
*/
var setData = function(el, data) {
if (!data) data = {};
id += 1;
var data_id = expando + id;
el.attr('data', data_id); el.data(check_name, data_id);
return data_store[data_id] = data;
}
/**
* Delete the data object for an element
* It's important this is called when the related element is deled, or memory could leak. We monkey-patch jQuery.removeData to make sure this happens
* @param {jQuery selection} el - The element to remove the data for
*/
var clearData = function(el) {
var data_id = el.attr('data');
if (!data_id) return;
el.removeAttr('data');
// Only remove the data if this is the last element with a data reference to it. This is so removing an element
// doesn't delete the data before any cloned elements have a chance to copy it
if ($('[data='+data_id+']').length == 0) delete data_store[data_id];
}
/**
* Get the data object for an element
* Sets an empty data object if the element does not have one yet
* Clones the data object if the element it's attached to has been cloned
* @param {jQuery selection} el - The element to retrieve the data of
*/
var getData = function(el) {
// If the data_id is missing, the element has no data
var data_id = el.attr('data');
if (!data_id) return setData(el);
var check_id = el.data(check_name);
if (!check_id || check_id != data_id) {
// If the check_id is missing, the element has been cloned. So clone the data too
var newdata = cloneData(data_store[data_id]);
setData(el, newdata);
// If we were the last element holding on to a reference to that old data, delete it now that we're done with it
if ($('[data='+data_id+']').length == 0) delete data_store[data_id];
return newdata;
}
// Otherwise, this element has some data, so return it
return data_store[data_id];
}
$.dat = {};
/**
* Check all data in data_store, removing any that are not longer referenced in the DOM
* Returns number of garbage-collected entries, for finding memory leaks
*/
$.dat.vacuum = function() {
var i = 0;
for (var k in data_store) {
if ($('[data='+k+']').length == 0) { delete data_store[k]; i++; }
}
return i;
}
/**
* Return count of items in data_store.
* Used in tests
*/
$.dat.size = function() {
var i = 0;
for (var k in data_store) i++;
return i;
}
/**
* Get the data object for the current element
*/
$.fn.d = function(a){
return getData(this.eq(0));
};
// Monkey patch removeData to also remove dat
var removeData_without_dat = $.removeData
$.removeData = function(elem, name) {
if (!name) clearData($(elem));
return removeData_without_dat.apply(this, arguments);
}
})(jQuery);

8
thirdparty/jquery-entwine/.piston.yml vendored Normal file
View File

@ -0,0 +1,8 @@
---
format: 1
handler:
commit: f09e97da777031a67e136945f8732890c84e3e65
branch: master
lock: false
repository_class: Piston::Git::Repository
repository_url: git://github.com/hafriedlander/jquery.entwine.git

View File

@ -1,25 +1,29 @@
h1. Concrete - Support for ConcreteUI programming in jQuery
h1. Entwine - Support for Concrete UI style programming in jQuery
By Hamish Friedlander, with thanks to "SilverStripe":http://www.silverstripe.com/
Concrete tries to provide a new model of code organisation - a replacement for Object Oriented programming that is focused on adding functions to groups of DOM elements based on the structure and contents of those DOM elements. It's a merging of the model and view layer that initially seems weird, but can give very powerful results.
Entwine tries to provide a new model of code organisation - a replacement for Object Oriented programming that is focused on adding functions to groups of DOM elements based on the structure and contents of those DOM elements. It's a merging of the model and view layer that initially seems weird, but can give very powerful results.
We're standing on the shoulders of giants here - combining ideas from Prototype's behaviour & lowpro and jQuery's effen & livequery (who themselves stole ideals from Self's Morphic UI and others), but extending & combining the concepts presented in those tools to provide a complete alternative to traditional OO concepts - self-aware methods, inheritance, polymorphisim and namespacing without a single class definition.
h2. Getting Started
* Walk through the "Tutorial":http://hafriedlander.github.com/jquery.concrete/tutorial/
* Walk through the "Tutorial":http://hafriedlander.github.com/jquery.entwine/tutorial/
* Watch the "Screencast":http://www.vimeo.com/6353390 (shot during a introductory developer meeting at SilverStripe)
* Join the "Google Group":http://groups.google.com/group/jquery-concrete and let us know what you think, or what other features you'd like to see
* Join the "Google Group":http://groups.google.com/group/jquery-entwine and let us know what you think, or what other features you'd like to see
h2. Name change
jQuery Entwine used to be called jQuery Concrete. The name was changed to avoid confusion with another product. The concrete function remains as an alias, but all new code should use entwine
h2. Basic use
h4. First intro
To attach methods to DOM nodes, call the `concrete` function on a jQuery selector object, passing a hash listing the method names and bodys
To attach methods to DOM nodes, call the `entwine` function on a jQuery selector object, passing a hash listing the method names and bodys
<pre><code>
$('div').concrete({
$('div').entwine({
foo: function(..){..},
bar: function(..){..}
});
@ -46,13 +50,13 @@ Given this DOM structure:
</body>
</code></pre>
And this concrete definition
And this entwine definition
<pre><code>
$('.internal_text').concrete({
$('.internal_text').entwine({
foo: function(){ console.log(this.text()); }
});
$('.attribute_text').concrete({
$('.attribute_text').entwine({
foo: function(){ console.log(this.attr('rel')); }
});
</code></pre>
@ -72,12 +76,12 @@ Will log this to the console
h4. Limitations
When defining methods, the jQuery object that concrete is called on must be a plain selector, without context. These examples will not work
When defining methods, the jQuery object that entwine is called on must be a plain selector, without context. These examples will not work
<pre><code>
$('div', el).concrete(...)
$([ela, elb, elc]).concrete(...)
$('<div id="a"></div>').concrete(...)
$('div', el).entwine(...)
$([ela, elb, elc]).entwine(...)
$('<div id="a"></div>').entwine(...)
</code></pre>
h2. Live
@ -100,13 +104,13 @@ Another example. Given this DOM structure
</body>
</code></pre>
And this concrete definition
And this entwine definition
<pre><code>
$('div').concrete({
$('div').entwine({
foo: function(){ console.log(this.text()); }
});
$('.attribute_text').concrete({
$('.attribute_text').entwine({
foo: function(){ console.log(this.attr('rel')); }
});
</code></pre>
@ -134,10 +138,10 @@ name. Just like other functions this binding will be live, and only the most spe
<head>
<script type='text/javascript'>
/* No need for onready wrapper. Events are bound as needed */
$('div').concrete({
$('div').entwine({
onclick: function(){ this.css({backgroundColor: 'blue'}); }
});
$('.green').concrete({
$('.green').entwine({
onclick: function(){ this.css({color: 'green'}); }
});
</script>
@ -153,7 +157,7 @@ h2. Constructors / Destructors
Declaring a function with the name `onmatch` will create a behavior that is called on each object when it matches. Likewise, `onunmatch` will
be called when an object that did match this selector stops matching it (because it is removed, or because you've changed its properties).
Note that an onunmatch block must be paired with an onmatch block - an onunmatch without an onmatch _in the same concrete definition block_ is illegal
Note that an onunmatch block must be paired with an onmatch block - an onunmatch without an onmatch _in the same entwine definition block_ is illegal
Like other functions, only the most specific definition will be used. However, because property changes are not atomic, this may not work as you
expect.
@ -163,8 +167,8 @@ h2. Namespaces
To avoid name clashes, to allow multiple bindings to the same event, and to generally seperate a set of functions from other code you can use namespaces
<pre><code>
$.concrete('foo.bar', function($){
$('div').concrete({
$.entwine('foo.bar', function($){
$('div').entwine({
baz: function(){}
});
});
@ -173,19 +177,19 @@ To avoid name clashes, to allow multiple bindings to the same event, and to gene
You can then call these functions like this:
<pre><code>
$('div').concrete('foo.bar').baz()
$('div').entwine('foo.bar').baz()
</code></pre>
Namespaced functions work just like regular functions (`this` is still set to a matching DOM Node). However, specifity is calculated per namespace.
This is particularly useful for events, because given this:
<pre><code>
$('div').concrete({
$('div').entwine({
onclick: function(){ this.css({backgroundColor: 'blue'}); }
});
$.concrete('foo', function($){
$('div').concrete({
$.entwine('foo', function($){
$('div').entwine({
onclick: function(){ this.css({color: 'green'}); }
});
});
@ -203,14 +207,14 @@ Inside a namespace definition, functions remember the namespace they are in, and
Where they don't exist, they will be looked up in the base namespace
<pre><code>
$.concrete('foo', function($){
$('div').concrete({
$.entwine('foo', function($){
$('div').entwine({
bar: function() { this.baz(); this.qux(); }
baz: function() { console.log('baz'); }
})
})
$('div').concrete({
$('div').entwine({
qux: function() { console.log('qux'); }
})
</code></pre>
@ -223,20 +227,20 @@ Note that 'exists' means that a function is declared in this namespace for _any_
<div>Internal text</div>
</code></pre>
And the concrete definitions
And the entwine definitions
<pre><code>
$.concrete('foo', function($){
$('div').concrete({
$.entwine('foo', function($){
$('div').entwine({
bar: function() { this.baz(); }
});
$('span').concrete({
$('span').entwine({
baz: function() { console.log('a'); }
});
})
$('div').concrete({
$('div').entwine({
baz: function() { console.log('b'); }
})
</code></pre>
@ -245,15 +249,15 @@ Then doing $('div').bar(); will _not_ display b. Even though the span rule could
h4. Nesting namespace blocks
You can also nest declarations. In this next example, we're defining the functions $().concrete('zap').bar() and $().concrete('zap.pow').baz()
You can also nest declarations. In this next example, we're defining the functions $().entwine('zap').bar() and $().entwine('zap.pow').baz()
<pre><code>
$.concrete('zap', function($){
$('div').concrete({
$.entwine('zap', function($){
$('div').entwine({
bar: function() { .. }
})
$.concrete('pow', function($){
$('div').concrete({
$.entwine('pow', function($){
$('div').entwine({
baz: function() { .. }
})
})
@ -264,12 +268,12 @@ h4. Calling to another namespace (and forcing base)
Inside a namespace, namespace lookups are by default relative to the current namespace.
In some situations (such as the last example) you may want to force using the base namespace. In this case you can call concrete with the first argument being the base namespace code '.'. For example, if the first definition in the previous example was
In some situations (such as the last example) you may want to force using the base namespace. In this case you can call entwine with the first argument being the base namespace code '.'. For example, if the first definition in the previous example was
<pre><code>
$.concrete('foo', function($){
$('div').concrete({
bar: function() { this.concrete('.').baz(); }
$.entwine('foo', function($){
$('div').entwine({
bar: function() { this.entwine('.').baz(); }
})
})
</code></pre>
@ -278,16 +282,16 @@ Then b _would_ be output to the console.
h4. Using
Sometimes a block outside of a namespace will need to refer to that namespace repeatedly. By passing a function to the concrete function, you can change the looked-up namespace
Sometimes a block outside of a namespace will need to refer to that namespace repeatedly. By passing a function to the entwine function, you can change the looked-up namespace
<pre><code>
$.concrete('foo', function($){
$('div').concrete({
$.entwine('foo', function($){
$('div').entwine({
bar: function() { console.log('a'); }
})
})
$('div').concrete('foo', function(){
$('div').entwine('foo', function(){
this.bar();
this.bar();
this.bar();
@ -296,6 +300,36 @@ Sometimes a block outside of a namespace will need to refer to that namespace re
This equivalent to /with/ in javascript, and just like /with/, care should be taken to only use this construct in situations that merit it.
h2. Tests
Specs are written using the awesome "JSpec":http://github.com/visionmedia/jspec library. You can run them in two ways
h4. Ad-hoc
Open the file `spec/spec.html` in any modern browser
h4. Continuous testing
JSpec has a command line client which can be used for continuous testing. Make sure ruby is installed and enabled the "Gemcutter":http://gemcutter.org/ gem hosting service, like so:
<pre><code>
sudo gem install gemcutter
sudo gem tumble
</code></pre>
Then install the jspec binary:
<pre><code>
sudo gem install jspec
</code></pre>
The JSpec command line tool should now be installed. This command will re-run the specs whenever you edit a file:
<pre><code>
jspec spec/spec.html -p src,spec
</code></pre>
h2. License
Copyright (C) 2009 Hamish Friedlander (hamish@silverstripe.com) and SilverStripe Limited (www.silverstripe.com). All rights reserved.

View File

@ -1,12 +1,12 @@
#!/bin/sh
# Specify the output file's name
FILE="dist/jquery.concrete-dist.js"
FILE="dist/jquery.entwine-dist.js"
mkdir -p dist
rm dist/*.js
echo "/* jQuery.Concrete - Copyright 2009 Hamish Friedlander and SilverStripe. Version $VER. */" > $FILE
echo "/* jQuery.Entwine - Copyright 2009 Hamish Friedlander and SilverStripe. Version $VER. */" > $FILE
for x in \
vendor/jquery.selector/jquery.class.js \
@ -14,11 +14,12 @@ for x in \
vendor/jquery.selector/jquery.selector.specifity.js \
vendor/jquery.selector/jquery.selector.matches.js \
src/jquery.focusinout.js \
src/jquery.concrete.js \
src/jquery.concrete.dommaybechanged.js \
src/jquery.concrete.events.js \
src/jquery.concrete.ctors.js \
src/jquery.concrete.properties.js
src/jquery.entwine.js \
src/jquery.entwine.dommaybechanged.js \
src/jquery.entwine.events.js \
src/jquery.entwine.ctors.js \
src/jquery.entwine.properties.js \
src/jquery.entwine.legacy.js
do \
echo >> $FILE
echo "/* $x */" >> $FILE

View File

@ -1,4 +1,4 @@
/* jQuery.Concrete - Copyright 2009 Hamish Friedlander and SilverStripe. Version . */
/* jQuery.Entwine - Copyright 2009 Hamish Friedlander and SilverStripe. Version . */
/* vendor/jquery.selector/jquery.class.js */
@ -16,17 +16,18 @@ var Base;
(function(){
var marker = {}, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
var marker = {}, fnTest = /xyz/.test(function(){var xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
Base = function(){};
Base.addMethod = function(name, func) {
var _super = this._super;
if (_super && fnTest.test(func)) {
var parent = this._super && this._super.prototype;
if (parent && fnTest.test(func)) {
this.prototype[name] = function(){
var tmp = this._super;
this._super = _super[name];
this._super = parent[name];
try {
var ret = func.apply(this, arguments);
}
@ -34,17 +35,25 @@ var Base;
this._super = tmp;
}
return ret;
}
};
}
else this.prototype[name] = func;
}
};
Base.addMethods = function(props) {
for (var name in props) {
if (typeof props[name] == 'function') this.addMethod(name, props[name]);
else this.prototype[name] = props[name];
}
}
};
Base.subclassOf = function(parentkls) {
var kls = this;
while (kls) {
if (kls === parentkls) return true;
kls = kls._super;
}
};
// Create a new Class that inherits from this class
Base.extend = function(props) {
@ -59,18 +68,21 @@ var Base;
else {
var ret = new Kls(marker); if (ret.init) ret.init.apply(ret, arguments); return ret;
}
}
};
// Add the common class variables and methods
Kls.constructor = Kls;
Kls.extend = Base.extend;
Kls.addMethod = Base.addMethod;
Kls.addMethods = Base.addMethods;
Kls._super = this.prototype;
Kls.subclassOf = Base.subclassOf;
Kls._super = this;
// Attach the parent object to the inheritance chain
Kls.prototype = new this(marker);
Kls.prototype.constructor = Kls;
// Copy the properties over onto the new prototype
Kls.addMethods(props);
@ -104,7 +116,7 @@ var Base;
WITHN: /([-+])?(INTEGER)?(n)\s*(?:([-+])\s*(INTEGER))?/,
WITHOUTN: /([-+])?(INTEGER)/
}
};
var rx = {
not: /:not\(/,
@ -121,12 +133,12 @@ var Base;
comb: /\s*(\+|~|>)\s*|\s+/,
comma: /\s*,\s*/,
important: /\s+!important\s*$/
}
};
/* Replace placeholders with actual regex, and mark all as case insensitive */
var token = /[A-Z][A-Z0-9]+/;
for (var k in rx) {
var src = rx[k].source;
var m, src = rx[k].source;
while (m = src.match(token)) src = src.replace(m[0], tokens[m[0]].source);
rx[k] = new RegExp(src, 'gi');
}
@ -160,7 +172,7 @@ var Base;
done: function() {
return this.pos == this.str.length;
}
})
});
/* A base class that all Selectors inherit off */
var SelectorBase = Base.extend({});
@ -187,7 +199,7 @@ var Base;
/* Then for each selection type, try and find a match */
do {
if (m = selector.match(rx.not)) {
this.nots[this.nots.length] = SelectorsGroup().parse(selector)
this.nots[this.nots.length] = SelectorsGroup().parse(selector);
if (!(m = selector.match(rx.not_end))) {
throw 'Invalid :not term in selector';
}
@ -213,7 +225,7 @@ var Base;
return this;
}
})
});
/**
* A class representing a Selector, as per the CSS3 selector spec
@ -261,14 +273,14 @@ var Base;
if (!cons.done()) throw 'Could not parse selector - ' + cons.showpos() ;
else return res;
}
};
$.selector.SelectorBase = SelectorBase;
$.selector.SimpleSelector = SimpleSelector;
$.selector.Selector = Selector;
$.selector.SelectorsGroup = SelectorsGroup;
})(jQuery)
})(jQuery);
;
@ -289,7 +301,7 @@ var Base;
});
return this.spec = spec;
})
});
$.selector.Selector.addMethod('specifity', function(){
if (this.spec) return this.spec;
@ -301,7 +313,7 @@ var Base;
});
return this.spec = spec;
})
});
$.selector.SelectorsGroup.addMethod('specifity', function(){
if (this.spec) return this.spec;
@ -312,7 +324,7 @@ var Base;
});
return this.spec = spec;
})
});
})(jQuery);
@ -341,16 +353,16 @@ 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_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' };
var testFunc = function(){ return 'good'; };
if ((new Function('',funcToString(testFunc)))() != 'good') funcToString = false;
}
catch(e) { funcToString = false; console.log(e.message);/*pass*/ }
@ -364,21 +376,26 @@ Sizzle is good for finding elements for a selector, but not so good for telling
var join = function(js) {
return js.join('\n');
}
};
var join_complex = function(js) {
code = new String(js.join('\n')); // String objects can have properties set. strings can't
var code = new String(js.join('\n')); // String objects can have properties set. strings can't
code.complex = true;
return code;
}
};
/**** ATTRIBUTE ACCESSORS ****/
// Not all attribute names can be used as identifiers, so we encode any non-acceptable characters as hex
var varForAttr = function(attr) {
return '_' + attr.replace(/^[^A-Za-z]|[^A-Za-z0-9]/g, function(m){ return '_0x' + m.charCodeAt(0).toString(16) + '_'; });
};
var getAttr;
// Good browsers
if (!getAttributeDodgy) {
getAttr = function(attr){ return 'var _'+attr+' = el.getAttribute("'+attr+'");' ; }
getAttr = function(attr){ return 'var '+varForAttr(attr)+' = el.getAttribute("'+attr+'");' ; };
}
// IE 6, 7
else {
@ -387,21 +404,21 @@ Sizzle is good for finding elements for a selector, but not so good for telling
getAttr = function(attr) {
var ieattr = getAttrIEMap[attr] || attr;
return 'var _'+attr+' = el.getAttribute("'+ieattr+'",2) || (el.getAttributeNode("'+attr+'")||{}).nodeValue;';
}
return 'var '+varForAttr(attr)+' = el.getAttribute("'+ieattr+'",2) || (el.getAttributeNode("'+attr+'")||{}).nodeValue;';
};
}
/**** ATTRIBUTE COMPARITORS ****/
var attrchecks = {
'-': '!_K',
'=': '_K != "V"',
'!=': '_K == "V"',
'~=': '__K.indexOf(" V ") == -1',
'^=': '!_K || _K.indexOf("V") != 0',
'*=': '!_K || _K.indexOf("V") == -1',
'$=': '!_K || _K.substr(_K.length-"V".length) != "V"'
}
'-': '!K',
'=': 'K != "V"',
'!=': 'K == "V"',
'~=': '_WS_K.indexOf(" V ") == -1',
'^=': '!K || K.indexOf("V") != 0',
'*=': '!K || K.indexOf("V") == -1',
'$=': '!K || K.substr(K.length-"V".length) != "V"'
};
/**** STATE TRACKER ****/
@ -429,7 +446,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
return join([
'while(el = el.previousSibling){',
'if (el.nodeType != 1) continue;',
body,
body
]);
},
parent: function() {
@ -453,7 +470,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
uses_wsattr: function(attr) {
if (this.wsattrs[attr]) return;
this.wsattrs[attr] = true;
return join([this.uses_attr(attr), 'var __'+attr+' = " "+_'+attr+'+" ";']);
return join([this.uses_attr(attr), 'var _WS_'+varForAttr(attr)+' = " "+'+varForAttr(attr)+'+" ";']);
},
save: function(lbl) {
@ -470,7 +487,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
var pseudoclschecks = {
'first-child': join([
'var cel = el;',
'while(cel = cel.previousSibling){ if (cel.nodeType === 1) BAD; }',
'while(cel = cel.previousSibling){ if (cel.nodeType === 1) BAD; }'
]),
'last-child': join([
'var cel = el;',
@ -481,7 +498,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
'var i = 1, cel = el;',
'while(cel = cel.previousSibling){',
'if (cel.nodeType === 1) i++;',
'}',
'}'
]);
if (a == 0) return join([
@ -530,15 +547,15 @@ Sizzle is good for finding elements for a selector, but not so good for telling
/* Check against class names */
$.each(this.classes, function(i, cls){
js[js.length] = 'if (__class.indexOf(" '+cls+' ") == -1) BAD;';
})
js[js.length] = 'if (_WS__class.indexOf(" '+cls+' ") == -1) BAD;';
});
}
/* Check against attributes */
$.each(this.attrs, function(i, attr){
js[js.length] = (attr[1] == '~=') ? el.uses_wsattr(attr[0]) : el.uses_attr(attr[0]);
var check = attrchecks[ attr[1] || '-' ];
check = check.replace( /K/g, attr[0]).replace( /V/g, attr[2] && attr[2].match(STARTS_WITH_QUOTES) ? attr[2].slice(1,-1) : attr[2] );
check = check.replace( /K/g, varForAttr(attr[0])).replace( /V/g, attr[2] && attr[2].match(STARTS_WITH_QUOTES) ? attr[2].slice(1,-1) : attr[2] );
js[js.length] = 'if ('+check+') BAD;';
});
@ -555,7 +572,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
el.save(lbl),
func,
el.restore(lbl)
])
]);
js[js.length] = func;
});
@ -571,7 +588,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
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] = 'if (!$.find.selectors.filters.'+pscls[0]+'(el)) BAD;';
}
}
});
@ -589,7 +606,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
return join([
'l'+(++lbl_id)+':{',
f.replace(GOOD, 'break l'+lbl_id),
'}',
'}'
]);
else
return f.replace(GOOD, '');
@ -637,11 +654,11 @@ Sizzle is good for finding elements for a selector, but not so good for telling
};
$.selector.Selector.addMethod('compile', function(el) {
l = this.parts.length;
var l = this.parts.length;
expr = this.parts[--l].compile(el);
var expr = this.parts[--l].compile(el);
while (l) {
combinator = this.parts[--l];
var combinator = this.parts[--l];
expr = combines[combinator](el, this.parts[--l], as_subexpr(expr));
}
@ -730,7 +747,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
})(jQuery);;
/* src/jquery.concrete.js */
/* src/jquery.entwine.js */
var console;
@ -738,28 +755,30 @@ var console;
var namespaces = {};
$.concrete = function() {
$.fn.concrete.apply(null, arguments);
}
$.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($.concrete, {
$.extend($.entwine, {
/**
* Get all the namespaces. Useful for introspection? Internal interface of Namespace not guaranteed consistant
*/
namespaces: namespaces,
/**
* Remove all concrete rules
* Remove all entwine rules
*/
clear_all_rules: function() {
// Remove proxy functions
for (var k in $.fn) { if ($.fn[k].concrete) delete $.fn[k] ; }
for (var k in $.fn) { if ($.fn[k].entwine) delete $.fn[k] ; }
// Remove bound events - TODO: Make this pluggable, so this code can be moved to jquery.entwine.events.js
$(document).unbind('.entwine');
// Remove namespaces, and start over again
namespaces = $.concrete.namespaces = {};
namespaces = $.entwine.namespaces = {};
},
WARN_LEVEL_NONE: 0,
@ -773,10 +792,21 @@ var console;
/** Utility to optionally display warning messages depending on level */
warn: function(message, level) {
if (level <= $.concrete.warningLevel && console && console.log) {
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);
}
}
});
@ -802,9 +832,9 @@ var console;
(as[1] - bs[1]) ||
(as[2] - bs[2]) ||
(a.rulecount - b.rulecount) ;
}
};
$.concrete.RuleList = function() {
$.entwine.RuleList = function() {
var list = [];
list.addRule = function(selector, name){
@ -817,16 +847,16 @@ var console;
};
return list;
}
};
var handlers = [];
/**
* A Namespace holds all the information needed for adding concrete methods to a namespace (including the _null_ namespace)
* A Namespace holds all the information needed for adding entwine methods to a namespace (including the _null_ namespace)
*/
$.concrete.Namespace = Base.extend({
$.entwine.Namespace = Base.extend({
init: function(name){
if (name && !name.match(/^[A-Za-z0-9.]+$/)) $.concrete.warn('Concrete namespace '+name+' is not formatted as period seperated identifiers', $.concrete.WARN_LEVEL_BESTPRACTISE);
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;
@ -835,12 +865,12 @@ var console;
namespaces[name] = this;
if (name == "__base") {
this.injectee = $.fn
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
var subfn = function(){}
var subfn = function(){};
this.injectee = subfn.prototype = new $();
// And then we provide an overriding $ that returns objects of our new Class, and an overriding pushStack to catch further selection building
@ -854,7 +884,7 @@ var console;
rv.selector = jq.selector; rv.context = jq.context; var i = rv.length = jq.length;
while (i--) rv[i] = jq[i];
return rv;
}
};
this.injectee.pushStack = function(elems, name, selector){
var ret = bound$(elems);
@ -867,25 +897,25 @@ var console;
// Return the newly-formed element set
return ret;
}
};
// Copy static functions through from $ to this.$ so e.g. $.ajax still works
// @bug, @cantfix: Any class functions added to $ after this call won't get mirrored through
$.extend(this.$, $);
// We override concrete to inject the name of this namespace when defining blocks inside this namespace
var concrete_wrapper = this.injectee.concrete = function(spacename) {
// 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.concrete.apply(this, args);
}
return $.fn.entwine.apply(this, args);
};
this.$.concrete = function() {
concrete_wrapper.apply(null, arguments);
}
this.$.entwine = function() {
entwine_wrapper.apply(null, arguments);
};
for (var i = 0; i < handlers.length; i++) {
var handler = handlers[i], builder;
@ -896,10 +926,10 @@ var console;
for (var k in overrides) this.injectee[k] = overrides[k];
}
// Inject $.concrete function overrides
// Inject $.entwine function overrides
if (builder = handler.namespaceStaticOverrides) {
var overrides = builder(this);
for (var k in overrides) this.$.concrete[k] = overrides[k];
for (var k in overrides) this.$.entwine[k] = overrides[k];
}
}
}
@ -910,8 +940,9 @@ var console;
* 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) {
one: function(name, funcprop, basefunc) {
var namespace = this;
var funcs = this.store[name];
@ -926,7 +957,9 @@ var console;
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;
},
@ -935,9 +968,10 @@ var console;
* 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) {
var one = this.one(name, 'func');
build_proxy: function(name, basefunc) {
var one = this.one(name, 'func', basefunc);
var prxy = function() {
var rv, ctx = $(this);
@ -951,17 +985,17 @@ var console;
},
bind_proxy: function(selector, name, func) {
var rulelist = this.store[name] || (this.store[name] = $.concrete.RuleList());
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_proxy(name);
this.injectee[name].concrete = true;
if (!this.injectee.hasOwnProperty(name) || !this.injectee[name].entwine) {
this.injectee[name] = this.build_proxy(name, this.injectee.hasOwnProperty(name) ? this.injectee[name] : null);
this.injectee[name].entwine = true;
}
if (!this.injectee[name].concrete) {
$.concrete.warn('Warning: Concrete function '+name+' clashes with regular jQuery function - concrete function will not be callable directly on jQuery object', $.concrete.WARN_LEVEL_IMPORTANT);
if (!this.injectee[name].entwine) {
$.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);
}
},
@ -993,12 +1027,12 @@ var console;
* 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
*/
$.concrete.Namespace.addHandler = function(handler) {
$.entwine.Namespace.addHandler = function(handler) {
for (var i = 0; i < handlers.length && handlers[i].order < handler.order; i++) { /* Pass */ }
handlers.splice(i, 0, handler);
}
};
$.concrete.Namespace.addHandler({
$.entwine.Namespace.addHandler({
order: 50,
bind: function(selector, k, v){
@ -1011,20 +1045,22 @@ var console;
$.extend($.fn, {
/**
* Main concrete function. Used for new definitions, calling into a namespace (or forcing the base namespace) and entering a using block
* Main entwine function. Used for new definitions, calling into a namespace (or forcing the base namespace) and entering a using block
*
*/
concrete: function(spacename) {
entwine: function(spacename) {
var i = 0;
var selector = this.selector ? $.selector(this.selector) : null;
/* 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 || $.concrete.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] || $.concrete.Namespace(spacename);
if (spacename) namespace = namespaces[spacename] || $.entwine.Namespace(spacename);
i=1;
}
@ -1032,19 +1068,19 @@ var console;
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 concrete definition
// 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) $.concrete.warn('Function block inside concrete definition does not take $ argument properly', $.concrete.WARN_LEVEL_IMPORTANT);
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 concrete definition hash, inject it into 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 $.concrete.warn('Concrete block given to concrete call without selector. Make sure you call $(selector).concrete when defining blocks', $.concrete.WARN_LEVEL_IMPORTANT);
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 */
@ -1052,7 +1088,7 @@ var console;
},
/**
* Calls the next most specific version of the current concrete method
* Calls the next most specific version of the current entwine method
*/
_super: function(){
var rv, i = this.length;
@ -1068,7 +1104,7 @@ var console;
;
/* src/jquery.concrete.dommaybechanged.js */
/* src/jquery.entwine.dommaybechanged.js */
(function($){
@ -1082,9 +1118,9 @@ var console;
var triggerEvent = function() {
$(document).triggerHandler('DOMMaybeChanged');
check_id = null;
}
};
$.extend($.concrete, {
$.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
@ -1092,7 +1128,7 @@ var console;
*/
synchronous_mode: function() {
if (check_id) clearTimeout(check_id); check_id = null;
runSoon = function(func, delay){ func.call(this); return null; }
runSoon = function(func, delay){ func.call(this); return null; };
},
/**
@ -1111,8 +1147,8 @@ var console;
var rv = old.apply(this, arguments);
if (!check_id) check_id = runSoon(triggerEvent, 100);
return rv;
}
})
};
});
}
function registerSetterGetterFunction() {
@ -1122,8 +1158,8 @@ var console;
var rv = old.apply(this, arguments);
if (!check_id && (b !== undefined || typeof a != 'string')) check_id = runSoon(triggerEvent, 100);
return rv;
}
})
};
});
}
// Register core DOM manipulation methods
@ -1131,12 +1167,12 @@ var console;
registerSetterGetterFunction('attr');
// And on DOM ready, trigger matching once
$(function(){ triggerEvent(); })
$(function(){ triggerEvent(); });
})(jQuery);;
/* src/jquery.concrete.events.js */
/* src/jquery.entwine.events.js */
(function($) {
@ -1148,24 +1184,26 @@ var console;
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 */
$.concrete.Namespace.addMethods({
$.entwine.Namespace.addMethods({
build_event_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function(e, originalevent) {
e = originalevent || e;
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 != document && !e.isPropagationStopped()) {
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(); }
@ -1184,7 +1222,7 @@ var console;
var el = e.target;
var rel = e.relatedTarget;
while (el && el != document && !e.isPropagationStopped()) {
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;
@ -1232,7 +1270,7 @@ var console;
// And if we decided that a change happened, do the actual triggering
if (e.type == 'change') {
while (el && el != document && !e.isPropagationStopped()) {
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(); }
@ -1246,7 +1284,7 @@ var console;
},
bind_event: function(selector, name, func, event) {
var funcs = this.store[name] || (this.store[name] = $.concrete.RuleList()) ;
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;
@ -1268,19 +1306,22 @@ var console;
}
break;
case 'onsubmit':
event = 'delegated_submit';
event = 'delegatedSubmit';
break;
case 'onfocus':
case 'onblur':
$.concrete.warn('Event '+event+' not supported - using focusin / focusout instead', $.concrete.WARN_LEVEL_IMPORTANT);
$.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, proxies[name]);
$(document).bind(event+'.entwine', proxies[name]);
}
}
});
$.concrete.Namespace.addHandler({
$.entwine.Namespace.addHandler({
order: 40,
bind: function(selector, k, v){
@ -1293,10 +1334,14 @@ var console;
}
});
// Find all forms and bind onsubmit to trigger on the document too. This is the only event that can't be grabbed via delegation.
// 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){ $(document).triggerHandler('delegated_submit', e); } // The function that handles the delegation
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');
@ -1310,14 +1355,14 @@ var console;
;
/* src/jquery.concrete.ctors.js */
/* src/jquery.entwine.ctors.js */
(function($) {
/* Add the methods to handle constructor & destructor binding to the Namespace class */
$.concrete.Namespace.addMethods({
$.entwine.Namespace.addMethods({
bind_condesc: function(selector, name, func) {
var ctors = this.store.ctors || (this.store.ctors = $.concrete.RuleList()) ;
var ctors = this.store.ctors || (this.store.ctors = $.entwine.RuleList()) ;
var rule;
for (var i = 0 ; i < ctors.length; i++) {
@ -1342,17 +1387,19 @@ var console;
var tmp_i = el.i, tmp_f = el.f;
el.i = i; el.f = one;
try { func.call(namespace.$(el)); }
catch(e) { el.i = tmp_i; el.f = tmp_f; }
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;
}
}
});
$.concrete.Namespace.addHandler({
$.entwine.Namespace.addHandler({
order: 30,
bind: function(selector, k, v) {
@ -1375,29 +1422,35 @@ var console;
*/
$(document).bind('DOMMaybeChanged', function(){
// For every namespace
for (var k in $.concrete.namespaces) {
for (var k in $.entwine.namespaces) {
// That has constructors or destructors
var ctors = $.concrete.namespaces[k].store.ctors;
var ctors = $.entwine.namespaces[k].store.ctors;
if (ctors) {
// Keep a record of elements that have matched already
var matched = $([]), match, add, rem;
var matched = $([]), add, rem, res, rule, sel, ctor, dtor;
// Stepping through each selector from most to least specific
var j = ctors.length;
while (j--) {
// Build some quick-acccess variables
var sel = ctors[j].selector.selector, ctor = ctors[j].onmatch; dtor = ctors[j].onunmatch;
// Build some quick-access variables
rule = ctors[j];
sel = rule.selector.selector;
ctor = rule.onmatch;
dtor = rule.onunmatch;
// Get the list of elements that match this selector, that haven't yet matched a more specific selector
res = add = $(sel).not(matched);
// If this selector has a list of elements it matched against last time
if (ctors[j].cache) {
// 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(ctors[j].cache);
// Find the ones that are gone this time
rem = ctors[j].cache.not(res);
// And call the desctructor on them
if (rem.length && dtor) ctors.onunmatchproxy(rem, j, dtor);
add = res.not(rule.cache);
if (dtor) {
// Find the ones that are gone this time
rem = rule.cache.not(res);
// And call the destructor on them
if (rem.length) ctors.onunmatchproxy(rem, j, dtor);
}
}
// Call the constructor on the newly matched ones
@ -1410,32 +1463,32 @@ var console;
}
}
}
})
});
})(jQuery);
;
/* src/jquery.concrete.properties.js */
/* src/jquery.entwine.properties.js */
(function($) {
var concrete_prepend = '__concrete!';
var entwine_prepend = '__entwine!';
var getConcreteData = function(el, namespace, property) {
return el.data(concrete_prepend + namespace + '!' + property);
}
var getEntwineData = function(el, namespace, property) {
return el.data(entwine_prepend + namespace + '!' + property);
};
var setConcreteData = function(el, namespace, property, value) {
return el.data(concrete_prepend + namespace + '!' + property, value);
}
var setEntwineData = function(el, namespace, property, value) {
return el.data(entwine_prepend + namespace + '!' + property, value);
};
var getConcreteDataAsHash = function(el, namespace) {
var getEntwineDataAsHash = function(el, namespace) {
var hash = {};
var id = jQuery.data(el[0]);
var matchstr = concrete_prepend + namespace + '!';
var matchstr = entwine_prepend + namespace + '!';
var matchlen = matchstr.length;
var cache = jQuery.cache[id];
@ -1444,52 +1497,60 @@ var console;
}
return hash;
}
};
var setConcreteDataFromHash = function(el, namespace, hash) {
for (var k in hash) setConcreteData(namespace, k, hash[k]);
}
var setEntwineDataFromHash = function(el, namespace, hash) {
for (var k in hash) setEntwineData(namespace, k, hash[k]);
};
var concreteData = function(el, namespace, args) {
var entwineData = function(el, namespace, args) {
switch (args.length) {
case 0:
return getConcreteDataAsHash(el, namespace);
return getEntwineDataAsHash(el, namespace);
case 1:
if (typeof args[0] == 'string') return getConcreteData(el, namespace, args[0]);
else return setConcreteDataFromHash(el, namespace, args[0]);
if (typeof args[0] == 'string') return getEntwineData(el, namespace, args[0]);
else return setEntwineDataFromHash(el, namespace, args[0]);
default:
return setConcreteData(el, namespace, args[0], args[1]);
return setEntwineData(el, namespace, args[0], args[1]);
}
}
};
$.extend($.fn, {
concreteData: function() {
return concreteData(this, '__base', arguments);
entwineData: function() {
return entwineData(this, '__base', arguments);
}
});
$.concrete.Namespace.addHandler({
$.entwine.Namespace.addHandler({
order: 60,
bind: function(selector, k, v) {
if (k.charAt(0) != k.charAt(0).toUpperCase()) $.concrete.warn('Concrete property '+k+' does not start with a capital letter', $.concrete.WARN_LEVEL_BESTPRACTISE);
if (k.charAt(0) != k.charAt(0).toUpperCase()) $.entwine.warn('Entwine property '+k+' does not start with a capital letter', $.entwine.WARN_LEVEL_BESTPRACTISE);
var namespace = this;
g = function() { return this.concreteData(k) || v ; }
s = function(v){ return this.concreteData(k, v); }
// Create the getters and setters
g.pname = s.pname = k;
var getterName = 'get'+k;
var setterName = 'set'+k;
this.bind_proxy(selector, 'get'+k, g);
this.bind_proxy(selector, 'set'+k, s);
this.bind_proxy(selector, getterName, function() { return this.entwineData(k) || v ; });
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 {
concreteData: function() {
return concreteData(this, namespace.name, arguments);
entwineData: function() {
return entwineData(this, namespace.name, arguments);
}
};
}
@ -1498,3 +1559,29 @@ var console;
})(jQuery);
;
/* src/jquery.entwine.legacy.js */
(function($) {
// Adds back concrete methods for backwards compatibility
$.concrete = $.entwine;
$.fn.concrete = $.fn.entwine;
$.fn.concreteData = $.fn.entwineData;
// Use addHandler to hack in the namespace.$.concrete equivilent to the namespace.$.entwine namespace-injection
$.entwine.Namespace.addHandler({
order: 100,
bind: function(selector, k, v) { return false; },
namespaceMethodOverrides: function(namespace){
namespace.$.concrete = namespace.$.entwine;
namespace.injectee.concrete = namespace.injectee.entwine;
namespace.injectee.concreteData = namespace.injectee.entwineData;
return {};
}
});
})(jQuery);
;

View File

@ -0,0 +1,1587 @@
/* jQuery.Entwine - Copyright 2009 Hamish Friedlander and SilverStripe. Version . */
/* vendor/jquery.selector/jquery.class.js */
/**
* Very basic Class utility. Based on base and jquery.class.
*
* Class definition: var Foo = Base.extend({ init: function(){ Constructor }; method_name: function(){ Method } });
*
* Inheritance: var Bar = Foo.extend({ method_name: function(){ this._super(); } });
*
* new-less Constructor: new Foo(arg) <-same as-> Foo(arg)
*/
var Base;
(function(){
var marker = {}, fnTest = /xyz/.test(function(){var xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
Base = function(){};
Base.addMethod = function(name, func) {
var parent = this._super && this._super.prototype;
if (parent && fnTest.test(func)) {
this.prototype[name] = function(){
var tmp = this._super;
this._super = parent[name];
try {
var ret = func.apply(this, arguments);
}
finally {
this._super = tmp;
}
return ret;
};
}
else this.prototype[name] = func;
};
Base.addMethods = function(props) {
for (var name in props) {
if (typeof props[name] == 'function') this.addMethod(name, props[name]);
else this.prototype[name] = props[name];
}
};
Base.subclassOf = function(parentkls) {
var kls = this;
while (kls) {
if (kls === parentkls) return true;
kls = kls._super;
}
};
// Create a new Class that inherits from this class
Base.extend = function(props) {
// The dummy class constructor
var Kls = function() {
if (arguments[0] === marker) return;
if (this instanceof Kls) {
if (this.init) this.init.apply(this, arguments);
}
else {
var ret = new Kls(marker); if (ret.init) ret.init.apply(ret, arguments); return ret;
}
};
// Add the common class variables and methods
Kls.constructor = Kls;
Kls.extend = Base.extend;
Kls.addMethod = Base.addMethod;
Kls.addMethods = Base.addMethods;
Kls.subclassOf = Base.subclassOf;
Kls._super = this;
// Attach the parent object to the inheritance chain
Kls.prototype = new this(marker);
Kls.prototype.constructor = Kls;
// Copy the properties over onto the new prototype
Kls.addMethods(props);
return Kls;
};
})();;
/* vendor/jquery.selector/jquery.selector.js */
(function($){
var tokens = {
UNICODE: /\\[0-9a-f]{1,6}(?:\r\n|[ \n\r\t\f])?/,
ESCAPE: /(?:UNICODE)|\\[^\n\r\f0-9a-f]/,
NONASCII: /[^\x00-\x7F]/,
NMSTART: /[_a-z]|(?:NONASCII)|(?:ESCAPE)/,
NMCHAR: /[_a-z0-9-]|(?:NONASCII)|(?:ESCAPE)/,
IDENT: /-?(?:NMSTART)(?:NMCHAR)*/,
NL: /\n|\r\n|\r|\f/,
STRING: /(?:STRING1)|(?:STRING2)|(?:STRINGBARE)/,
STRING1: /"(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\"])*"/,
STRING2: /'(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\'])*'/,
STRINGBARE: /(?:(?:ESCAPE)|\\(?:NL)|[^\n\r\f\]])*/,
FUNCTION: /(?:IDENT)\(\)/,
INTEGER: /[0-9]+/,
WITHN: /([-+])?(INTEGER)?(n)\s*(?:([-+])\s*(INTEGER))?/,
WITHOUTN: /([-+])?(INTEGER)/
};
var rx = {
not: /:not\(/,
not_end: /\)/,
tag: /((?:IDENT)|\*)/,
id: /#(IDENT)/,
cls: /\.(IDENT)/,
attr: /\[\s*(IDENT)\s*(?:([^=]?=)\s*(STRING)\s*)?\]/,
pseudo_el: /(?::(first-line|first-letter|before|after))|(?:::((?:FUNCTION)|(?:IDENT)))/,
pseudo_cls_nth: /:nth-child\(\s*(?:(?:WITHN)|(?:WITHOUTN)|(odd|even))\s*\)/,
pseudo_cls: /:(IDENT)/,
comb: /\s*(\+|~|>)\s*|\s+/,
comma: /\s*,\s*/,
important: /\s+!important\s*$/
};
/* Replace placeholders with actual regex, and mark all as case insensitive */
var token = /[A-Z][A-Z0-9]+/;
for (var k in rx) {
var m, src = rx[k].source;
while (m = src.match(token)) src = src.replace(m[0], tokens[m[0]].source);
rx[k] = new RegExp(src, 'gi');
}
/**
* A string that matches itself against regexii, and keeps track of how much of itself has been matched
*/
var ConsumableString = Base.extend({
init: function(str) {
this.str = str;
this.pos = 0;
},
match: function(rx) {
var m;
rx.lastIndex = this.pos;
if ((m = rx.exec(this.str)) && m.index == this.pos ) {
this.pos = rx.lastIndex ? rx.lastIndex : this.str.length ;
return m;
}
return null;
},
peek: function(rx) {
var m;
rx.lastIndex = this.pos;
if ((m = rx.exec(this.str)) && m.index == this.pos ) return m;
return null;
},
showpos: function() {
return this.str.slice(0,this.pos)+'<HERE>' + this.str.slice(this.pos);
},
done: function() {
return this.pos == this.str.length;
}
});
/* A base class that all Selectors inherit off */
var SelectorBase = Base.extend({});
/**
* A class representing a Simple Selector, as per the CSS3 selector spec
*/
var SimpleSelector = SelectorBase.extend({
init: function() {
this.tag = null;
this.id = null;
this.classes = [];
this.attrs = [];
this.nots = [];
this.pseudo_classes = [];
this.pseudo_els = [];
},
parse: function(selector) {
var m;
/* Pull out the initial tag first, if there is one */
if (m = selector.match(rx.tag)) this.tag = m[1];
/* Then for each selection type, try and find a match */
do {
if (m = selector.match(rx.not)) {
this.nots[this.nots.length] = SelectorsGroup().parse(selector);
if (!(m = selector.match(rx.not_end))) {
throw 'Invalid :not term in selector';
}
}
else if (m = selector.match(rx.id)) this.id = m[1];
else if (m = selector.match(rx.cls)) this.classes[this.classes.length] = m[1];
else if (m = selector.match(rx.attr)) this.attrs[this.attrs.length] = [ m[1], m[2], m[3] ];
else if (m = selector.match(rx.pseudo_el)) this.pseudo_els[this.pseudo_els.length] = m[1] || m[2];
else if (m = selector.match(rx.pseudo_cls_nth)) {
if (m[3]) {
var a = parseInt((m[1]||'')+(m[2]||'1'));
var b = parseInt((m[4]||'')+(m[5]||'0'));
}
else {
var a = m[8] ? 2 : 0;
var b = m[8] ? (4-m[8].length) : parseInt((m[6]||'')+m[7]);
}
this.pseudo_classes[this.pseudo_classes.length] = ['nth-child', [a, b]];
}
else if (m = selector.match(rx.pseudo_cls)) this.pseudo_classes[this.pseudo_classes.length] = [m[1]];
} while(m && !selector.done());
return this;
}
});
/**
* A class representing a Selector, as per the CSS3 selector spec
*/
var Selector = SelectorBase.extend({
init: function(){
this.parts = [];
},
parse: function(cons){
this.parts[this.parts.length] = SimpleSelector().parse(cons);
while (!cons.done() && !cons.peek(rx.comma) && (m = cons.match(rx.comb))) {
this.parts[this.parts.length] = m[1] || ' ';
this.parts[this.parts.length] = SimpleSelector().parse(cons);
}
return this.parts.length == 1 ? this.parts[0] : this;
}
});
/**
* A class representing a sequence of selectors, as per the CSS3 selector spec
*/
var SelectorsGroup = SelectorBase.extend({
init: function(){
this.parts = [];
},
parse: function(cons){
this.parts[this.parts.length] = Selector().parse(cons);
while (!cons.done() && (m = cons.match(rx.comma))) {
this.parts[this.parts.length] = Selector().parse(cons);
}
return this.parts.length == 1 ? this.parts[0] : this;
}
});
$.selector = function(s){
var cons = ConsumableString(s);
var res = SelectorsGroup().parse(cons);
res.selector = s;
if (!cons.done()) throw 'Could not parse selector - ' + cons.showpos() ;
else return res;
};
$.selector.SelectorBase = SelectorBase;
$.selector.SimpleSelector = SimpleSelector;
$.selector.Selector = Selector;
$.selector.SelectorsGroup = SelectorsGroup;
})(jQuery);
;
/* vendor/jquery.selector/jquery.selector.specifity.js */
(function($) {
$.selector.SimpleSelector.addMethod('specifity', function() {
if (this.spec) return this.spec;
var spec = [
this.id ? 1 : 0,
this.classes.length + this.attrs.length + this.pseudo_classes.length,
((this.tag && this.tag != '*') ? 1 : 0) + this.pseudo_els.length
];
$.each(this.nots, function(i,not){
var ns = not.specifity(); spec[0] += ns[0]; spec[1] += ns[1]; spec[2] += ns[2];
});
return this.spec = spec;
});
$.selector.Selector.addMethod('specifity', function(){
if (this.spec) return this.spec;
var spec = [0,0,0];
$.each(this.parts, function(i,part){
if (i%2) return;
var ps = part.specifity(); spec[0] += ps[0]; spec[1] += ps[1]; spec[2] += ps[2];
});
return this.spec = spec;
});
$.selector.SelectorsGroup.addMethod('specifity', function(){
if (this.spec) return this.spec;
var spec = [0,0,0];
$.each(this.parts, function(i,part){
var ps = part.specifity(); spec[0] += ps[0]; spec[1] += ps[1]; spec[2] += ps[2];
});
return this.spec = spec;
});
})(jQuery);
;
/* vendor/jquery.selector/jquery.selector.matches.js */
/*
This attempts to do the opposite of Sizzle.
Sizzle is good for finding elements for a selector, but not so good for telling if an individual element matches a selector
*/
(function($) {
/**** CAPABILITY TESTS ****/
var div = document.createElement('div');
div.innerHTML = '<form id="test"><input name="id" type="text"/></form>';
// In IE 6-7, getAttribute often does the wrong thing (returns similar to el.attr), so we need to use getAttributeNode on that browser
var getAttributeDodgy = div.firstChild.getAttribute('id') !== 'test';
// Does browser support Element.firstElementChild, Element.previousElementSibling, etc.
var hasElementTraversal = div.firstElementChild && div.firstElementChild.tagName == 'FORM';
// Does browser support Element.children
var hasChildren = div.children && div.children[0].tagName == 'FORM';
var FUNC_IN = /^\s*function\s*\([^)]*\)\s*\{/;
var FUNC_OUT = /}\s*$/;
var funcToString = function(f) {
return (''+f).replace(FUNC_IN,'').replace(FUNC_OUT,'');
};
// Can we use Function#toString ?
try {
var testFunc = function(){ return 'good'; };
if ((new Function('',funcToString(testFunc)))() != 'good') funcToString = false;
}
catch(e) { funcToString = false; console.log(e.message);/*pass*/ }
/**** INTRO ****/
var GOOD = /GOOD/g;
var BAD = /BAD/g;
var STARTS_WITH_QUOTES = /^['"]/g;
var join = function(js) {
return js.join('\n');
};
var join_complex = function(js) {
var code = new String(js.join('\n')); // String objects can have properties set. strings can't
code.complex = true;
return code;
};
/**** ATTRIBUTE ACCESSORS ****/
// Not all attribute names can be used as identifiers, so we encode any non-acceptable characters as hex
var varForAttr = function(attr) {
return '_' + attr.replace(/^[^A-Za-z]|[^A-Za-z0-9]/g, function(m){ return '_0x' + m.charCodeAt(0).toString(16) + '_'; });
};
var getAttr;
// Good browsers
if (!getAttributeDodgy) {
getAttr = function(attr){ return 'var '+varForAttr(attr)+' = el.getAttribute("'+attr+'");' ; };
}
// IE 6, 7
else {
// On IE 6 + 7, getAttribute still has to be called with DOM property mirror name, not attribute name. Map attributes to those names
var getAttrIEMap = { 'class': 'className', 'for': 'htmlFor' };
getAttr = function(attr) {
var ieattr = getAttrIEMap[attr] || attr;
return 'var '+varForAttr(attr)+' = el.getAttribute("'+ieattr+'",2) || (el.getAttributeNode("'+attr+'")||{}).nodeValue;';
};
}
/**** ATTRIBUTE COMPARITORS ****/
var attrchecks = {
'-': '!K',
'=': 'K != "V"',
'!=': 'K == "V"',
'~=': '_WS_K.indexOf(" V ") == -1',
'^=': '!K || K.indexOf("V") != 0',
'*=': '!K || K.indexOf("V") == -1',
'$=': '!K || K.substr(K.length-"V".length) != "V"'
};
/**** STATE TRACKER ****/
var State = $.selector.State = Base.extend({
init: function(){
this.reset();
},
reset: function() {
this.attrs = {}; this.wsattrs = {};
},
prev: function(){
this.reset();
if (hasElementTraversal) return 'el = el.previousElementSibling';
return 'while((el = el.previousSibling) && el.nodeType != 1) {}';
},
next: function() {
this.reset();
if (hasElementTraversal) return 'el = el.nextElementSibling';
return 'while((el = el.nextSibling) && el.nodeType != 1) {}';
},
prevLoop: function(body){
this.reset();
if (hasElementTraversal) return join([ 'while(el = el.previousElementSibling){', body]);
return join([
'while(el = el.previousSibling){',
'if (el.nodeType != 1) continue;',
body
]);
},
parent: function() {
this.reset();
return 'el = el.parentNode;';
},
parentLoop: function(body) {
this.reset();
return join([
'while((el = el.parentNode) && el.nodeType == 1){',
body,
'}'
]);
},
uses_attr: function(attr) {
if (this.attrs[attr]) return;
this.attrs[attr] = true;
return getAttr(attr);
},
uses_wsattr: function(attr) {
if (this.wsattrs[attr]) return;
this.wsattrs[attr] = true;
return join([this.uses_attr(attr), 'var _WS_'+varForAttr(attr)+' = " "+'+varForAttr(attr)+'+" ";']);
},
save: function(lbl) {
return 'var el'+lbl+' = el;';
},
restore: function(lbl) {
this.reset();
return 'el = el'+lbl+';';
}
});
/**** PSEUDO-CLASS DETAILS ****/
var pseudoclschecks = {
'first-child': join([
'var cel = el;',
'while(cel = cel.previousSibling){ if (cel.nodeType === 1) BAD; }'
]),
'last-child': join([
'var cel = el;',
'while(cel = cel.nextSibling){ if (cel.nodeType === 1) BAD; }'
]),
'nth-child': function(a,b) {
var get_i = join([
'var i = 1, cel = el;',
'while(cel = cel.previousSibling){',
'if (cel.nodeType === 1) i++;',
'}'
]);
if (a == 0) return join([
get_i,
'if (i- '+b+' != 0) BAD;'
]);
else if (b == 0 && a >= 0) return join([
get_i,
'if (i%'+a+' != 0 || i/'+a+' < 0) BAD;'
]);
else if (b == 0 && a < 0) return join([
'BAD;'
]);
else return join([
get_i,
'if ((i- '+b+')%'+a+' != 0 || (i- '+b+')/'+a+' < 0) BAD;'
]);
}
};
// Needs to refence contents of object, so must be injected after definition
pseudoclschecks['only-child'] = join([
pseudoclschecks['first-child'],
pseudoclschecks['last-child']
]);
/**** SimpleSelector ****/
$.selector.SimpleSelector.addMethod('compile', function(el) {
var js = [];
/* Check against element name */
if (this.tag && this.tag != '*') {
js[js.length] = 'if (el.tagName != "'+this.tag.toUpperCase()+'") BAD;';
}
/* Check against ID */
if (this.id) {
js[js.length] = el.uses_attr('id');
js[js.length] = 'if (_id !== "'+this.id+'") BAD;';
}
/* Build className checking variable */
if (this.classes.length) {
js[js.length] = el.uses_wsattr('class');
/* Check against class names */
$.each(this.classes, function(i, cls){
js[js.length] = 'if (_WS__class.indexOf(" '+cls+' ") == -1) BAD;';
});
}
/* Check against attributes */
$.each(this.attrs, function(i, attr){
js[js.length] = (attr[1] == '~=') ? el.uses_wsattr(attr[0]) : el.uses_attr(attr[0]);
var check = attrchecks[ attr[1] || '-' ];
check = check.replace( /K/g, varForAttr(attr[0])).replace( /V/g, attr[2] && attr[2].match(STARTS_WITH_QUOTES) ? attr[2].slice(1,-1) : attr[2] );
js[js.length] = 'if ('+check+') BAD;';
});
/* Check against nots */
$.each(this.nots, function(i, not){
var lbl = ++lbl_id;
var func = join([
'l'+lbl+':{',
not.compile(el).replace(BAD, 'break l'+lbl).replace(GOOD, 'BAD'),
'}'
]);
if (!(not instanceof $.selector.SimpleSelector)) func = join([
el.save(lbl),
func,
el.restore(lbl)
]);
js[js.length] = func;
});
/* Check against pseudo-classes */
$.each(this.pseudo_classes, function(i, pscls){
var check = pseudoclschecks[pscls[0]];
if (check) {
js[js.length] = ( typeof check == 'function' ? check.apply(this, pscls[1]) : check );
}
else if (check = $.find.selectors.filters[pscls[0]]) {
if (funcToString) {
js[js.length] = funcToString(check).replace(/elem/g,'el').replace(/return([^;]+);/,'if (!($1)) BAD;');
}
else {
js[js.length] = 'if (!$.find.selectors.filters.'+pscls[0]+'(el)) BAD;';
}
}
});
js[js.length] = 'GOOD';
/* Pass */
return join(js);
});
var lbl_id = 0;
/** Turns an compiled fragment into the first part of a combination */
function as_subexpr(f) {
if (f.complex)
return join([
'l'+(++lbl_id)+':{',
f.replace(GOOD, 'break l'+lbl_id),
'}'
]);
else
return f.replace(GOOD, '');
}
var combines = {
' ': function(el, f1, f2) {
return join_complex([
f2,
'while(true){',
el.parent(),
'if (!el || el.nodeType !== 1) BAD;',
f1.compile(el).replace(BAD, 'continue'),
'}'
]);
},
'>': function(el, f1, f2) {
return join([
f2,
el.parent(),
'if (!el || el.nodeType !== 1) BAD;',
f1.compile(el)
]);
},
'~': function(el, f1, f2) {
return join_complex([
f2,
el.prevLoop(),
f1.compile(el).replace(BAD, 'continue'),
'}',
'BAD;'
]);
},
'+': function(el, f1, f2) {
return join([
f2,
el.prev(),
'if (!el) BAD;',
f1.compile(el)
]);
}
};
$.selector.Selector.addMethod('compile', function(el) {
var l = this.parts.length;
var expr = this.parts[--l].compile(el);
while (l) {
var combinator = this.parts[--l];
expr = combines[combinator](el, this.parts[--l], as_subexpr(expr));
}
return expr;
});
$.selector.SelectorsGroup.addMethod('compile', function(el) {
var expr = [], lbl = ++lbl_id;
for (var i=0; i < this.parts.length; i++) {
expr[expr.length] = join([
i == 0 ? el.save(lbl) : el.restore(lbl),
'l'+lbl+'_'+i+':{',
this.parts[i].compile(el).replace(BAD, 'break l'+lbl+'_'+i),
'}'
]);
}
expr[expr.length] = 'BAD;';
return join(expr);
});
$.selector.SelectorBase.addMethod('matches', function(el){
this.matches = new Function('el', join([
'if (!el) return false;',
this.compile(new State()).replace(BAD, 'return false').replace(GOOD, 'return true')
]));
return this.matches(el);
});
})(jQuery);
;
/* src/jquery.focusinout.js */
(function($){
/**
* Add focusin and focusout support to bind and live for browers other than IE. Designed to be usable in a delegated fashion (like $.live)
* Copyright (c) 2007 Jörn Zaefferer
*/
$.support.focusInOut = !!($.browser.msie);
if (!$.support.focusInOut) {
// Emulate focusin and focusout by binding focus and blur in capturing mode
$.each({focus: 'focusin', blur: 'focusout'}, function(original, fix){
$.event.special[fix] = {
setup: function(){
if (!this.addEventListener) return false;
this.addEventListener(original, $.event.special[fix].handler, true);
},
teardown: function(){
if (!this.removeEventListener) return false;
this.removeEventListener(original, $.event.special[fix].handler, true);
},
handler: function(e){
arguments[0] = $.event.fix(e);
arguments[0].type = fix;
return $.event.handle.apply(this, arguments);
}
};
});
}
(function(){
//IE has some trouble with focusout with select and keyboard navigation
var activeFocus = null;
$(document)
.bind('focusin', function(e){
var target = e.realTarget || e.target;
if (activeFocus && activeFocus !== target) {
e.type = 'focusout';
$(activeFocus).trigger(e);
e.type = 'focusin';
e.target = target;
}
activeFocus = target;
})
.bind('focusout', function(e){
activeFocus = null;
});
})();
})(jQuery);;
/* src/jquery.entwine.js */
var console;
(function($) {
var namespaces = {};
$.entwine = function() {
$.fn.entwine.apply(null, arguments);
};
/**
* A couple of utility functions for accessing the store outside of this closure, and for making things
* operate in a little more easy-to-test manner
*/
$.extend($.entwine, {
/**
* Get all the namespaces. Useful for introspection? Internal interface of Namespace not guaranteed consistant
*/
namespaces: namespaces,
/**
* Remove all entwine rules
*/
clear_all_rules: function() {
// Remove proxy functions
for (var k in $.fn) { if ($.fn[k].entwine) delete $.fn[k] ; }
// Remove bound events - TODO: Make this pluggable, so this code can be moved to jquery.entwine.events.js
$(document).unbind('.entwine');
// Remove namespaces, and start over again
namespaces = $.entwine.namespaces = {};
},
WARN_LEVEL_NONE: 0,
WARN_LEVEL_IMPORTANT: 1,
WARN_LEVEL_BESTPRACTISE: 2,
/**
* Warning level. Set to a higher level to get warnings dumped to console.
*/
warningLevel: 0,
/** Utility to optionally display warning messages depending on level */
warn: function(message, level) {
if (level <= $.entwine.warningLevel && console && console.warn) {
console.warn(message);
if (console.trace) console.trace();
}
},
warn_exception: function(where, /* optional: */ on, e) {
if ($.entwine.WARN_LEVEL_IMPORTANT <= $.entwine.warningLevel && console && console.warn) {
if (arguments.length == 2) { e = on; on = null; }
if (on) console.warn('Uncaught exception',e,'in',where,'on',on);
else console.warn('Uncaught exception',e,'in',where);
if (e.stack) console.warn("Stack Trace:\n" + e.stack);
}
}
});
/** Stores a count of definitions, so that we can sort identical selectors by definition order */
var rulecount = 0;
var Rule = Base.extend({
init: function(selector, name) {
this.selector = selector;
this.specifity = selector.specifity();
this.important = 0;
this.name = name;
this.rulecount = rulecount++;
}
});
Rule.compare = function(a, b) {
var as = a.specifity, bs = b.specifity;
return (a.important - b.important) ||
(as[0] - bs[0]) ||
(as[1] - bs[1]) ||
(as[2] - bs[2]) ||
(a.rulecount - b.rulecount) ;
};
$.entwine.RuleList = function() {
var list = [];
list.addRule = function(selector, name){
var rule = Rule(selector, name);
list[list.length] = rule;
list.sort(Rule.compare);
return rule;
};
return list;
};
var handlers = [];
/**
* A Namespace holds all the information needed for adding entwine methods to a namespace (including the _null_ namespace)
*/
$.entwine.Namespace = Base.extend({
init: function(name){
if (name && !name.match(/^[A-Za-z0-9.]+$/)) $.entwine.warn('Entwine namespace '+name+' is not formatted as period seperated identifiers', $.entwine.WARN_LEVEL_BESTPRACTISE);
name = name || '__base';
this.name = name;
this.store = {};
namespaces[name] = this;
if (name == "__base") {
this.injectee = $.fn;
this.$ = $;
}
else {
// We're in a namespace, so we build a Class that subclasses the jQuery Object Class to inject namespace functions into
var subfn = function(){};
this.injectee = subfn.prototype = new $();
// And then we provide an overriding $ that returns objects of our new Class, and an overriding pushStack to catch further selection building
var bound$ = this.$ = function(a) {
// Try the simple way first
var jq = $.fn.init.apply(new subfn(), arguments);
if (jq instanceof subfn) return jq;
// That didn't return a bound object, so now we need to copy it
var rv = new subfn();
rv.selector = jq.selector; rv.context = jq.context; var i = rv.length = jq.length;
while (i--) rv[i] = jq[i];
return rv;
};
this.injectee.pushStack = function(elems, name, selector){
var ret = bound$(elems);
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
if ( name === "find" ) ret.selector = this.selector + (this.selector ? " " : "") + selector;
else if ( name ) ret.selector = this.selector + "." + name + "(" + selector + ")";
// Return the newly-formed element set
return ret;
};
// Copy static functions through from $ to this.$ so e.g. $.ajax still works
// @bug, @cantfix: Any class functions added to $ after this call won't get mirrored through
$.extend(this.$, $);
// We override entwine to inject the name of this namespace when defining blocks inside this namespace
var entwine_wrapper = this.injectee.entwine = function(spacename) {
var args = arguments;
if (!spacename || typeof spacename != 'string') { args = $.makeArray(args); args.unshift(name); }
else if (spacename.charAt(0) != '.') args[0] = name+'.'+spacename;
return $.fn.entwine.apply(this, args);
};
this.$.entwine = function() {
entwine_wrapper.apply(null, arguments);
};
for (var i = 0; i < handlers.length; i++) {
var handler = handlers[i], builder;
// Inject jQuery object method overrides
if (builder = handler.namespaceMethodOverrides) {
var overrides = builder(this);
for (var k in overrides) this.injectee[k] = overrides[k];
}
// Inject $.entwine function overrides
if (builder = handler.namespaceStaticOverrides) {
var overrides = builder(this);
for (var k in overrides) this.$.entwine[k] = overrides[k];
}
}
}
},
/**
* Returns a function that does selector matching against the function list for a function name
* Used by proxy for all calls, and by ctorProxy to handle _super calls
* @param {String} name - name of the function as passed in the construction object
* @param {String} funcprop - the property on the Rule object that gives the actual function to call
* @param {function} basefunc - the non-entwine function to use as the catch-all function at the bottom of the stack
*/
one: function(name, funcprop, basefunc) {
var namespace = this;
var funcs = this.store[name];
var one = function(el, args, i){
if (i === undefined) i = funcs.length;
while (i--) {
if (funcs[i].selector.matches(el)) {
var ret, tmp_i = el.i, tmp_f = el.f;
el.i = i; el.f = one;
try { ret = funcs[i][funcprop].apply(namespace.$(el), args); }
finally { el.i = tmp_i; el.f = tmp_f; }
return ret;
}
}
// If we didn't find a entwine-defined function, but there is a non-entwine function to use as a base, try that
if (basefunc) return basefunc.apply(namespace.$(el), args);
};
return one;
},
/**
* A proxy is a function attached to a callable object (either the base jQuery.fn or a subspace object) which handles
* finding and calling the correct function for each member of the current jQuery context
* @param {String} name - name of the function as passed in the construction object
* @param {function} basefunc - the non-entwine function to use as the catch-all function at the bottom of the stack
*/
build_proxy: function(name, basefunc) {
var one = this.one(name, 'func', basefunc);
var prxy = function() {
var rv, ctx = $(this);
var i = ctx.length;
while (i--) rv = one(ctx[i], arguments);
return rv;
};
return prxy;
},
bind_proxy: function(selector, name, func) {
var rulelist = this.store[name] || (this.store[name] = $.entwine.RuleList());
var rule = rulelist.addRule(selector, name); rule.func = func;
if (!this.injectee.hasOwnProperty(name) || !this.injectee[name].entwine) {
this.injectee[name] = this.build_proxy(name, this.injectee.hasOwnProperty(name) ? this.injectee[name] : null);
this.injectee[name].entwine = true;
}
if (!this.injectee[name].entwine) {
$.entwine.warn('Warning: Entwine function '+name+' clashes with regular jQuery function - entwine function will not be callable directly on jQuery object', $.entwine.WARN_LEVEL_IMPORTANT);
}
},
add: function(selector, data) {
// For every item in the hash, try ever method handler, until one returns true
for (var k in data) {
var v = data[k];
for (var i = 0; i < handlers.length; i++) {
if (handlers[i].bind && handlers[i].bind.call(this, selector, k, v)) break;
}
}
},
has: function(ctx, name) {
var rulelist = this.store[name];
if (!rulelist) return false;
/* We go forward this time, since low specifity is likely to knock out a bunch of elements quickly */
for (var i = 0 ; i < rulelist.length; i++) {
ctx = ctx.not(rulelist[i].selector);
if (!ctx.length) return true;
}
return false;
}
});
/**
* A handler is some javascript code that adds support for some time of key / value pair passed in the hash to the Namespace add method.
* The default handlers provided (and included by default) are event, ctor and properties
*/
$.entwine.Namespace.addHandler = function(handler) {
for (var i = 0; i < handlers.length && handlers[i].order < handler.order; i++) { /* Pass */ }
handlers.splice(i, 0, handler);
};
$.entwine.Namespace.addHandler({
order: 50,
bind: function(selector, k, v){
if ($.isFunction(v)) {
this.bind_proxy(selector, k, v);
return true;
}
}
});
$.extend($.fn, {
/**
* Main entwine function. Used for new definitions, calling into a namespace (or forcing the base namespace) and entering a using block
*
*/
entwine: function(spacename) {
var i = 0;
/* Don't actually work out selector until we try and define something on it - we might be opening a namespace on an function-traveresed object
which have non-standard selectors like .parents(.foo).slice(0,1) */
var selector = null;
/* By default we operator on the base namespace */
var namespace = namespaces.__base || $.entwine.Namespace();
/* If the first argument is a string, then it's the name of a namespace. Look it up */
if (typeof spacename == 'string') {
if (spacename.charAt('0') == '.') spacename = spacename.substr(1);
if (spacename) namespace = namespaces[spacename] || $.entwine.Namespace(spacename);
i=1;
}
/* All remaining arguments should either be using blocks or definition hashs */
while (i < arguments.length) {
var res = arguments[i++];
// If it's a function, call it - either it's a using block or it's a namespaced entwine definition
if ($.isFunction(res)) {
if (res.length != 1) $.entwine.warn('Function block inside entwine definition does not take $ argument properly', $.entwine.WARN_LEVEL_IMPORTANT);
res = res.call(namespace.$(this), namespace.$);
}
// If we have a entwine definition hash, inject it into namespace
if (res) {
if (selector === null) selector = this.selector ? $.selector(this.selector) : false;
if (selector) namespace.add(selector, res);
else $.entwine.warn('Entwine block given to entwine call without selector. Make sure you call $(selector).entwine when defining blocks', $.entwine.WARN_LEVEL_IMPORTANT);
}
}
/* Finally, return the jQuery object 'this' refers to, wrapped in the new namespace */
return namespace.$(this);
},
/**
* Calls the next most specific version of the current entwine method
*/
_super: function(){
var rv, i = this.length;
while (i--) {
var el = this[0];
rv = el.f(el, arguments, el.i);
}
return rv;
}
});
})(jQuery);
;
/* src/jquery.entwine.dommaybechanged.js */
(function($){
/** What to call to run a function 'soon'. Normally setTimeout, but for syncronous mode we override so soon === now */
var runSoon = window.setTimeout;
/** The timer handle for the asyncronous matching call */
var check_id = null;
/** Fire the change event. Only fires on the document node, so bind to that */
var triggerEvent = function() {
$(document).triggerHandler('DOMMaybeChanged');
check_id = null;
};
$.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 (check_id) clearTimeout(check_id); check_id = null;
runSoon = function(func, delay){ func.call(this); return null; };
},
/**
* Trigger onmatch and onunmatch now - usefull for after DOM manipulation by methods other than through jQuery.
* Called automatically on document.ready
*/
triggerMatching: function() {
matching();
}
});
function registerMutateFunction() {
$.each(arguments, function(i,func){
var old = $.fn[func];
$.fn[func] = function() {
var rv = old.apply(this, arguments);
if (!check_id) check_id = runSoon(triggerEvent, 100);
return rv;
};
});
}
function registerSetterGetterFunction() {
$.each(arguments, function(i,func){
var old = $.fn[func];
$.fn[func] = function(a, b) {
var rv = old.apply(this, arguments);
if (!check_id && (b !== undefined || typeof a != 'string')) check_id = runSoon(triggerEvent, 100);
return rv;
};
});
}
// Register core DOM manipulation methods
registerMutateFunction('append', 'prepend', 'after', 'before', 'wrap', 'removeAttr', 'addClass', 'removeClass', 'toggleClass', 'empty', 'remove');
registerSetterGetterFunction('attr');
// And on DOM ready, trigger matching once
$(function(){ triggerEvent(); });
})(jQuery);;
/* src/jquery.entwine.events.js */
(function($) {
/* If we are any browser other than IE or Safari, we don't have to do anything special to handle
* onchange delegation */
$.support.bubblingChange = !($.browser.msie || $.browser.safari);
/* 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');
var prxy = function(e) {
var el = e.target;
// If this is a keydown event, only worry about the enter key, since browsers only trigger onchange on enter or focus loss
if (e.type === 'keydown' && e.keyCode !== 13) return;
// Make sure this is event is for an input type we're interested in
if (el.tagName !== 'INPUT' && el.tagName !== 'TEXTAREA' && el.tagName !== 'SELECT') return;
var $el = $(el), nowVal, oldVal = $el.data('changeVal');
// Detect changes on checkboxes & radiobuttons, which have different value logic. We don't use el.value, since el is part
// of a set, and we only want to raise onchange once for a single user action.
if (el.type == 'checkbox' || el.type == 'radio') {
if (!el.disabled && e.type === 'click') {
nowVal = el.checked;
// If radio, we get two changes - the activation, and the deactivation. We only want to fire one change though
if ((el.type === 'checkbox' || nowVal === true) && oldVal !== nowVal) e.type = 'change';
}
}
// Detect changes on other input types. In this case value is OK.
else {
nowVal = el.value;
if (oldVal !== undefined && oldVal !== nowVal) e.type = 'change';
}
// Save the current value for next time
if (nowVal !== undefined) $el.data('changeVal', nowVal);
// And if we decided that a change happened, do the actual triggering
if (e.type == 'change') {
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;
},
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.bubblingChange) {
proxies[name] = this.build_change_proxy(name);
event = 'click focusin focusout keydown';
}
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+'.entwine', proxies[name]);
}
}
});
$.entwine.Namespace.addHandler({
order: 40,
bind: function(selector, k, v){
var match, event;
if ($.isFunction(v) && (match = k.match(/^on(.*)/))) {
event = match[1];
this.bind_event(selector, k, v, event);
return true;
}
}
});
// Find all forms and bind onsubmit to trigger on the document too.
// This is the only event that can't be grabbed via delegation
var form_binding_cache = $([]); // A cache for already-handled form elements
var delegate_submit = function(e, data){
var delegationEvent = $.Event('delegatedSubmit'); delegationEvent.delegatedEvent = e;
return $(document).trigger(delegationEvent, data);
};
$(document).bind('DOMMaybeChanged', function(){
var forms = $('form');
// Only bind to forms we haven't processed yet
forms.not(form_binding_cache).bind('submit', delegate_submit);
// Then remember the current set of forms
form_binding_cache = forms;
});
})(jQuery);
;
/* src/jquery.entwine.ctors.js */
(function($) {
/* Add the methods to handle constructor & destructor binding to the Namespace class */
$.entwine.Namespace.addMethods({
bind_condesc: function(selector, name, func) {
var ctors = this.store.ctors || (this.store.ctors = $.entwine.RuleList()) ;
var rule;
for (var i = 0 ; i < ctors.length; i++) {
if (ctors[i].selector.selector == selector.selector) {
rule = ctors[i]; break;
}
}
if (!rule) {
rule = ctors.addRule(selector, 'ctors');
}
rule[name] = func;
if (!ctors[name+'proxy']) {
var one = this.one('ctors', name);
var namespace = this;
var proxy = function(els, i, func) {
var j = els.length;
while (j--) {
var el = els[j];
var tmp_i = el.i, tmp_f = el.f;
el.i = i; el.f = one;
try { func.call(namespace.$(el)); }
catch(e) { $.entwine.warn_exception(name, el, e); }
finally { el.i = tmp_i; el.f = tmp_f; }
}
};
ctors[name+'proxy'] = proxy;
}
}
});
$.entwine.Namespace.addHandler({
order: 30,
bind: function(selector, k, v) {
if ($.isFunction(v) && (k == 'onmatch' || k == 'onunmatch')) {
this.bind_condesc(selector, k, v);
return true;
}
}
});
/**
* Finds all the elements that now match a different rule (or have been removed) and call onmatch on onunmatch as appropriate
*
* Because this has to scan the DOM, and is therefore fairly slow, this is normally triggered off a short timeout, so that
* a series of DOM manipulations will only trigger this once.
*
* The downside of this is that things like:
* $('#foo').addClass('tabs'); $('#foo').tabFunctionBar();
* won't work.
*/
$(document).bind('DOMMaybeChanged', function(){
// For every namespace
for (var k in $.entwine.namespaces) {
// That has constructors or destructors
var ctors = $.entwine.namespaces[k].store.ctors;
if (ctors) {
// Keep a record of elements that have matched already
var matched = $([]), add, rem, res, rule, sel, ctor, dtor;
// 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;
// Get the list of elements that match this selector, that haven't yet matched a more specific selector
res = add = $(sel).not(matched);
// If this selector has a list of elements it matched against last time
if (rule.cache) {
// Find the ones that are extra this time
add = res.not(rule.cache);
if (dtor) {
// Find the ones that are gone this time
rem = rule.cache.not(res);
// And call the destructor on them
if (rem.length) ctors.onunmatchproxy(rem, j, dtor);
}
}
// Call the constructor on the newly matched ones
if (add.length && ctor) ctors.onmatchproxy(add, j, ctor);
// Add these matched ones to the list tracking all elements matched so far
matched = matched.add(res);
// And remember this list of matching elements again this selector, so next matching we can find the unmatched ones
ctors[j].cache = res;
}
}
}
});
})(jQuery);
;
/* src/jquery.entwine.properties.js */
(function($) {
var entwine_prepend = '__entwine!';
var getEntwineData = function(el, namespace, property) {
return el.data(entwine_prepend + namespace + '!' + property);
};
var setEntwineData = function(el, namespace, property, value) {
return el.data(entwine_prepend + namespace + '!' + property, value);
};
var getEntwineDataAsHash = function(el, namespace) {
var hash = {};
var id = jQuery.data(el[0]);
var matchstr = entwine_prepend + namespace + '!';
var matchlen = matchstr.length;
var cache = jQuery.cache[id];
for (var k in cache) {
if (k.substr(0,matchlen) == matchstr) hash[k.substr(matchlen)] = cache[k];
}
return hash;
};
var setEntwineDataFromHash = function(el, namespace, hash) {
for (var k in hash) setEntwineData(namespace, k, hash[k]);
};
var entwineData = function(el, namespace, args) {
switch (args.length) {
case 0:
return getEntwineDataAsHash(el, namespace);
case 1:
if (typeof args[0] == 'string') return getEntwineData(el, namespace, args[0]);
else return setEntwineDataFromHash(el, namespace, args[0]);
default:
return setEntwineData(el, namespace, args[0], args[1]);
}
};
$.extend($.fn, {
entwineData: function() {
return entwineData(this, '__base', arguments);
}
});
$.entwine.Namespace.addHandler({
order: 60,
bind: function(selector, k, v) {
if (k.charAt(0) != k.charAt(0).toUpperCase()) $.entwine.warn('Entwine property '+k+' does not start with a capital letter', $.entwine.WARN_LEVEL_BESTPRACTISE);
// Create the getters and setters
var getterName = 'get'+k;
var setterName = 'set'+k;
this.bind_proxy(selector, getterName, function() { return this.entwineData(k) || v ; });
this.bind_proxy(selector, setterName, function(v){ return this.entwineData(k, v); });
// Get the get and set proxies we just created
var getter = this.injectee[getterName];
var setter = this.injectee[setterName];
// And bind in the jQuery-style accessor
this.bind_proxy(selector, k, function(v){ return (arguments.length == 1 ? setter : getter).call(this, v) ; });
return true;
},
namespaceMethodOverrides: function(namespace){
return {
entwineData: function() {
return entwineData(this, namespace.name, arguments);
}
};
}
});
})(jQuery);
;
/* src/jquery.entwine.legacy.js */
(function($) {
// Adds back concrete methods for backwards compatibility
$.concrete = $.entwine;
$.fn.concrete = $.fn.entwine;
$.fn.concreteData = $.fn.entwineData;
// Use addHandler to hack in the namespace.$.concrete equivilent to the namespace.$.entwine namespace-injection
$.entwine.Namespace.addHandler({
order: 100,
bind: function(selector, k, v) { return false; },
namespaceMethodOverrides: function(namespace){
namespace.$.concrete = namespace.$.entwine;
namespace.injectee.concrete = namespace.injectee.entwine;
namespace.injectee.concreteData = namespace.injectee.entwineData;
return {};
}
});
})(jQuery);
;

View File

@ -0,0 +1,30 @@
<html>
<head>
<!-- spec-dist is the same as spec, but runs on the distribution version of the library, to make sure no problems were introduced during building -->
<link type="text/css" rel="stylesheet" href="../../vendor/jspec/lib/jspec.css" />
<script src="../../vendor/jquery-1.3.2.js"></script>
<script src="../../vendor/jspec/lib/jspec.js"></script>
<script src="../../vendor/jspec/lib/jspec.jquery.js"></script>
<script src="../../dist/jquery.entwine-dist.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.concrete.basics.js')
.exec('spec.concrete.namespaces.js')
.exec('spec.concrete.super.js')
.exec('spec.concrete.events.js')
.exec('spec.concrete.ctors.js')
.exec('spec.concrete.properties.js')
.run()
.report()
}
</script>
</head>
<body class="jspec" onLoad="runSuites();">
<div id="jspec-top"><h2 id="jspec-title">JSpec <em><script>document.write(JSpec.version)</script></em></h2></div>
<div id="jspec"><div class="loading"></div></div>
<div id="jspec-bottom"></div>
</body>
</html>

View File

@ -11,7 +11,7 @@ describe 'Concrete'
before_each
$.concrete.clear_all_rules();
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="c d e"></div>');
$('#dom_test').html('<div id="a" class="a b c" data-fieldtype="foo"></div><div id="b" class="c d e"></div>');
end
it 'can attach and call a base function'
@ -20,6 +20,13 @@ describe 'Concrete'
});
$('.a').foo().should.equal 'a'
end
it 'can attach and call a base function on a selector using a data attribute selection'
$('[data-fieldtype=foo]').concrete({
foo: function(){return this.attr('id');}
});
$('.a').foo().should.equal 'a'
end
it 'can attach and call several base functions'
$('#a').concrete({

View File

@ -10,12 +10,12 @@ describe 'Concrete'
end
before_each
$.concrete.synchronous_mode();
$.concrete.synchronous_mode();
$.concrete.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div>')
end
it 'calls onfoo when foo triggered'
it 'calls onfoo when foo triggered'
var a = 0;
$('#a').concrete({onfoo: function(){a = 1;} });
a.should.equal 0
@ -63,6 +63,50 @@ describe 'Concrete'
$('#a').trigger('foo')
[a, b].should.eql [2, 1]
end
it 'passes event object'
var event;
$('#a').concrete({onfoo: function(e){event = e;} });
$('#a').trigger('foo');
event.should.have_prop 'type', 'foo'
$(event.target).should.have_attr 'id', 'a'
end
it 'delegates submit events to forms'
var a = 0;
$('<form class="foo" action="javascript:undefined">').appendTo('#dom_test');
$('.foo').concrete({onsubmit: function(e, d){a = 1;} });
a.should.eql 0
$('.foo').trigger('submit');
a.should.eql 1
end
describe 'can pass event data'
it 'on custom events'
var data;
$('#a').concrete({onfoo: function(e, d){data = d;} });
$('#a').trigger('foo', {cheese: 'burger'});
data.cheese.should.eql 'burger'
end
it 'on normal events'
var data;
$('#a').concrete({onclick: function(e, d){data = d;} });
$('#a').trigger('click', {finger: 'left'});
data.finger.should.eql 'left'
end
it 'on submit'
var data;
$('<form class="foo" action="javascript:undefined">').appendTo('#dom_test');
$('.foo').concrete({onsubmit: function(e, d){data = d; return false;} })
$('.foo').trigger('submit', {cheese: 'burger'});
data.cheese.should.eql 'burger'
end
end
end
end

View File

@ -27,7 +27,7 @@ describe 'Concrete'
$('.a').setFoo(1);
$('.a').getFoo().should.equal 1
end
it 'can define a default value'
$('#a').concrete({
Foo: 1
@ -73,5 +73,30 @@ describe 'Concrete'
$('.a').concreteData('Foo').should.equal 4
$('.a').concrete('test').concreteData('Foo').should.equal 8
end
describe 'jQuery style accessors'
it 'can define and get a basic property'
$('#a').concrete({
Foo: null
});
$('.a').Foo().should.be_null
end
it 'can define and set a basic property'
$('#a').concrete({
Foo: null
});
$('.a').Foo(1);
$('.a').Foo().should.equal 1
end
it 'can define a default value'
$('#a').concrete({
Foo: 1
});
$('.a').Foo().should.equal 1
end
end
end
end

View File

@ -10,35 +10,35 @@ describe 'Concrete'
before_each
$.concrete.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="c d e"></div>')
$('#dom_test').html('<div id="a" class="a b c">Foo</div><div id="b" class="c d e">Bar</div>')
end
it 'can call the super function'
var a = 1;
var a = 1;
$('#a').concrete({
foo: function(){a *= 2;}
});
$('#a.a').concrete({
foo: function(){a += 2; this._super();}
});
$('#a').foo();
a.should.equal 6
$('#a').foo();
a.should.equal 6
end
it 'super to a non-existant class should be ignored'
var a = 1;
var a = 1;
$('#a').concrete({
foo: function(){a *= 2; this._super();}
});
$('#a.a').concrete({
foo: function(){a += 2; this._super();}
});
$('#a').foo();
a.should.equal 6
$('#a').foo();
a.should.equal 6
end
it 'can call super from two different functions without screwing up what super points to'
var list = [];
var list = [];
$('#a').concrete({
foo: function(){ list.push('foo'); this.bar(); },
bar: function(){ list.push('bar'); }
@ -47,8 +47,20 @@ describe 'Concrete'
foo: function(){ list.push('foo2'); this._super(); list.push('foo2'); this._super(); },
bar: function(){ list.push('bar2'); this._super(); }
});
$('#a').foo();
list.should.eql [ 'foo2', 'foo', 'bar2', 'bar', 'foo2', 'foo', 'bar2', 'bar' ]
$('#a').foo();
list.should.eql [ 'foo2', 'foo', 'bar2', 'bar', 'foo2', 'foo', 'bar2', 'bar' ]
end
it 'can override (and call via super) a non-concrete jquery function'
var a = 1
$('#a').concrete({
text: function(){ a = this._super(); }
});
$('#a').text();
a.should.equal 'Foo'
$('#b').text().should.equal 'Bar'
end
end
end

View File

@ -0,0 +1,39 @@
<html>
<head>
<link type="text/css" rel="stylesheet" href="../../vendor/jspec/lib/jspec.css" />
<script src="../../vendor/jquery-1.3.2.js"></script>
<script src="../../vendor/jspec/lib/jspec.js"></script>
<script src="../../vendor/jspec/lib/jspec.jquery.js"></script>
<script src="../../vendor/jquery.selector/jquery.class.js"></script>
<script src="../../vendor/jquery.selector/jquery.selector.js"></script>
<script src="../../vendor/jquery.selector/jquery.selector.specifity.js"></script>
<script src="../../vendor/jquery.selector/jquery.selector.matches.js"></script>
<script src="../../src/jquery.entwine.js"></script>
<script src="../../src/jquery.entwine.dommaybechanged.js"></script>
<script src="../../src/jquery.entwine.events.js"></script>
<script src="../../src/jquery.entwine.ctors.js"></script>
<script src="../../src/jquery.entwine.properties.js"></script>
<script src="../../src/jquery.entwine.legacy.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.concrete.basics.js')
.exec('spec.concrete.namespaces.js')
.exec('spec.concrete.super.js')
.exec('spec.concrete.events.js')
.exec('spec.concrete.ctors.js')
.exec('spec.concrete.properties.js')
.run()
.report()
}
</script>
</head>
<body class="jspec" onLoad="runSuites();">
<div id="jspec-top"><h2 id="jspec-title">JSpec <em><script>document.write(JSpec.version)</script></em></h2></div>
<div id="jspec"><div class="loading"></div></div>
<div id="jspec-bottom"></div>
</body>
</html>

View File

@ -6,17 +6,17 @@
<script src="../vendor/jspec/lib/jspec.js"></script>
<script src="../vendor/jspec/lib/jspec.jquery.js"></script>
<script src="../dist/jquery.concrete-latest.js"></script>
<script src="../dist/jquery.entwine-dist.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.concrete.basics.js')
.exec('spec.concrete.namespaces.js')
.exec('spec.concrete.super.js')
.exec('spec.concrete.events.js')
.exec('spec.concrete.ctors.js')
.exec('spec.concrete.properties.js')
.exec('spec.entwine.basics.js')
.exec('spec.entwine.namespaces.js')
.exec('spec.entwine.super.js')
.exec('spec.entwine.events.js')
.exec('spec.entwine.ctors.js')
.exec('spec.entwine.properties.js')
.run()
.report()
}

View File

@ -0,0 +1,85 @@
describe 'Entwine'
describe 'Basics'
before
$.entwine.warningLevel = $.entwine.WARN_LEVEL_BESTPRACTISE;
$('body').append('<div id="dom_test"></div>');
end
after
$('#dom_test').remove();
end
before_each
$.entwine.clear_all_rules();
$('#dom_test').html('<div id="a" class="a b c" data-fieldtype="foo"></div><div id="b" class="c d e"></div>');
end
it 'can attach and call a base function'
$('#a').entwine({
foo: function(){return this.attr('id');}
});
$('.a').foo().should.equal 'a'
end
it 'can attach and call a base function on a selector using a data attribute selection'
$('[data-fieldtype=foo]').entwine({
foo: function(){return this.attr('id');}
});
$('.a').foo().should.equal 'a'
end
it 'can attach and call several base functions'
$('#a').entwine({
foo: function(){return 'foo_' + this.attr('id');},
bar: function(){return 'bar_' + this.attr('id');}
});
$('.a').foo().should.equal 'foo_a'
$('.a').bar().should.equal 'bar_a'
end
it 'can attach and call a namespaced function'
$.entwine('bar', function($){
$('#a').entwine({
foo: function(){return this.attr('id');}
});
});
$('.a').entwine('bar').foo().should.equal 'a'
end
it 'can attach and call a nested namespaced function'
$.entwine('qux.baz.bar', function($){
$('#a').entwine({
foo: function(){return this.attr('id');}
});
});
$('.a').entwine('qux.baz.bar').foo().should.equal 'a'
end
it 'can call two functions on two elements'
var res = []
$('#a').entwine({
foo: function(){res.push(this.attr('id'));}
});
$('#b.c').entwine({
foo: function(){res.push(this.attr('id'));}
});
$('#dom_test div').foo();
res.should.eql ['b', 'a']
end
it 'can call two namespaced functions on two elements'
var res = []
$.entwine('bar', function($){
$('#a').entwine({
foo: function(){res.push(this.attr('id'));}
});
$('#b.c').entwine({
foo: function(){res.push(this.attr('id'));}
});
});
$('#dom_test div').entwine('bar').foo();
res.should.eql ['b', 'a']
end
end
end

View File

@ -0,0 +1,68 @@
describe 'Entwine'
describe 'Ctors'
before
$('body').append('<div id="dom_test"></div>')
end
after
$('#dom_test').remove()
end
before_each
$.entwine.synchronous_mode();
$.entwine.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div>')
end
it 'calls onmatch when new element created'
var a = false;
$('#b').entwine({onmatch: function(){a = true;} });
a.should.be_false
$('#a').after('<div id="b"></div>');
a.should.be_true
end
it 'calls onunmatch when new element deleted'
var a = 0;
$('#b').entwine({onmatch: function(){a = 1;}, onunmatch: function(){a = 2;} });
a.should.equal 0
$('#a').after('<div id="b"></div>');
a.should.equal 1
$('#b').remove();
a.should.equal 2
end
it 'calls onmatch when ruleset matches after class added'
var a = 0;
$('#a.foo').entwine({onmatch: function(){a = 1;} });
a.should.equal 0
$('#a').addClass('foo');
a.should.equal 1
end
it 'calls onmatch in both direct and namespaced onmatch, does not call less specific onmatch'
var a = 0, b=0, c=0, d=0;
$('.foo').entwine({onmatch: function(){a = 1;}})
$('.foo').entwine('bar', function($){return{onmatch: function(){b = 1;}}})
$('#a.foo').entwine({onmatch: function(){c = 1;}})
$('#a.foo').entwine('bar', function($){return{onmatch: function(){d = 1}}})
[a, b, c, d].should.eql [0, 0, 0, 0]
$('#a').addClass('foo');
[a, b, c, d].should.eql [0, 0, 1, 1]
end
it 'calls onmatch in both direct and namespaced onmatch, super works as expected'
var a = 0, b=0, c=0, d=0;
$('.foo').entwine({onmatch: function(){a += 1;}})
$('.foo').entwine('bar', function($){return{onmatch: function(){b += 1;}}})
$('#a.foo').entwine({onmatch: function(){this._super(); c = 1; this._super();}})
$('#a.foo').entwine('bar', function($){return{onmatch: function(){this._super(); d = 1; this._super();}}})
[a, b, c, d].should.eql [0, 0, 0, 0]
$('#a').addClass('foo');
[a, b, c, d].should.eql [2, 2, 1, 1]
end
end
end

View File

@ -0,0 +1,112 @@
describe 'Entwine'
describe 'Events'
before
$('body').append('<div id="dom_test"></div>')
end
after
$('#dom_test').remove()
end
before_each
$.entwine.synchronous_mode();
$.entwine.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div>')
end
it 'calls onfoo when foo triggered'
var a = 0;
$('#a').entwine({onfoo: function(){a = 1;} });
a.should.equal 0
$('#a').trigger('foo');
a.should.equal 1
end
it 'only calls most specific onfoo when foo triggered'
var a = 0, b = 0;
$('#a.a').entwine({onfoo: function(){a = 1;} });
$('#a').entwine({onfoo: function(){b = 1;} });
a.should.equal 0
b.should.equal 0
$('#a').trigger('foo');
a.should.equal 1
b.should.equal 0
end
it 'calls namespaced onfoo when foo triggered'
var a = 0;
$('#a').entwine('bar', function($){return{onfoo: function(){a = 1;} }});
a.should.equal 0
$('#a').trigger('foo');
a.should.equal 1
end
it 'calls most specific namespaced onfoo and most specific non-namespaced onfoo when foo triggered'
var a = 0, b = 0, c = 0, d = 0;
$('#a.a').entwine({onfoo: function(){a = 1;} });
$('#a').entwine({onfoo: function(){b = 1;} });
$('#a.a').entwine('bar', function($){return{onfoo: function(){c = 1;} }});
$('#a').entwine('bar', function($){return{onfoo: function(){d = 1;} }});
[a, b, c, d].should.eql [0, 0, 0, 0]
$('#a').trigger('foo');
[a, b, c, d].should.eql [1, 0, 1, 0]
end
it 'calls up correctly on _super'
var a = 0, b = 0;
$('#a').entwine({onfoo: function(){a += 1;} });
$('#a.a').entwine({onfoo: function(){this._super(); b += 1; this._super();} });
[a, b].should.eql [0, 0]
$('#a').trigger('foo')
[a, b].should.eql [2, 1]
end
it 'passes event object'
var event;
$('#a').entwine({onfoo: function(e){event = e;} });
$('#a').trigger('foo');
event.should.have_prop 'type', 'foo'
$(event.target).should.have_attr 'id', 'a'
end
it 'delegates submit events to forms'
var a = 0;
$('<form class="foo" action="javascript:undefined">').appendTo('#dom_test');
$('.foo').entwine({onsubmit: function(e, d){a = 1;} });
a.should.eql 0
$('.foo').trigger('submit');
a.should.eql 1
end
describe 'can pass event data'
it 'on custom events'
var data;
$('#a').entwine({onfoo: function(e, d){data = d;} });
$('#a').trigger('foo', {cheese: 'burger'});
data.cheese.should.eql 'burger'
end
it 'on normal events'
var data;
$('#a').entwine({onclick: function(e, d){data = d;} });
$('#a').trigger('click', {finger: 'left'});
data.finger.should.eql 'left'
end
it 'on submit'
var data;
$('<form class="foo" action="javascript:undefined">').appendTo('#dom_test');
$('.foo').entwine({onsubmit: function(e, d){data = d; return false;} })
$('.foo').trigger('submit', {cheese: 'burger'});
data.cheese.should.eql 'burger'
end
end
end
end

View File

@ -0,0 +1,246 @@
describe 'Entwine'
describe 'Namespaces'
before
$('body').append('<div id="dom_test"></div>')
end
after
$('#dom_test').remove()
end
before_each
$.entwine.synchronous_mode();
$.entwine.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="c d e"></div>')
end
it 'namespaced functions work (single definition mode)'
$('#a').entwine('bar', function($){return{
bar: function(){return 'a';}
}})
$('#a').entwine('bar').bar().should.equal 'a'
end
it 'namespaced functions work (block definition mode)'
$.entwine('zap', function($){
$('#a').entwine({
bar: function(){return 'a';}
})
});
$('#a').entwine('zap').bar().should.equal 'a'
end
it 'double-namespaced functions work (block definition mode)'
$.entwine('zap', function($){
$.entwine('pow', function($){
$('#a').entwine({
bar: function(){return 'a';}
})
})
})
$('#a').entwine('zap.pow').bar().should.equal 'a'
end
it 'revert to base namespacing work (block definition mode)'
$.entwine('zap', function($){
$.entwine('.pow', function($){
$('#a').entwine({
bar: function(){return 'a';}
})
})
})
$('#a').entwine('pow').bar().should.equal 'a'
end
it 'internal to namespace, will look up functions in namespace before in base'
var res = []
$('#a').entwine({
foo: function(){res.push(1);},
bar: function(){res.push(2); this.foo();}
})
$('#a').entwine('bar', function($){return{
foo: function(){res.push(3);},
bar: function(){res.push(4); $(this).foo();}
}})
$('#dom_test div').bar();
res.should.eql [2, 1]
$('#dom_test div').entwine('bar').bar();
res.should.eql [2, 1, 4, 3]
end
it 'internal to namespace, will look up functions in namespace before in base, even in closure'
var res = []
$('#a').entwine({
foo: function(){res.push(1);},
bar: function(){res.push(2); this.foo();}
})
$('#a').entwine('bar', function($){return{
foo: function(){res.push(3);},
bar: function(){res.push(4); $('#a').each(function(){ $(this).foo(); })}
}})
$('#dom_test div').bar();
res.should.eql [2, 1]
$('#dom_test div').entwine('bar').bar();
res.should.eql [2, 1, 4, 3]
end
it 'internal to namespace, will look up functions in namespace before in base, even in onmatch'
var res = []
$('#a').entwine({
foo: function(){res.push(1);},
bar: function(){res.push(2); this.foo();}
})
$('#a').entwine('bar', function($){return{
foo: function(){res.push(3);}
}})
$('#a.d').entwine('bar', function($){return{
onmatch: function(){res.push(4); this.foo();}
}})
$('#dom_test div').bar();
res.should.eql [2, 1]
$('#a').addClass('d');
res.should.eql [2, 1, 4, 3]
end
it 'internal to namespace, will look up functions in base when not present in namespace'
var res = []
$('#a').entwine({
foo: function(){res.push(1);}
})
$('#a').entwine('bar', function($){return{
bar: function(){res.push(2); this.foo();}
}})
$('#dom_test div').entwine('bar').bar();
res.should.eql [2, 1]
end
it 'internal to namespace, will not look up functions in base if present in namespace, even when not applicable to selector'
var res = []
$('#a').entwine('bar', function($){return{
foo: function(){this.bar();}
}})
$('#a').entwine({
bar: function(){res.push(1);}
})
$('span').entwine('bar', function($){return{
bar: function(){res.push(2);}
}})
$('#a').entwine('bar').foo()
res.should.eql []
end
it 'internal to namespace, can be directed to base namespace'
var res = []
$('#a').entwine({
foo: function(){res.push(1);},
bar: function(){res.push(2); this.foo();}
})
$('#a').entwine('bar', function($){return{
foo: function(){res.push(3);},
bar: function(){res.push(4); this.foo(); this.entwine('.').foo();}
}})
$('#dom_test div').bar();
res.should.eql [2, 1]
$('#dom_test div').entwine('bar').bar();
res.should.eql [2, 1, 4, 3, 1]
end
it 'internal to namespace, will look up functions in namespace called the same as a regular jQuery base function'
var res = []
$('#a').entwine('bar', function($){return{
load: function(){res.push(1);},
bar: function(){res.push(2); this.load();}
}})
$('#dom_test div').entwine('bar').bar();
res.should.eql [2, 1]
end
it 'internal to namespace, can be directed to regular jQuery base function'
var res = []
$.fn.testy = function(){ res.push(1); }
$('#a').entwine('bar', function($){return{
testy: function(){res.push(3);},
bar: function(){res.push(2); this.entwine('.').testy();}
}})
$('#dom_test div').entwine('bar').bar();
res.should.eql [2, 1]
end
it 'internal to namespace, can be directed to sub namespace'
var res = []
$.entwine('zap', function($){
$('#a').entwine({
foo: function(){ res.push(1); this.entwine('pow').bar(); }
})
$.entwine('pow', function($){
$('#a').entwine({
bar: function(){ res.push(2); }
})
})
})
$('#dom_test div').entwine('zap').foo();
res.should.eql [1, 2]
end
it 'internal to namespace, can be directed to unrelated namespace'
var res = []
$.entwine('zap', function($){
$('#a').entwine({
foo: function(){ res.push(1); this.entwine('.pow').bar(); }
})
$.entwine('pow', function($){
$('#a').entwine({
bar: function(){ res.push(2); }
})
})
})
$.entwine('pow', function($){
$('#a').entwine({
bar: function(){ res.push(3); }
})
})
$('#dom_test div').entwine('zap').foo();
res.should.eql [1, 3]
end
it 'a function passed out of a namespace will remember its namespace'
var res = []
var func = function(func) {
func.call($('#a, #b'));
}
$('#a, #b').entwine('bar', function($){return{
zap: function(){res.push($(this).attr('id'));},
bar: function(){res.push(2); func(this.zap);}
}})
$('#dom_test #a').entwine('bar').bar();
res.should.eql [2, 'b', 'a']
end
it 'using block functions'
var res = []
$('#a').entwine({
foo: function(){res.push(1);}
})
$('#a').entwine('bar', function($){return{
foo: function(){res.push(3);}
}})
$('#dom_test div').foo();
res.should.eql [1]
$('#dom_test div').entwine('bar', function($){
$(this).foo();
})
res.should.eql [1, 3]
end
end
end

View File

@ -0,0 +1,102 @@
describe 'Entwine'
describe 'Properties'
before
$('body').append('<div id="dom_test"></div>')
end
after
$('#dom_test').remove()
end
before_each
$.entwine.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c"></div><div id="b" class="b c"></div>')
end
it 'can define and get a basic property'
$('#a').entwine({
Foo: null
});
$('.a').getFoo().should.be_null
end
it 'can define and set a basic property'
$('#a').entwine({
Foo: null
});
$('.a').setFoo(1);
$('.a').getFoo().should.equal 1
end
it 'can define a default value'
$('#a').entwine({
Foo: 1
});
$('.a').getFoo().should.equal 1
end
it 'should manage proprties in namespaces without clashing'
$('#a').entwine({
Foo: 1
});
$.entwine('test', function($){
$('#a').entwine({
Foo: 2
});
});
$('.a').getFoo().should.equal 1
$('.a').entwine('test').getFoo().should.equal 2
$('.a').setFoo(4);
$('.a').entwine('test').setFoo(8);
$('.a').getFoo().should.equal 4
$('.a').entwine('test').getFoo().should.equal 8
end
it 'should manage directly setting proprties in namespaces without clashing'
$('#a').entwine({
Foo: null
});
$.entwine('test', function($){
$('#a').entwine({
Foo: null
});
});
$('.a').entwineData('Foo', 4);
$('.a').entwine('test').entwineData('Foo', 8);
$('.a').entwineData('Foo').should.equal 4
$('.a').entwine('test').entwineData('Foo').should.equal 8
end
describe 'jQuery style accessors'
it 'can define and get a basic property'
$('#a').entwine({
Foo: null
});
$('.a').Foo().should.be_null
end
it 'can define and set a basic property'
$('#a').entwine({
Foo: null
});
$('.a').Foo(1);
$('.a').Foo().should.equal 1
end
it 'can define a default value'
$('#a').entwine({
Foo: 1
});
$('.a').Foo().should.equal 1
end
end
end
end

View File

@ -0,0 +1,66 @@
describe 'Entwine'
describe 'Super'
before
$('body').append('<div id="dom_test"></div>')
end
after
$('#dom_test').remove()
end
before_each
$.entwine.clear_all_rules()
$('#dom_test').html('<div id="a" class="a b c">Foo</div><div id="b" class="c d e">Bar</div>')
end
it 'can call the super function'
var a = 1;
$('#a').entwine({
foo: function(){a *= 2;}
});
$('#a.a').entwine({
foo: function(){a += 2; this._super();}
});
$('#a').foo();
a.should.equal 6
end
it 'super to a non-existant class should be ignored'
var a = 1;
$('#a').entwine({
foo: function(){a *= 2; this._super();}
});
$('#a.a').entwine({
foo: function(){a += 2; this._super();}
});
$('#a').foo();
a.should.equal 6
end
it 'can call super from two different functions without screwing up what super points to'
var list = [];
$('#a').entwine({
foo: function(){ list.push('foo'); this.bar(); },
bar: function(){ list.push('bar'); }
});
$('#a.a').entwine({
foo: function(){ list.push('foo2'); this._super(); list.push('foo2'); this._super(); },
bar: function(){ list.push('bar2'); this._super(); }
});
$('#a').foo();
list.should.eql [ 'foo2', 'foo', 'bar2', 'bar', 'foo2', 'foo', 'bar2', 'bar' ]
end
it 'can override (and call via super) a non-entwine jquery function'
var a = 1
$('#a').entwine({
text: function(){ a = this._super(); }
});
$('#a').text();
a.should.equal 'Foo'
$('#b').text().should.equal 'Bar'
end
end
end

View File

@ -10,21 +10,21 @@
<script src="../vendor/jquery.selector/jquery.selector.specifity.js"></script>
<script src="../vendor/jquery.selector/jquery.selector.matches.js"></script>
<script src="../src/jquery.concrete.js"></script>
<script src="../src/jquery.concrete.dommaybechanged.js"></script>
<script src="../src/jquery.concrete.events.js"></script>
<script src="../src/jquery.concrete.ctors.js"></script>
<script src="../src/jquery.concrete.properties.js"></script>
<script src="../src/jquery.entwine.js"></script>
<script src="../src/jquery.entwine.dommaybechanged.js"></script>
<script src="../src/jquery.entwine.events.js"></script>
<script src="../src/jquery.entwine.ctors.js"></script>
<script src="../src/jquery.entwine.properties.js"></script>
<script>
function runSuites() {
JSpec
.exec('spec.concrete.basics.js')
.exec('spec.concrete.namespaces.js')
.exec('spec.concrete.super.js')
.exec('spec.concrete.events.js')
.exec('spec.concrete.ctors.js')
.exec('spec.concrete.properties.js')
.exec('spec.entwine.basics.js')
.exec('spec.entwine.namespaces.js')
.exec('spec.entwine.super.js')
.exec('spec.entwine.events.js')
.exec('spec.entwine.ctors.js')
.exec('spec.entwine.properties.js')
.run()
.report()
}

View File

@ -1,9 +1,9 @@
(function($) {
/* Add the methods to handle constructor & destructor binding to the Namespace class */
$.concrete.Namespace.addMethods({
$.entwine.Namespace.addMethods({
bind_condesc: function(selector, name, func) {
var ctors = this.store.ctors || (this.store.ctors = $.concrete.RuleList()) ;
var ctors = this.store.ctors || (this.store.ctors = $.entwine.RuleList()) ;
var rule;
for (var i = 0 ; i < ctors.length; i++) {
@ -28,17 +28,19 @@
var tmp_i = el.i, tmp_f = el.f;
el.i = i; el.f = one;
try { func.call(namespace.$(el)); }
catch(e) { el.i = tmp_i; el.f = tmp_f; }
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;
}
}
});
$.concrete.Namespace.addHandler({
$.entwine.Namespace.addHandler({
order: 30,
bind: function(selector, k, v) {
@ -61,29 +63,35 @@
*/
$(document).bind('DOMMaybeChanged', function(){
// For every namespace
for (var k in $.concrete.namespaces) {
for (var k in $.entwine.namespaces) {
// That has constructors or destructors
var ctors = $.concrete.namespaces[k].store.ctors;
var ctors = $.entwine.namespaces[k].store.ctors;
if (ctors) {
// Keep a record of elements that have matched already
var matched = $([]), match, add, rem;
var matched = $([]), add, rem, res, rule, sel, ctor, dtor;
// Stepping through each selector from most to least specific
var j = ctors.length;
while (j--) {
// Build some quick-acccess variables
var sel = ctors[j].selector.selector, ctor = ctors[j].onmatch; dtor = ctors[j].onunmatch;
// Build some quick-access variables
rule = ctors[j];
sel = rule.selector.selector;
ctor = rule.onmatch;
dtor = rule.onunmatch;
// Get the list of elements that match this selector, that haven't yet matched a more specific selector
res = add = $(sel).not(matched);
// If this selector has a list of elements it matched against last time
if (ctors[j].cache) {
// 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(ctors[j].cache);
// Find the ones that are gone this time
rem = ctors[j].cache.not(res);
// And call the desctructor on them
if (rem.length && dtor) ctors.onunmatchproxy(rem, j, dtor);
add = res.not(rule.cache);
if (dtor) {
// Find the ones that are gone this time
rem = rule.cache.not(res);
// And call the destructor on them
if (rem.length) ctors.onunmatchproxy(rem, j, dtor);
}
}
// Call the constructor on the newly matched ones
@ -96,7 +104,7 @@
}
}
}
})
});
})(jQuery);

View File

@ -10,9 +10,9 @@
var triggerEvent = function() {
$(document).triggerHandler('DOMMaybeChanged');
check_id = null;
}
};
$.extend($.concrete, {
$.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
@ -20,7 +20,7 @@
*/
synchronous_mode: function() {
if (check_id) clearTimeout(check_id); check_id = null;
runSoon = function(func, delay){ func.call(this); return null; }
runSoon = function(func, delay){ func.call(this); return null; };
},
/**
@ -39,8 +39,8 @@
var rv = old.apply(this, arguments);
if (!check_id) check_id = runSoon(triggerEvent, 100);
return rv;
}
})
};
});
}
function registerSetterGetterFunction() {
@ -50,8 +50,8 @@
var rv = old.apply(this, arguments);
if (!check_id && (b !== undefined || typeof a != 'string')) check_id = runSoon(triggerEvent, 100);
return rv;
}
})
};
});
}
// Register core DOM manipulation methods
@ -59,6 +59,6 @@
registerSetterGetterFunction('attr');
// And on DOM ready, trigger matching once
$(function(){ triggerEvent(); })
$(function(){ triggerEvent(); });
})(jQuery);

View File

@ -8,24 +8,26 @@
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 */
$.concrete.Namespace.addMethods({
$.entwine.Namespace.addMethods({
build_event_proxy: function(name) {
var one = this.one(name, 'func');
var prxy = function(e, originalevent) {
e = originalevent || e;
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 != document && !e.isPropagationStopped()) {
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(); }
@ -44,7 +46,7 @@
var el = e.target;
var rel = e.relatedTarget;
while (el && el != document && !e.isPropagationStopped()) {
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;
@ -92,7 +94,7 @@
// And if we decided that a change happened, do the actual triggering
if (e.type == 'change') {
while (el && el != document && !e.isPropagationStopped()) {
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(); }
@ -106,7 +108,7 @@
},
bind_event: function(selector, name, func, event) {
var funcs = this.store[name] || (this.store[name] = $.concrete.RuleList()) ;
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;
@ -128,19 +130,22 @@
}
break;
case 'onsubmit':
event = 'delegated_submit';
event = 'delegatedSubmit';
break;
case 'onfocus':
case 'onblur':
$.concrete.warn('Event '+event+' not supported - using focusin / focusout instead', $.concrete.WARN_LEVEL_IMPORTANT);
$.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, proxies[name]);
$(document).bind(event+'.entwine', proxies[name]);
}
}
});
$.concrete.Namespace.addHandler({
$.entwine.Namespace.addHandler({
order: 40,
bind: function(selector, k, v){
@ -153,10 +158,14 @@
}
});
// Find all forms and bind onsubmit to trigger on the document too. This is the only event that can't be grabbed via delegation.
// 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){ $(document).triggerHandler('delegated_submit', e); } // The function that handles the delegation
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');

View File

@ -4,28 +4,30 @@ var console;
var namespaces = {};
$.concrete = function() {
$.fn.concrete.apply(null, arguments);
}
$.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($.concrete, {
$.extend($.entwine, {
/**
* Get all the namespaces. Useful for introspection? Internal interface of Namespace not guaranteed consistant
*/
namespaces: namespaces,
/**
* Remove all concrete rules
* Remove all entwine rules
*/
clear_all_rules: function() {
// Remove proxy functions
for (var k in $.fn) { if ($.fn[k].concrete) delete $.fn[k] ; }
for (var k in $.fn) { if ($.fn[k].entwine) delete $.fn[k] ; }
// Remove bound events - TODO: Make this pluggable, so this code can be moved to jquery.entwine.events.js
$(document).unbind('.entwine');
// Remove namespaces, and start over again
namespaces = $.concrete.namespaces = {};
namespaces = $.entwine.namespaces = {};
},
WARN_LEVEL_NONE: 0,
@ -39,10 +41,21 @@ var console;
/** Utility to optionally display warning messages depending on level */
warn: function(message, level) {
if (level <= $.concrete.warningLevel && console && console.log) {
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);
}
}
});
@ -68,9 +81,9 @@ var console;
(as[1] - bs[1]) ||
(as[2] - bs[2]) ||
(a.rulecount - b.rulecount) ;
}
};
$.concrete.RuleList = function() {
$.entwine.RuleList = function() {
var list = [];
list.addRule = function(selector, name){
@ -83,16 +96,16 @@ var console;
};
return list;
}
};
var handlers = [];
/**
* A Namespace holds all the information needed for adding concrete methods to a namespace (including the _null_ namespace)
* A Namespace holds all the information needed for adding entwine methods to a namespace (including the _null_ namespace)
*/
$.concrete.Namespace = Base.extend({
$.entwine.Namespace = Base.extend({
init: function(name){
if (name && !name.match(/^[A-Za-z0-9.]+$/)) $.concrete.warn('Concrete namespace '+name+' is not formatted as period seperated identifiers', $.concrete.WARN_LEVEL_BESTPRACTISE);
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;
@ -101,12 +114,12 @@ var console;
namespaces[name] = this;
if (name == "__base") {
this.injectee = $.fn
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
var subfn = function(){}
var subfn = function(){};
this.injectee = subfn.prototype = new $();
// And then we provide an overriding $ that returns objects of our new Class, and an overriding pushStack to catch further selection building
@ -120,7 +133,7 @@ var console;
rv.selector = jq.selector; rv.context = jq.context; var i = rv.length = jq.length;
while (i--) rv[i] = jq[i];
return rv;
}
};
this.injectee.pushStack = function(elems, name, selector){
var ret = bound$(elems);
@ -133,25 +146,25 @@ var console;
// Return the newly-formed element set
return ret;
}
};
// Copy static functions through from $ to this.$ so e.g. $.ajax still works
// @bug, @cantfix: Any class functions added to $ after this call won't get mirrored through
$.extend(this.$, $);
// We override concrete to inject the name of this namespace when defining blocks inside this namespace
var concrete_wrapper = this.injectee.concrete = function(spacename) {
// 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.concrete.apply(this, args);
}
return $.fn.entwine.apply(this, args);
};
this.$.concrete = function() {
concrete_wrapper.apply(null, arguments);
}
this.$.entwine = function() {
entwine_wrapper.apply(null, arguments);
};
for (var i = 0; i < handlers.length; i++) {
var handler = handlers[i], builder;
@ -162,10 +175,10 @@ var console;
for (var k in overrides) this.injectee[k] = overrides[k];
}
// Inject $.concrete function overrides
// Inject $.entwine function overrides
if (builder = handler.namespaceStaticOverrides) {
var overrides = builder(this);
for (var k in overrides) this.$.concrete[k] = overrides[k];
for (var k in overrides) this.$.entwine[k] = overrides[k];
}
}
}
@ -176,8 +189,9 @@ var console;
* 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) {
one: function(name, funcprop, basefunc) {
var namespace = this;
var funcs = this.store[name];
@ -192,7 +206,9 @@ var console;
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;
},
@ -201,9 +217,10 @@ var console;
* 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) {
var one = this.one(name, 'func');
build_proxy: function(name, basefunc) {
var one = this.one(name, 'func', basefunc);
var prxy = function() {
var rv, ctx = $(this);
@ -217,17 +234,17 @@ var console;
},
bind_proxy: function(selector, name, func) {
var rulelist = this.store[name] || (this.store[name] = $.concrete.RuleList());
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_proxy(name);
this.injectee[name].concrete = true;
if (!this.injectee.hasOwnProperty(name) || !this.injectee[name].entwine) {
this.injectee[name] = this.build_proxy(name, this.injectee.hasOwnProperty(name) ? this.injectee[name] : null);
this.injectee[name].entwine = true;
}
if (!this.injectee[name].concrete) {
$.concrete.warn('Warning: Concrete function '+name+' clashes with regular jQuery function - concrete function will not be callable directly on jQuery object', $.concrete.WARN_LEVEL_IMPORTANT);
if (!this.injectee[name].entwine) {
$.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);
}
},
@ -259,12 +276,12 @@ var console;
* 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
*/
$.concrete.Namespace.addHandler = function(handler) {
$.entwine.Namespace.addHandler = function(handler) {
for (var i = 0; i < handlers.length && handlers[i].order < handler.order; i++) { /* Pass */ }
handlers.splice(i, 0, handler);
}
};
$.concrete.Namespace.addHandler({
$.entwine.Namespace.addHandler({
order: 50,
bind: function(selector, k, v){
@ -277,20 +294,22 @@ var console;
$.extend($.fn, {
/**
* Main concrete function. Used for new definitions, calling into a namespace (or forcing the base namespace) and entering a using block
* Main entwine function. Used for new definitions, calling into a namespace (or forcing the base namespace) and entering a using block
*
*/
concrete: function(spacename) {
entwine: function(spacename) {
var i = 0;
var selector = this.selector ? $.selector(this.selector) : null;
/* 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 || $.concrete.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] || $.concrete.Namespace(spacename);
if (spacename) namespace = namespaces[spacename] || $.entwine.Namespace(spacename);
i=1;
}
@ -298,19 +317,19 @@ var console;
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 concrete definition
// 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) $.concrete.warn('Function block inside concrete definition does not take $ argument properly', $.concrete.WARN_LEVEL_IMPORTANT);
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 concrete definition hash, inject it into 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 $.concrete.warn('Concrete block given to concrete call without selector. Make sure you call $(selector).concrete when defining blocks', $.concrete.WARN_LEVEL_IMPORTANT);
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 */
@ -318,7 +337,7 @@ var console;
},
/**
* Calls the next most specific version of the current concrete method
* Calls the next most specific version of the current entwine method
*/
_super: function(){
var rv, i = this.length;

View File

@ -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);

View File

@ -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() { return this.entwineData(k) || v ; });
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);

View File

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 457 B

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 499 B

After

Width:  |  Height:  |  Size: 499 B

View File

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 524 B

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 64 B

After

Width:  |  Height:  |  Size: 64 B

View File

Before

Width:  |  Height:  |  Size: 300 B

After

Width:  |  Height:  |  Size: 300 B

View File

Before

Width:  |  Height:  |  Size: 202 B

After

Width:  |  Height:  |  Size: 202 B

View File

Before

Width:  |  Height:  |  Size: 516 B

After

Width:  |  Height:  |  Size: 516 B

View File

@ -12,17 +12,18 @@ var Base;
(function(){
var marker = {}, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
var marker = {}, fnTest = /xyz/.test(function(){var xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
Base = function(){};
Base.addMethod = function(name, func) {
var _super = this._super;
if (_super && fnTest.test(func)) {
var parent = this._super && this._super.prototype;
if (parent && fnTest.test(func)) {
this.prototype[name] = function(){
var tmp = this._super;
this._super = _super[name];
this._super = parent[name];
try {
var ret = func.apply(this, arguments);
}
@ -30,17 +31,25 @@ var Base;
this._super = tmp;
}
return ret;
}
};
}
else this.prototype[name] = func;
}
};
Base.addMethods = function(props) {
for (var name in props) {
if (typeof props[name] == 'function') this.addMethod(name, props[name]);
else this.prototype[name] = props[name];
}
}
};
Base.subclassOf = function(parentkls) {
var kls = this;
while (kls) {
if (kls === parentkls) return true;
kls = kls._super;
}
};
// Create a new Class that inherits from this class
Base.extend = function(props) {
@ -55,18 +64,21 @@ var Base;
else {
var ret = new Kls(marker); if (ret.init) ret.init.apply(ret, arguments); return ret;
}
}
};
// Add the common class variables and methods
Kls.constructor = Kls;
Kls.extend = Base.extend;
Kls.addMethod = Base.addMethod;
Kls.addMethods = Base.addMethods;
Kls._super = this.prototype;
Kls.subclassOf = Base.subclassOf;
Kls._super = this;
// Attach the parent object to the inheritance chain
Kls.prototype = new this(marker);
Kls.prototype.constructor = Kls;
// Copy the properties over onto the new prototype
Kls.addMethods(props);

View File

@ -21,7 +21,7 @@
WITHN: /([-+])?(INTEGER)?(n)\s*(?:([-+])\s*(INTEGER))?/,
WITHOUTN: /([-+])?(INTEGER)/
}
};
var rx = {
not: /:not\(/,
@ -38,12 +38,12 @@
comb: /\s*(\+|~|>)\s*|\s+/,
comma: /\s*,\s*/,
important: /\s+!important\s*$/
}
};
/* Replace placeholders with actual regex, and mark all as case insensitive */
var token = /[A-Z][A-Z0-9]+/;
for (var k in rx) {
var src = rx[k].source;
var m, src = rx[k].source;
while (m = src.match(token)) src = src.replace(m[0], tokens[m[0]].source);
rx[k] = new RegExp(src, 'gi');
}
@ -77,7 +77,7 @@
done: function() {
return this.pos == this.str.length;
}
})
});
/* A base class that all Selectors inherit off */
var SelectorBase = Base.extend({});
@ -104,7 +104,7 @@
/* Then for each selection type, try and find a match */
do {
if (m = selector.match(rx.not)) {
this.nots[this.nots.length] = SelectorsGroup().parse(selector)
this.nots[this.nots.length] = SelectorsGroup().parse(selector);
if (!(m = selector.match(rx.not_end))) {
throw 'Invalid :not term in selector';
}
@ -130,7 +130,7 @@
return this;
}
})
});
/**
* A class representing a Selector, as per the CSS3 selector spec
@ -178,11 +178,11 @@
if (!cons.done()) throw 'Could not parse selector - ' + cons.showpos() ;
else return res;
}
};
$.selector.SelectorBase = SelectorBase;
$.selector.SimpleSelector = SimpleSelector;
$.selector.Selector = Selector;
$.selector.SelectorsGroup = SelectorsGroup;
})(jQuery)
})(jQuery);

View File

@ -18,16 +18,16 @@ 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_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' };
var testFunc = function(){ return 'good'; };
if ((new Function('',funcToString(testFunc)))() != 'good') funcToString = false;
}
catch(e) { funcToString = false; console.log(e.message);/*pass*/ }
@ -41,21 +41,26 @@ Sizzle is good for finding elements for a selector, but not so good for telling
var join = function(js) {
return js.join('\n');
}
};
var join_complex = function(js) {
code = new String(js.join('\n')); // String objects can have properties set. strings can't
var code = new String(js.join('\n')); // String objects can have properties set. strings can't
code.complex = true;
return code;
}
};
/**** ATTRIBUTE ACCESSORS ****/
// Not all attribute names can be used as identifiers, so we encode any non-acceptable characters as hex
var varForAttr = function(attr) {
return '_' + attr.replace(/^[^A-Za-z]|[^A-Za-z0-9]/g, function(m){ return '_0x' + m.charCodeAt(0).toString(16) + '_'; });
};
var getAttr;
// Good browsers
if (!getAttributeDodgy) {
getAttr = function(attr){ return 'var _'+attr+' = el.getAttribute("'+attr+'");' ; }
getAttr = function(attr){ return 'var '+varForAttr(attr)+' = el.getAttribute("'+attr+'");' ; };
}
// IE 6, 7
else {
@ -64,21 +69,21 @@ Sizzle is good for finding elements for a selector, but not so good for telling
getAttr = function(attr) {
var ieattr = getAttrIEMap[attr] || attr;
return 'var _'+attr+' = el.getAttribute("'+ieattr+'",2) || (el.getAttributeNode("'+attr+'")||{}).nodeValue;';
}
return 'var '+varForAttr(attr)+' = el.getAttribute("'+ieattr+'",2) || (el.getAttributeNode("'+attr+'")||{}).nodeValue;';
};
}
/**** ATTRIBUTE COMPARITORS ****/
var attrchecks = {
'-': '!_K',
'=': '_K != "V"',
'!=': '_K == "V"',
'~=': '__K.indexOf(" V ") == -1',
'^=': '!_K || _K.indexOf("V") != 0',
'*=': '!_K || _K.indexOf("V") == -1',
'$=': '!_K || _K.substr(_K.length-"V".length) != "V"'
}
'-': '!K',
'=': 'K != "V"',
'!=': 'K == "V"',
'~=': '_WS_K.indexOf(" V ") == -1',
'^=': '!K || K.indexOf("V") != 0',
'*=': '!K || K.indexOf("V") == -1',
'$=': '!K || K.substr(K.length-"V".length) != "V"'
};
/**** STATE TRACKER ****/
@ -106,7 +111,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
return join([
'while(el = el.previousSibling){',
'if (el.nodeType != 1) continue;',
body,
body
]);
},
parent: function() {
@ -130,7 +135,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
uses_wsattr: function(attr) {
if (this.wsattrs[attr]) return;
this.wsattrs[attr] = true;
return join([this.uses_attr(attr), 'var __'+attr+' = " "+_'+attr+'+" ";']);
return join([this.uses_attr(attr), 'var _WS_'+varForAttr(attr)+' = " "+'+varForAttr(attr)+'+" ";']);
},
save: function(lbl) {
@ -147,7 +152,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
var pseudoclschecks = {
'first-child': join([
'var cel = el;',
'while(cel = cel.previousSibling){ if (cel.nodeType === 1) BAD; }',
'while(cel = cel.previousSibling){ if (cel.nodeType === 1) BAD; }'
]),
'last-child': join([
'var cel = el;',
@ -158,7 +163,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
'var i = 1, cel = el;',
'while(cel = cel.previousSibling){',
'if (cel.nodeType === 1) i++;',
'}',
'}'
]);
if (a == 0) return join([
@ -207,15 +212,15 @@ Sizzle is good for finding elements for a selector, but not so good for telling
/* Check against class names */
$.each(this.classes, function(i, cls){
js[js.length] = 'if (__class.indexOf(" '+cls+' ") == -1) BAD;';
})
js[js.length] = 'if (_WS__class.indexOf(" '+cls+' ") == -1) BAD;';
});
}
/* Check against attributes */
$.each(this.attrs, function(i, attr){
js[js.length] = (attr[1] == '~=') ? el.uses_wsattr(attr[0]) : el.uses_attr(attr[0]);
var check = attrchecks[ attr[1] || '-' ];
check = check.replace( /K/g, attr[0]).replace( /V/g, attr[2] && attr[2].match(STARTS_WITH_QUOTES) ? attr[2].slice(1,-1) : attr[2] );
check = check.replace( /K/g, varForAttr(attr[0])).replace( /V/g, attr[2] && attr[2].match(STARTS_WITH_QUOTES) ? attr[2].slice(1,-1) : attr[2] );
js[js.length] = 'if ('+check+') BAD;';
});
@ -232,7 +237,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
el.save(lbl),
func,
el.restore(lbl)
])
]);
js[js.length] = func;
});
@ -248,7 +253,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
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] = 'if (!$.find.selectors.filters.'+pscls[0]+'(el)) BAD;';
}
}
});
@ -266,7 +271,7 @@ Sizzle is good for finding elements for a selector, but not so good for telling
return join([
'l'+(++lbl_id)+':{',
f.replace(GOOD, 'break l'+lbl_id),
'}',
'}'
]);
else
return f.replace(GOOD, '');
@ -314,11 +319,11 @@ Sizzle is good for finding elements for a selector, but not so good for telling
};
$.selector.Selector.addMethod('compile', function(el) {
l = this.parts.length;
var l = this.parts.length;
expr = this.parts[--l].compile(el);
var expr = this.parts[--l].compile(el);
while (l) {
combinator = this.parts[--l];
var combinator = this.parts[--l];
expr = combines[combinator](el, this.parts[--l], as_subexpr(expr));
}

View File

@ -13,7 +13,7 @@
});
return this.spec = spec;
})
});
$.selector.Selector.addMethod('specifity', function(){
if (this.spec) return this.spec;
@ -25,7 +25,7 @@
});
return this.spec = spec;
})
});
$.selector.SelectorsGroup.addMethod('specifity', function(){
if (this.spec) return this.spec;
@ -36,7 +36,7 @@
});
return this.spec = spec;
})
});
})(jQuery);

View File

Before

Width:  |  Height:  |  Size: 154 B

After

Width:  |  Height:  |  Size: 154 B

View File

Before

Width:  |  Height:  |  Size: 321 B

After

Width:  |  Height:  |  Size: 321 B

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

Before

Width:  |  Height:  |  Size: 145 B

After

Width:  |  Height:  |  Size: 145 B