2011-05-12 08:11:41 +02:00
try {
console . log ;
}
catch ( e ) {
window . console = undefined ;
}
2009-11-21 03:26:09 +01:00
( function ( $ ) {
2009-11-21 03:33:06 +01:00
var namespaces = { } ;
2009-11-21 03:26:09 +01:00
2010-04-13 07:45:29 +02:00
$ . entwine = function ( ) {
$ . fn . entwine . apply ( null , arguments ) ;
} ;
2009-11-21 03:33:06 +01:00
/ * *
* 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
* /
2010-04-13 07:45:29 +02:00
$ . extend ( $ . entwine , {
2009-11-21 03:26:09 +01:00
/ * *
2009-11-21 03:33:06 +01:00
* Get all the namespaces . Useful for introspection ? Internal interface of Namespace not guaranteed consistant
2009-11-21 03:26:09 +01:00
* /
2009-11-21 03:33:06 +01:00
namespaces : namespaces ,
2009-11-21 03:26:09 +01:00
2009-11-21 03:33:06 +01:00
/ * *
2010-04-13 07:45:29 +02:00
* Remove all entwine rules
2009-11-21 03:33:06 +01:00
* /
clear _all _rules : function ( ) {
// Remove proxy functions
2011-02-23 22:03:38 +01:00
for ( var k in $ . fn ) { if ( $ . fn [ k ] . isentwinemethod ) delete $ . fn [ k ] ; }
2010-04-13 07:45:29 +02:00
// Remove bound events - TODO: Make this pluggable, so this code can be moved to jquery.entwine.events.js
$ ( document ) . unbind ( '.entwine' ) ;
2009-11-21 03:33:06 +01:00
// Remove namespaces, and start over again
2010-04-13 07:45:29 +02:00
namespaces = $ . entwine . namespaces = { } ;
2009-11-21 03:26:09 +01:00
} ,
2009-11-21 03:33:06 +01:00
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 ) {
2010-04-13 07:45:29 +02:00
if ( level <= $ . entwine . warningLevel && console && console . warn ) {
2009-11-21 03:33:06 +01:00
console . warn ( message ) ;
if ( console . trace ) console . trace ( ) ;
2009-11-21 03:26:09 +01:00
}
2010-04-13 07:45:29 +02:00
} ,
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 ) ;
}
2009-11-21 03:26:09 +01:00
}
} ) ;
2009-11-21 03:33:06 +01:00
2009-11-21 03:26:09 +01:00
2009-11-21 03:33:06 +01:00
/** Stores a count of definitions, so that we can sort identical selectors by definition order */
var rulecount = 0 ;
2009-11-21 03:26:09 +01:00
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 ) ;
2010-04-13 07:45:29 +02:00
} ;
2009-11-21 03:26:09 +01:00
2010-04-13 07:45:29 +02:00
$ . entwine . RuleList = function ( ) {
2009-11-21 03:33:06 +01:00
var list = [ ] ;
list . addRule = function ( selector , name ) {
var rule = Rule ( selector , name ) ;
list [ list . length ] = rule ;
list . sort ( Rule . compare ) ;
return rule ;
} ;
return list ;
2010-04-13 07:45:29 +02:00
} ;
2009-11-21 03:26:09 +01:00
2009-11-21 03:33:06 +01:00
var handlers = [ ] ;
/ * *
2010-04-13 07:45:29 +02:00
* A Namespace holds all the information needed for adding entwine methods to a namespace ( including the _null _ namespace )
2009-11-21 03:33:06 +01:00
* /
2010-04-13 07:45:29 +02:00
$ . entwine . Namespace = Base . extend ( {
2009-11-21 03:26:09 +01:00
init : function ( name ) {
2010-04-13 07:45:29 +02:00
if ( name && ! name . match ( /^[A-Za-z0-9.]+$/ ) ) $ . entwine . warn ( 'Entwine namespace ' + name + ' is not formatted as period seperated identifiers' , $ . entwine . WARN _LEVEL _BESTPRACTISE ) ;
2009-11-21 03:26:09 +01:00
name = name || '__base' ;
this . name = name ;
this . store = { } ;
namespaces [ name ] = this ;
2009-11-21 03:31:55 +01:00
if ( name == "__base" ) {
2010-04-13 07:45:29 +02:00
this . injectee = $ . fn ;
2009-11-21 03:31:55 +01:00
this . $ = $ ;
}
else {
// We're in a namespace, so we build a Class that subclasses the jQuery Object Class to inject namespace functions into
2011-02-23 22:03:38 +01:00
// jQuery 1.5 already provides a nice way to subclass, so use it
if ( $ . sub ) {
this . $ = $ . sub ( ) ;
this . injectee = this . $ . prototype ;
}
// For jQuery < 1.5 we have to do it ourselves
else {
var subfn = function ( ) { } ;
this . injectee = subfn . prototype = new $ ;
// And then we provide an overriding $ that returns objects of our new Class, and an overriding pushStack to catch further selection building
var bound$ = this . $ = function ( a ) {
// Try the simple way first
var jq = $ . fn . init . apply ( new subfn ( ) , arguments ) ;
if ( jq instanceof subfn ) return jq ;
2009-11-21 03:33:06 +01:00
2011-02-23 22:03:38 +01:00
// 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 ) ;
2009-11-21 03:33:06 +01:00
2011-02-23 22:03:38 +01:00
// Add the old object onto the stack (as a reference)
ret . prevObject = this ;
ret . context = this . context ;
2009-11-21 03:33:06 +01:00
2011-02-23 22:03:38 +01:00
if ( name === "find" ) ret . selector = this . selector + ( this . selector ? " " : "" ) + selector ;
else if ( name ) ret . selector = this . selector + "." + name + "(" + selector + ")" ;
2009-11-21 03:33:06 +01:00
2011-02-23 22:03:38 +01:00
// Return the newly-formed element set
return ret ;
} ;
2009-11-21 03:33:06 +01:00
2011-02-23 22:03:38 +01:00
// 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 . $ , $ ) ;
}
2009-11-21 03:33:06 +01:00
2010-04-13 07:45:29 +02:00
// We override entwine to inject the name of this namespace when defining blocks inside this namespace
var entwine _wrapper = this . injectee . entwine = function ( spacename ) {
2009-11-21 03:33:06 +01:00
var args = arguments ;
if ( ! spacename || typeof spacename != 'string' ) { args = $ . makeArray ( args ) ; args . unshift ( name ) ; }
else if ( spacename . charAt ( 0 ) != '.' ) args [ 0 ] = name + '.' + spacename ;
2010-04-13 07:45:29 +02:00
return $ . fn . entwine . apply ( this , args ) ;
} ;
2009-11-21 03:33:06 +01:00
2010-04-13 07:45:29 +02:00
this . $ . entwine = function ( ) {
entwine _wrapper . apply ( null , arguments ) ;
} ;
2009-11-21 03:33:06 +01:00
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 ] ;
}
2010-04-13 07:45:29 +02:00
// Inject $.entwine function overrides
2009-11-21 03:33:06 +01:00
if ( builder = handler . namespaceStaticOverrides ) {
var overrides = builder ( this ) ;
2010-04-13 07:45:29 +02:00
for ( var k in overrides ) this . $ . entwine [ k ] = overrides [ k ] ;
2009-11-21 03:33:06 +01:00
}
}
2009-11-21 03:26:09 +01:00
}
} ,
/ * *
* 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
2010-04-13 07:45:29 +02:00
* @ param { function } basefunc - the non - entwine function to use as the catch - all function at the bottom of the stack
2009-11-21 03:26:09 +01:00
* /
2010-04-13 07:45:29 +02:00
one : function ( name , funcprop , basefunc ) {
2009-11-21 03:26:09 +01:00
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 ;
}
}
2010-04-13 07:45:29 +02:00
// 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 ) ;
} ;
2009-11-21 03:26:09 +01:00
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
2010-04-13 07:45:29 +02:00
* @ param { function } basefunc - the non - entwine function to use as the catch - all function at the bottom of the stack
2009-11-21 03:26:09 +01:00
* /
2010-04-13 07:45:29 +02:00
build _proxy : function ( name , basefunc ) {
var one = this . one ( name , 'func' , basefunc ) ;
2009-11-21 03:26:09 +01:00
var prxy = function ( ) {
2009-11-21 03:31:55 +01:00
var rv , ctx = $ ( this ) ;
2009-11-21 03:26:09 +01:00
var i = ctx . length ;
while ( i -- ) rv = one ( ctx [ i ] , arguments ) ;
return rv ;
} ;
return prxy ;
} ,
bind _proxy : function ( selector , name , func ) {
2010-04-13 07:45:29 +02:00
var rulelist = this . store [ name ] || ( this . store [ name ] = $ . entwine . RuleList ( ) ) ;
2009-11-21 03:26:09 +01:00
2009-11-21 03:33:06 +01:00
var rule = rulelist . addRule ( selector , name ) ; rule . func = func ;
2009-11-21 03:26:09 +01:00
2011-02-23 22:03:38 +01:00
if ( ! this . injectee . hasOwnProperty ( name ) || ! this . injectee [ name ] . isentwinemethod ) {
2010-04-13 07:45:29 +02:00
this . injectee [ name ] = this . build _proxy ( name , this . injectee . hasOwnProperty ( name ) ? this . injectee [ name ] : null ) ;
2011-02-23 22:03:38 +01:00
this . injectee [ name ] . isentwinemethod = true ;
2009-11-21 03:31:55 +01:00
}
2011-02-23 22:03:38 +01:00
if ( ! this . injectee [ name ] . isentwinemethod ) {
2010-04-13 07:45:29 +02:00
$ . 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 ) ;
2009-11-21 03:26:09 +01:00
}
} ,
add : function ( selector , data ) {
2009-11-21 03:33:06 +01:00
// For every item in the hash, try ever method handler, until one returns true
for ( var k in data ) {
var v = data [ k ] ;
2009-11-21 03:26:09 +01:00
2009-11-21 03:33:06 +01:00
for ( var i = 0 ; i < handlers . length ; i ++ ) {
if ( handlers [ i ] . bind && handlers [ i ] . bind . call ( this , selector , k , v ) ) break ;
2009-11-21 03:26:09 +01:00
}
}
} ,
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 ;
}
} ) ;
2009-11-21 03:33:06 +01:00
2009-11-21 03:26:09 +01:00
/ * *
2009-11-21 03:33:06 +01:00
* 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
2009-11-21 03:26:09 +01:00
* /
2010-04-13 07:45:29 +02:00
$ . entwine . Namespace . addHandler = function ( handler ) {
2009-11-21 03:33:06 +01:00
for ( var i = 0 ; i < handlers . length && handlers [ i ] . order < handler . order ; i ++ ) { /* Pass */ }
handlers . splice ( i , 0 , handler ) ;
2010-04-13 07:45:29 +02:00
} ;
2009-11-21 03:26:09 +01:00
2010-04-13 07:45:29 +02:00
$ . entwine . Namespace . addHandler ( {
2009-11-21 03:33:06 +01:00
order : 50 ,
2009-11-21 03:26:09 +01:00
2009-11-21 03:33:06 +01:00
bind : function ( selector , k , v ) {
if ( $ . isFunction ( v ) ) {
this . bind _proxy ( selector , k , v ) ;
return true ;
}
}
} ) ;
$ . extend ( $ . fn , {
2009-11-21 03:26:09 +01:00
/ * *
2010-04-13 07:45:29 +02:00
* Main entwine function . Used for new definitions , calling into a namespace ( or forcing the base namespace ) and entering a using block
2009-11-21 03:33:06 +01:00
*
2009-11-21 03:26:09 +01:00
* /
2010-04-13 07:45:29 +02:00
entwine : function ( spacename ) {
2009-11-21 03:33:06 +01:00
var i = 0 ;
2010-04-13 07:45:29 +02:00
/ * D o n ' t a c t u a l l y w o r k o u t s e l e c t o r u n t i l w e t r y a n d d e f i n e s o m e t h i n g o n i t - w e m i g h t b e o p e n i n g a n a m e s p a c e o n a n f u n c t i o n - t r a v e r e s e d o b j e c t
which have non - standard selectors like . parents ( . foo ) . slice ( 0 , 1 ) * /
var selector = null ;
2009-11-21 03:26:09 +01:00
2009-11-21 03:33:06 +01:00
/* By default we operator on the base namespace */
2010-04-13 07:45:29 +02:00
var namespace = namespaces . _ _base || $ . entwine . Namespace ( ) ;
2009-11-21 03:33:06 +01:00
/* 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 ) ;
2010-04-13 07:45:29 +02:00
if ( spacename ) namespace = namespaces [ spacename ] || $ . entwine . Namespace ( spacename ) ;
2009-11-21 03:33:06 +01:00
i = 1 ;
}
2009-11-21 03:26:09 +01:00
2009-11-21 03:33:06 +01:00
/* All remaining arguments should either be using blocks or definition hashs */
while ( i < arguments . length ) {
var res = arguments [ i ++ ] ;
2010-04-13 07:45:29 +02:00
// If it's a function, call it - either it's a using block or it's a namespaced entwine definition
2009-11-21 03:33:06 +01:00
if ( $ . isFunction ( res ) ) {
2010-04-13 07:45:29 +02:00
if ( res . length != 1 ) $ . entwine . warn ( 'Function block inside entwine definition does not take $ argument properly' , $ . entwine . WARN _LEVEL _IMPORTANT ) ;
2009-11-21 03:33:06 +01:00
res = res . call ( namespace . $ ( this ) , namespace . $ ) ;
}
2010-04-13 07:45:29 +02:00
// If we have a entwine definition hash, inject it into namespace
2009-11-21 03:33:06 +01:00
if ( res ) {
2010-04-13 07:45:29 +02:00
if ( selector === null ) selector = this . selector ? $ . selector ( this . selector ) : false ;
2009-11-21 03:33:06 +01:00
if ( selector ) namespace . add ( selector , res ) ;
2010-04-13 07:45:29 +02:00
else $ . entwine . warn ( 'Entwine block given to entwine call without selector. Make sure you call $(selector).entwine when defining blocks' , $ . entwine . WARN _LEVEL _IMPORTANT ) ;
2009-11-21 03:33:06 +01:00
}
}
2009-11-21 03:26:09 +01:00
2009-11-21 03:33:06 +01:00
/* Finally, return the jQuery object 'this' refers to, wrapped in the new namespace */
return namespace . $ ( this ) ;
2009-11-21 03:26:09 +01:00
} ,
/ * *
2010-04-13 07:45:29 +02:00
* Calls the next most specific version of the current entwine method
2009-11-21 03:31:55 +01:00
* /
2009-11-21 03:33:06 +01:00
_super : function ( ) {
var rv , i = this . length ;
while ( i -- ) {
var el = this [ 0 ] ;
rv = el . f ( el , arguments , el . i ) ;
2009-11-21 03:26:09 +01:00
}
2009-11-21 03:33:06 +01:00
return rv ;
2009-11-21 03:26:09 +01:00
}
2009-11-21 03:33:06 +01:00
} ) ;
2009-11-21 03:26:09 +01:00
} ) ( jQuery ) ;