Created
January 19, 2013 23:09
-
-
Save DeadWisdom/4575758 to your computer and use it in GitHub Desktop.
Core functionality of Tea
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Tea | |
Complex UI framework based on jQuery. | |
Copyright (c) 2012 Brantley Harris. All rights reserved. | |
**/ | |
(function() { | |
var Tea = window.Tea = (window.Tea == undefined ? {root: ''} : window.Tea); | |
/** Tea.require(...) | |
Imports the given arguments by appending <script> or <style> tags to the head. | |
Note: Importing is done relative to the page we're on, not the script. | |
Note: The required script is loaded sometime AFTER the requiring script, so you can't use | |
the provided namespace (functions and variables) right away. | |
arguments: | |
Strings of urls to the given resource. If the string ends with .css, it is added with | |
a <style> tag; if it's a .js, it is added with a <script> tag. | |
**/ | |
Tea.require = function() | |
{ | |
for(var i=0; i < arguments.length; i++) | |
{ | |
var src = Tea.root + arguments[i]; | |
if (Tea.require.map[src]) | |
return; | |
Tea.require.map[src] = true; | |
try { | |
extension = src.match(/.*?\.(js|css)/i)[1]; | |
} catch(e) { throw "Can only import .css or .js files, not whatever this is: " + src; } | |
if (extension == 'js') | |
document.write('<script type="text/javascript" src="' + src + '"></script>\n'); | |
else if (extension == 'css') | |
document.write('<link rel="stylesheet" href="' + src + '" type="text/css"/>\n'); | |
} | |
} | |
Tea.require.map = {} | |
/** Tea.overrideMethod(super_function, function) | |
Creates a callback that when run, provides a {{{__super__}}} on *this* which points to | |
{{{super_function}}}, and then runs {{{func}}}. A great way to do inheritance. | |
**/ | |
Tea.overrideMethod = function(super_func, func) | |
{ | |
return function() | |
{ | |
this.__super__ = function() { return super_func.apply(this, arguments) }; | |
var r = func.apply(this, arguments); | |
delete this.__super__; | |
return r; | |
} | |
} | |
/** Tea.generateUniqueID(prefix) | |
Generates a unique id with the given {{{prefix}}}. | |
**/ | |
Tea.generateUniqueID = function(prefix) { | |
if (!Tea._uniqueID) | |
Tea._uniqueID = 1; | |
return prefix + "-" + Tea._uniqueID++; | |
} | |
/// Types ///////// | |
Tea.types = {} | |
/** Tea.registerType(name, object) | |
Registeres an {{{object}}} as a type with the given {{{name}}}. | |
name: | |
Name of the type. | |
object: | |
The object. | |
**/ | |
Tea.registerType = function(name, object) { Tea.types[name] = object; } | |
/** Tea.getType(name) | |
Returns the object registered with the given {{{name}}}. | |
**/ | |
Tea.getType = function(name) | |
{ | |
return Tea.types[name]; | |
} | |
/** Tea.create(options) | |
Returns an object created using the {{{options}}}, which can be an object | |
or a string. | |
If {{{options}}} is a string, a type returned by {{{Tea.getType()}}} will | |
be returned, unless it can't find the type, then it is treated as a | |
jQuery object. | |
If {{options}} is a jQuery object it is wrapped as the source of a | |
Tea.Element and returned. | |
If {{{options}}} is an {{{Object}}}, the {{{.type}}} attribute will be | |
used to look up the correct type and will be used to create an instance | |
of that type. | |
If {{{options}}} is an instance of a {{{Tea.Object}}}, it will be returned | |
unaffected. | |
**/ | |
Tea.create = function(options, extra) { | |
if (options instanceof Tea.Object) return options; | |
if (typeof(options) == 'string') { | |
var t = Tea.getType(options); | |
if (t) | |
return t(extra); | |
try { | |
var jq = jQuery(options); | |
options = jq; | |
} catch(e) {} | |
} | |
if (options instanceof jQuery) { | |
return Tea.create({type: 't-element', source: options}, extra); | |
} | |
if (options instanceof Object) { | |
if (!options.type) throw "Tea.create() called with an object that has no type"; | |
var type = Tea.getType(options.type); | |
if (!type) throw "Tea.create() called with unknown type: " + options.type; | |
return type(jQuery.extend({}, options, extra)); | |
} | |
throw "Tea.create() called with incorrect argument: " + options; | |
} | |
/// Tea.Object ////////// | |
var _creating = false; | |
Tea.createInstance = function(Type) { | |
_creating = true; | |
var instance = new Type(); | |
//console.log('createInstance', instance, instance.constructor, Type); | |
_creating = false; | |
return instance; | |
} | |
Tea.extend = function(object, properties) { | |
jQuery.each(properties, function(k, v) { | |
if (jQuery.isFunction(v)) { | |
var supr = object[k]; | |
if (jQuery.isFunction(supr)) | |
v = Tea.overrideMethod(supr, v); | |
} | |
object[k] = v; | |
}); | |
} | |
Tea.extendType = function(SuperType, name, properties) { | |
var Type = makeType(name); | |
Type.supertype = SuperType; | |
Type.prototype = Tea.createInstance(SuperType); | |
Tea.extend(Type.prototype, properties); | |
Type._name = name; | |
Type.prototype.constructor = Type; | |
Tea.registerType(name, Type); | |
return Type; | |
} | |
function makeType(name) { | |
var Type = function() { | |
if (_creating) return this; | |
return Type.create.apply(this, arguments); | |
} | |
Type.name = name || "t-object"; | |
if (name) { | |
Type.toString = function() { | |
return "[type: " + name + "]"; | |
} | |
Tea.registerType(name, Type); | |
} | |
Type.extend = function(properties) { | |
properties = properties || {}; | |
return Tea.extendType(Type, properties.type, properties); | |
} | |
Type.create = function(options) { | |
var instance = Tea.createInstance(Type); | |
//console.log("create", instance, instance.constructor, Type, options); | |
instance.__options__ = options || {}; | |
Tea.extend(instance, instance.__options__); | |
instance.init(instance.__options__); | |
return instance; | |
} | |
return Type; | |
} | |
/** Tea.Object | |
Base object that allows class/subclass behavior, events, and a regard | |
for "options". | |
Mamal = Tea.Class({type: 'Mamal'}); | |
Human = Mamal.extend({type: 'Mamal'}); | |
Bob = Human({name: 'Bob'}); | |
**/ | |
Tea.Object = makeType("t-object"); | |
Tea.Object.prototype = { | |
/** Tea.Object.init(options) | |
Override this to change initialization code. | |
Note: Not called on class prototypes. | |
**/ | |
init : jQuery.noop, | |
/** Tea.Object.toString() | |
Returns a string representation of the object. | |
**/ | |
toString : function() | |
{ | |
return (this.constructor._name || "t-object"); | |
}, | |
/** Tea.Object.bind(event, handler, [args]) | |
Binds an event for this instance to the given function which will be | |
called with the given args. | |
event: | |
An event name to bind. | |
handler: | |
The function to call when the event is triggered. | |
args (optional): | |
A list of arguments to pass into when calling the handler. | |
**/ | |
bind : function(event, handler, args) | |
{ | |
if (!this.__events) this.__events = {}; | |
if (!this.__events[event]) this.__events[event] = []; | |
this.__events[event].push([handler, args]); | |
}, | |
/** Tea.Object.prototype.unbind(event, [handler]) | |
Unbinds an events from this instance. If a handler is given, only | |
events pointing to that handler are unbound. Otherwise all handlers | |
for that event are unbound. | |
event: | |
An event name to unbind. | |
handler: | |
Only events pointing the given handler are unbound. | |
**/ | |
unbind : function(event, handler) { | |
if (!this.__events) return; | |
var handlers = this.__events[event]; | |
if (!handlers) return; | |
if (handler) { | |
jQuery.each(handlers, function(i, pair) { | |
if (pair && pair[0] == handler) { | |
handlers.splice(i, 1); | |
} | |
}); | |
} else { | |
delete this.__events[event]; | |
} | |
}, | |
/** Tea.Object.hook(target, event, handler, [args]) | |
Binds onto the target, but does so in a manner that allows this object | |
to track its "hooks". One can then unhook(target), or unhookAll() to | |
release the bind. This is beneficial from a memory standpoint, as | |
hooks won't leak like a bind will. | |
target: | |
The target to bind onto. | |
event: | |
An event name to bind. | |
handler: | |
The function to call when the event is triggered. | |
args (optional): | |
A list of arguments to pass into when calling the handler. | |
**/ | |
hook : function(target, event, func, args) { | |
if (!this.__hooks) this.__hooks = []; | |
var handler = jQuery.proxy(func, this); | |
target.bind(event, handler, args); | |
this.__hooks.push([target, event, handler]); | |
}, | |
/** Tea.Object.unhook(target) | |
Unhooks all binds on target. | |
target: | |
The target to release all binds from. | |
**/ | |
unhook : function(target) | |
{ | |
if (!this.__hooks) return; | |
for(var i=0; i<this.__hooks.length; i++) { | |
var hook = this.__hooks[i]; | |
if (target != hook[0]) continue; | |
var event = hook[1]; | |
var handler = hook[2]; | |
target.unbind(event, handler); | |
} | |
}, | |
/** Tea.Object.unhookAll() | |
Unhooks all binds on all targets. | |
**/ | |
unhookAll : function() | |
{ | |
if (!this.__hooks) return; | |
while(this.__hooks.length > 0) { | |
var hook = this.__hooks.pop(); | |
var target = hook[0]; | |
var event = hook[1]; | |
var handler = hook[2]; | |
target.unbind(event, handler); | |
} | |
}, | |
/** Tea.Object.prototype.trigger(name) | |
event: | |
The event name to trigger. | |
args: | |
Arguments to pass onto the function. These go after | |
any arguments set in the bind(). | |
**/ | |
trigger : function(event, args) { | |
if (!this.__events) return; | |
var handlers = this.__events[event]; | |
if (!handlers) return; | |
if (!args) args = []; | |
for(var i = 0; i < handlers.length; i++) | |
{ | |
handlers[i][0].apply(this, (handlers[i][1] || []).concat(args)); | |
} | |
}, | |
/** Tea.Object.prototype.destroy() | |
Destroys the object and unhooks all events. | |
**/ | |
destroy : function() { | |
this.trigger('destroy'); | |
this.unhookAll(); | |
} | |
} | |
Tea.Class = Tea.Object.extend; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment