[Rt-commit] rt branch, lcore, updated. f8103049e59b60e07d78480a60162b75ee6104d9
clkao at bestpractical.com
clkao at bestpractical.com
Thu Oct 15 11:05:06 EDT 2009
The branch, lcore has been updated
via f8103049e59b60e07d78480a60162b75ee6104d9 (commit)
via f60219fcdc9cacf769e12efeb0364e9ac52a4bd4 (commit)
via e19a124462f378b001dcbb4a4246caebee090df0 (commit)
via af14beecc266c9c36d9b6a52ec41c07973fbb4f4 (commit)
from e5f48a2101fcedb756f7a3abeead7ba228f54b0c (commit)
Summary of changes:
lib/RT.pm | 2 +-
lib/RT/Lorzy.pm | 4 +-
share/web/static/js/joose.js | 3801 ++++++++++++++++++++++++++++++++++++
share/web/static/js/rulebuilder.js | 661 ++++---
4 files changed, 4149 insertions(+), 319 deletions(-)
create mode 100644 share/web/static/js/joose.js
- Log -----------------------------------------------------------------
commit af14beecc266c9c36d9b6a52ec41c07973fbb4f4
Author: Chia-liang Kao <clkao at bestpractical.com>
Date: Thu Oct 15 19:55:51 2009 +0800
correct type for context
diff --git a/lib/RT/Lorzy.pm b/lib/RT/Lorzy.pm
index 88ab33b..253a582 100644
--- a/lib/RT/Lorzy.pm
+++ b/lib/RT/Lorzy.pm
@@ -43,7 +43,7 @@ $LCORE->env->set_symbol('RT.RuleAction.Prepare' => LCore::Primitive->new
lazy => 0,
parameters => [ LCore::Parameter->new({ name => 'name', type => 'Str' }),
LCore::Parameter->new({ name => 'template', type => 'Str' }),
- LCore::Parameter->new({ name => 'context', type => 'Str' }),
+ LCore::Parameter->new({ name => 'context', type => 'HashRef' }),
LCore::Parameter->new({ name => 'ticket', type => 'RT::Model::Ticket' }),
LCore::Parameter->new({ name => 'transaction', type => 'RT::Model::Transaction' }) ],
@@ -65,7 +65,7 @@ $LCORE->env->set_symbol('RT.RuleAction.Run' => LCore::Primitive->new
lazy => 0,
parameters => [ LCore::Parameter->new({ name => 'name', type => 'Str' }),
LCore::Parameter->new({ name => 'template', type => 'Str' }),
- LCore::Parameter->new({ name => 'context', type => 'Str' }),
+ LCore::Parameter->new({ name => 'context', type => 'HashRef' }),
LCore::Parameter->new({ name => 'ticket', type => 'RT::Model::Ticket' }),
LCore::Parameter->new({ name => 'transaction', type => 'RT::Model::Transaction' }) ],
commit e19a124462f378b001dcbb4a4246caebee090df0
Author: Chia-liang Kao <clkao at bestpractical.com>
Date: Thu Oct 15 20:38:15 2009 +0800
correct new top-context location on transform
diff --git a/share/web/static/js/rulebuilder.js b/share/web/static/js/rulebuilder.js
index 30f963e..133bba6 100644
--- a/share/web/static/js/rulebuilder.js
+++ b/share/web/static/js/rulebuilder.js
@@ -429,7 +429,7 @@ RuleBuilder.Context.prototype.transform = function(func_name) {
}
else {
jQuery(rb.top_context.element).removeClass('top-context').remove();
- new_element.addClass('top-context').prependTo(rb.ebuilder);
+ new_element.addClass('top-context').appendTo(rb.panel);
rb.top_context = parent;
}
commit f60219fcdc9cacf769e12efeb0364e9ac52a4bd4
Author: Chia-liang Kao <clkao at bestpractical.com>
Date: Thu Oct 15 21:44:57 2009 +0800
rather hacky code to get loading into arraybuilder work for now.
diff --git a/share/web/static/js/rulebuilder.js b/share/web/static/js/rulebuilder.js
index 133bba6..6d4391e 100644
--- a/share/web/static/js/rulebuilder.js
+++ b/share/web/static/js/rulebuilder.js
@@ -57,15 +57,27 @@ RuleBuilder.prototype.load_expressions = function (node, ctx) {
var operands = node.operands;
if (operands instanceof Array) {
for (var i in ctx.children) {
- this.load_expressions(node.operands[i], ctx.children[i]);
+ var childctx = ctx.children[i];
+ // XXX: make arraybuilder proper ctx subclass and provide methods for the following manipulation
+ if (childctx.inner_type) {
+ jQuery('span.arraybuilder-icon', childctx.element)
+ .trigger('click');
+ this.load_expressions(node.operands[i], childctx.children[0]);
+ for (var j = parseInt(i)+1; j < node.operands.length; ++j) {
+ var newchild = childctx.mk_array_item_context(childctx.inner_type,
+ jQuery('div.array-item-container', childctx.arraybuilder), childctx.children[childctx.children.length-1].element);
+ childctx.children.push(newchild);
+ this.load_expressions(node.operands[j], newchild);
+ }
+ }
+ else
+ this.load_expressions(node.operands[i], childctx);
}
}
else {
var names = jQuery.map(this.functions[func_name].parameters,
function(param) { return param.name });
- console.log(names);
for (var i in ctx.children) {
- console.log("setting "+i+" "+names[i]);
this.load_expressions(node.operands[names[i]], ctx.children[i]);
}
}
commit f8103049e59b60e07d78480a60162b75ee6104d9
Author: Chia-liang Kao <clkao at bestpractical.com>
Date: Thu Oct 15 23:02:52 2009 +0800
first cut of joosification for rulebuilder.context.
diff --git a/lib/RT.pm b/lib/RT.pm
index f46e325..797ae91 100644
--- a/lib/RT.pm
+++ b/lib/RT.pm
@@ -413,7 +413,7 @@ sub start {
RT::init();
Jifty->web->add_javascript(
- qw( titlebox-state.js util.js ahah.js list.js class.js
+ qw( joose.js titlebox-state.js util.js ahah.js list.js class.js
jquery.createdomnodes.js jquery.menu.js combobox.js cascaded.js rulebuilder.js
)
);
diff --git a/share/web/static/js/joose.js b/share/web/static/js/joose.js
new file mode 100644
index 0000000..27ebfbf
--- /dev/null
+++ b/share/web/static/js/joose.js
@@ -0,0 +1,3801 @@
+/*!
+ * This is Joose 2.1
+ * For documentation see http://code.google.com/p/joose-js/
+ * Copyright (c) 2009 Malte Ubl
+ * Generated: Sun Aug 2 16:02:39 2009
+ */
+// ##########################
+// File: Joose.js
+// ##########################
+var joosetop = this;
+
+Joose = function () {
+ this.cc = null; // the current class
+ this.currentModule = null
+ this.top = joosetop;
+ this.globalObjects = [];
+
+ this.anonymouseClassCounter = 0;
+};
+
+// Static helpers for Arrays
+Joose.A = {};
+Joose.A.each = function (array, func) {
+ for(var i = 0; i < array.length; i++) {
+ func(array[i], i)
+ }
+}
+Joose.A.exists = function (array, value) {
+ for(var i = 0; i < array.length; i++) {
+ if(array[i] == value) {
+ return true
+ }
+ }
+ return false
+}
+Joose.A.concat = function (source, array) {
+ source.push.apply(source, array)
+ return source
+}
+
+Joose.A.grep = function (array, func) {
+ var a = [];
+ Joose.A.each(array, function (t) {
+ if(func(t)) {
+ a.push(t)
+ }
+ })
+ return a
+}
+Joose.A.remove = function (array, removeEle) {
+ var a = [];
+ Joose.A.each(array, function (t) {
+ if(t !== removeEle) {
+ a.push(t)
+ }
+ })
+ return a
+}
+
+// Static helpers for Strings
+Joose.S = {};
+Joose.S.uppercaseFirst = function (string) {
+ var first = string.substr(0,1);
+ var rest = string.substr(1,string.length-1);
+ first = first.toUpperCase()
+ return first + rest;
+}
+
+Joose.S.isString = function (thing) {
+ if(typeof thing == "string") {
+ return true
+ }
+ return false
+}
+
+// Static helpers for objects
+Joose.O = {};
+Joose.O.each = function (object, func) {
+ for(var i in object) {
+ func(object[i], i)
+ }
+}
+
+Joose.O.eachSafe = function (object, func) {
+ for(var i in object) {
+ if(object.hasOwnProperty(i)) {
+ func(object[i], i)
+ }
+ }
+}
+
+// Experimental!
+Joose.O.extend = function (target, newObject) {
+ for(var i in newObject) {
+ var thing = newObject[i]
+ target[i] = thing
+ }
+}
+
+
+Joose.prototype = {
+
+ addToString: function (object, func) {
+ object.toString = func;
+ },
+
+ /*
+ * Differentiates between instances and classes
+ */
+ isInstance: function(obj) {
+ if(!obj.meta) {
+ throw "isInstance only works with Joose objects and classes."
+ }
+ if(obj.constructor === obj.meta.c) {
+ return true
+ }
+ return false
+ },
+
+ init: function () {
+ this.builder = new Joose.Builder();
+ this.builder.globalize()
+ },
+ // this needs to be updated in release.pl too, if files are added
+ components: function () {
+ return [
+ "Joose.Builder",
+ "Joose.Class",
+ "Joose.Method",
+ "Joose.ClassMethod",
+ "Joose.Attribute",
+ "Joose.Role",
+ "Joose.Singleton",
+ "Joose.SimpleRequest",
+ "Joose.Gears",
+ "Joose.Storage",
+ "Joose.Storage.Unpacker",
+ "Joose.Decorator",
+ "Joose.Module",
+ "Joose.TypeChecker",
+ "Joose.TypeConstraint",
+ "Joose.TypeCoercion",
+ "Joose.Types",
+ "Joose.Prototype",
+ "Joose.TypedMethod",
+ "Joose.MultiMethod"
+ ]
+ },
+
+ loadComponents: function (basePath) {
+ var html = "";
+ Joose.A.each(this.components(), function (name) {
+ var url = ""+basePath + "/" + name.split(".").join("/") + ".js";
+
+ html += '<script type="text/javascript" src="'+url+'"></script>'
+ })
+ document.write(html)
+ }
+}
+
+Joose.copyObject = function (source, target) {
+ var keys = "";
+ Joose.O.each(source, function (value, name) { keys+=", "+name; target[name] = value })
+ return target
+};
+
+
+
+Joose.emptyFunction = function () {};
+
+this.joose = new Joose();
+
+// Rhino is the only popular JS engine that does not traverse objects in insertion order
+// Check for Rhino (which uses the global Packages function) and set CHAOTIC_TRAVERSION_ORDER to true
+(function () {
+
+ if(
+ typeof this["load"] == "function" &&
+ (
+ typeof this["Packages"] == "function" ||
+ typeof this["Packages"] == "object"
+ )
+ ) {
+ joose.CHAOTIC_TRAVERSION_ORDER = true
+ }
+})()
+
+
+Joose.bootstrap = function () {
+ // Bootstrap
+ var BOOT = new Joose.MetaClassBootstrap();
+
+ BOOT.builder = Joose.MetaClassBootstrap;
+
+ Joose.MetaClass = BOOT.createClass("Joose.MetaClass");
+
+ Joose.MetaClass.meta.addNonJooseSuperClass("Joose.MetaClassBootstrap", BOOT)
+
+ Joose.MetaClass.meta.addMethod("initialize", function () { this._name = "Joose.MetaClass" })
+
+ var META = new Joose.MetaClass();
+
+ META.builder = Joose.MetaClass;
+
+ Joose.Class = META.createClass("Joose.Class")
+ Joose.Class.meta.addSuperClass(Joose.MetaClass);
+ Joose.MetaClass.meta.addMethod("initialize", function () { this._name = "Joose.Class" })
+
+ Joose.Class.create = function (name, optionalConstructor, optionalModule) {
+ var aClass = new this();
+
+ // aClass.builder allows creating more instances of the same meta class
+ // Workaround for broken object.constructor implementation.
+ aClass.builder = this;
+ var c = aClass.createClass(name, optionalConstructor, optionalModule)
+ c.meta.builder = this
+
+ return c;
+ }
+}
+
+Joose.bootstrapCompletedBuilder = function () {
+ // Turn Joose.Method into a Joose.Class object
+ Joose.Builder.Globals.joosify("Joose.Method", Joose.Method)
+ Joose.Builder.Globals.joosify("Joose.Attribute", Joose.Attribute)
+
+}
+
+Joose.bootstrapCompletedClassMethod = function () {
+ Joose.Class.meta.addClassMethod("create", Joose.Class.create)
+}
+
+Joose.bootstrap3 = function () {
+ // make the .meta object circular
+}
+
+/**
+ * @name Joose.Class
+ * @constructor
+ */
+/*
+ * Joose.MetaClassBootstrap is used to bootstrap the Joose.Class with a regular JS constructor
+ */
+/** ignore */ // Do not display the Bootstrap classes in the docs
+Joose.MetaClassBootstrap = function () {
+ this._name = "Joose.MetaClassBootstrap";
+ this.methodNames = [];
+ this.attributeNames = ["_name", "isAbstract", "isDetached", "methodNames", "attributeNames", "methods", "parentClasses", "roles", "c"];
+ this.attributes = {};
+ this.methods = {};
+ this.classMethods = {};
+ this.parentClasses = [];
+ this.roles = []; // All roles
+ this.myRoles = []; // Only roles applied to me directly
+ this.isAbstract = false;
+ this.isDetached = false;
+}
+/** @ignore */
+Joose.MetaClassBootstrap.prototype = {
+
+ toString: function () {
+ if(this.meta) {
+ return "a "+this.meta.className();
+ }
+ return "NoMeta"
+ },
+
+ /**
+ * Returns the name of the class
+ * @name className
+ * @function
+ * @memberof Joose.Class
+ */
+ /** @ignore */
+ className: function () {
+ return this._name
+ },
+
+ /**
+ * Returns the name of the class (alias to className())
+ * @name getName
+ * @function
+ * @memberof Joose.Class
+ */
+ /** @ignore */
+ getName: function () {
+ return this.className()
+ },
+
+ /**
+ * Creates a new empty meta class object
+ * @function
+ * @name newMetaClass
+ * @memberof Joose.Class
+ */
+ /** @ignore */
+ newMetaClass: function () {
+
+ var me = this;
+
+ var metaClassClass = this.builder;
+
+ var c = new metaClassClass();
+ c.builder = metaClassClass;
+ c._name = this._name
+
+ c.methodNames = [];
+ c.attributeNames = [];
+ c.methods = {};
+ c.classMethods = {};
+ c.parentClasses = [];
+ c.roles = [];
+ c.myRoles = [];
+ c.attributes = {};
+
+ var myMeta = this.meta;
+ if(!myMeta) {
+ myMeta = this;
+ }
+
+ c.meta = myMeta
+
+ return c
+ },
+
+ /**
+ * Creates a new class object
+ * @function
+ * @name createClass
+ * @param {function} optionalConstructor If provided will be used as the class constructor (You should not need this)
+ * @param {Joose.Module} optionalModuleObject If provided the Module's name will be prepended to the class name
+ * @memberof Joose.Class
+ */
+ /** @ignore */
+ createClass: function (name, optionalConstructor, optionalModuleObject) {
+ var meta = this.newMetaClass();
+
+ var c;
+
+ if(optionalConstructor) {
+ c = optionalConstructor
+ } else {
+ c = this.defaultClassFunctionBody()
+
+ if(optionalModuleObject) {
+ optionalModuleObject.addElement(c)
+ // meta.setModule(optionalModuleObject)
+ }
+ }
+
+ c.prototype.meta = meta
+ c.meta = meta;
+ if(name == null) {
+ meta._name = "__anonymous__"
+ } else {
+ var className = name;
+ if(optionalModuleObject) {
+ className = optionalModuleObject.getName() + "." + name
+ }
+ meta._name = className;
+ }
+ meta.c = c;
+
+ // store them in the global object if they have no namespace
+ // They will end up in the Module __JOOSE_GLOBAL__
+ if(!optionalModuleObject) {
+ // Because the class Joose.Module might not exist yet, we use this temp store
+ // that will later be in the global module
+ joose.globalObjects.push(c)
+ }
+
+ meta.addInitializer();
+ meta.addToString();
+ meta.addDetacher();
+
+ return c;
+ },
+
+ buildComplete: function () {
+ // may be overriden in sublass
+ },
+
+ // intializes a class from the class definitions
+ initializeFromProps: function (props) {
+ this._initializeFromProps(props)
+ },
+
+ _initializeFromProps: function (props) {
+ var me = this;
+ if(props) {
+
+ if(joose.CHAOTIC_TRAVERSION_ORDER) {
+ Joose.A.each(["isa", "does", "has", "method", "methods"], function (name) {
+ if(name in props) {
+ var value = props[name];
+ me._initializeFromProp(name, value, props)
+ delete props[name]
+ }
+ })
+ }
+
+ // For each property of the class constructor call the builder
+ Joose.O.eachSafe(props, function (value, name) {
+ me._initializeFromProp(name, value, props)
+ })
+
+ for(var i = 0; i < this.roles.length; i++) {
+ var role = this.roles[i];
+ role.meta.applyMethodModifiers(this.c)
+ }
+
+ me.buildComplete();
+ me.validateClass();
+ }
+ },
+
+ _initializeFromProp: function (propName, value, props) {
+ var paras = value;
+ var customBuilderName = "handleProp"+propName;
+ // if the meta class of the current class implements handleProp+nameOfBuilder we use that
+ if(this.meta.can(customBuilderName)) {
+ this[customBuilderName](paras, props)
+ } else { // Otherwise use a builder from this file
+ throw new Error("Called invalid builder "+propName+" while creating class "+this.className())
+ }
+ },
+
+ /**
+ * Returns a new instance of the class that this meta class instance is representing
+ * @function
+ * @name instantiate
+ * @memberof Joose.Class
+ */
+ instantiate: function () {
+ //var o = new this.c.apply(this, arguments);
+
+ // Ough! Calling a constructor with arbitrary arguments hack
+ var f = function () {};
+ f.prototype = this.c.prototype;
+ f.prototype.constructor = this.c;
+ var obj = new f();
+ this.c.apply(obj, arguments);
+ return obj;
+ },
+
+ /**
+ * Returns the default constructor function for new classes. You might want to override this in derived meta classes
+ * Default calls initialize on a new object upon construction.
+ * The class object will stringify to it's name
+ * @function
+ * @name defaultClassFunctionBody
+ * @memberof Joose.Class
+ */
+ /** @ignore */
+ defaultClassFunctionBody: function () {
+ var f = function () {
+ this.initialize.apply(this, arguments);
+ };
+ joose.addToString(f, function () {
+ return this.meta.className()
+ })
+ return f;
+ },
+
+ /**
+ * Adds a toString method to a class
+ * The default toString method will call the method stringify if available.
+ * This make overriding stringification easier because toString cannot
+ * be reliably overriden in some JS implementations.
+ * @function
+ * @name addToString
+ * @memberof Joose.Class
+ */
+ /** @ignore */
+ addToString: function () {
+ this.addMethod("toString", function () {
+ if(this.stringify) {
+ return this.stringify()
+ }
+ return "a "+ this.meta.className()
+ })
+ },
+
+ /**
+ * Adds the method returned by the initializer method to the class
+ * @function
+ * @name addInitializer
+ * @memberof Joose.Class
+ */
+ /** @ignore */
+ addInitializer: function () {
+ if(!this.c.prototype.initialize) {
+ this.addMethod("initialize", this.initializer())
+ }
+ },
+
+ /**
+ * Adds a toString method to a class
+ * @function
+ * @name initializer
+ * @memberof Joose.Class
+ */
+ /** @ignore */
+ initializer: function () {
+ return function initialize (paras) {
+ var me = this;
+ if(this.meta.isAbstract) {
+ var name = this.meta.className();
+ throw ""+name+" is an abstract class and may not instantiated."
+ }
+ var attributes = this.meta.getAttributes();
+ for(var i in attributes) {
+ if(attributes.hasOwnProperty(i)) {
+ var attr = attributes[i];
+ attr.doInitialization(me, paras);
+ }
+ }
+ }
+ },
+
+ dieIfString: function (thing) {
+ if(Joose.S.isString(thing)) {
+ throw new TypeError("Parameter must not be a string.")
+ }
+ },
+
+ addRole: function (roleClass) {
+ this.dieIfString(roleClass);
+ var c = this.getClassObject();
+ if(roleClass.meta.apply(c)) {
+ this.roles.push(roleClass);
+ this.myRoles.push(roleClass);
+ }
+
+ },
+
+ getClassObject: function () {
+ return this.c
+ },
+
+ classNameToClassObject: function (className) {
+ var top = joose.top;
+ var parts = className.split(".");
+ var object = top;
+ for(var i = 0; i < parts.length; i++) {
+ var part = parts[i];
+ object = object[part];
+ if(!object) {
+ throw "Unable to find class "+className
+ }
+ }
+ return object
+ },
+
+ addNonJooseSuperClass: function (name, object) {
+
+ var pseudoMeta = new Joose.MetaClassBootstrap();
+ pseudoMeta.builder = Joose.MetaClassBootstrap;
+ var pseudoClass = pseudoMeta.createClass(name)
+
+ Joose.O.each(object, function(value, name) {
+ if(typeof(value) == "function") {
+ pseudoClass.meta.addMethod(name, value)
+ } else {
+ pseudoClass.meta.addAttribute(name, {init: value})
+ }
+ })
+
+ this.addSuperClass(pseudoClass);
+ },
+
+ addSuperClass: function (classObject) {
+ this.dieIfString(classObject);
+ var me = this;
+
+ //this._fixMetaclassIncompatability(classObject)
+
+ // Methods
+ var names = classObject.meta.getMethodNames();
+ for(var i = 0; i < names.length; i++) {
+ var name = names[i]
+
+ var m = classObject.meta.getMethodObject(name)
+ if(m) {
+ var method = m.copy();
+ method.setIsFromSuperClass(true);
+ me.addMethodObject(method)
+ }
+ // there can be class methods and instance methods of the same name
+ m = classObject.meta.getClassMethodObject(name)
+ if(m) {
+ var method = m.copy();
+ method.setIsFromSuperClass(true);
+ me.addMethodObject(method)
+ }
+ }
+
+ // Attributes
+ Joose.O.eachSafe(classObject.meta.attributes, function (attr, name) {
+ me.addAttribute(name, attr.getProps())
+ })
+
+ // Roles
+ var roles = classObject.meta.roles
+ for(var i = 0; i < roles.length; i++) {
+ var role = roles[i]
+ me.roles.push(role)
+ }
+
+ this.parentClasses.unshift(classObject)
+ },
+
+ _fixMetaclassIncompatability: function (superClass) {
+
+ var superMeta = superClass.meta;
+ var superMetaName = superMeta.meta.className();
+
+ if(
+ superMetaName == "Joose.Class" ||
+ superMetaName == "Joose.MetaClass" ||
+ superMetaName == "Joose.MetaClassBootstrap") {
+ return
+ }
+
+ // we are compatible
+ if(this.meta.meta.isa(superMeta)) {
+ return
+ }
+
+ // fix this into becoming a superMeta
+ var patched = superMeta.meta.instantiate(this);
+
+ for(var i in patched) {
+ this[i] = patched[i]
+ }
+ },
+
+ isa: function (classObject) {
+ this.dieIfString(classObject);
+ var name = classObject.meta.className()
+ // Same type
+ if(this.className() == name) {
+ return true
+ }
+ // Look up into parent classes
+ for(var i = 0; i < this.parentClasses.length; i++) {
+ var parent = this.parentClasses[i].meta
+ if(parent.className() == name) {
+ return true
+ }
+ if(parent.isa(classObject)) {
+ return true
+ }
+ }
+ return false
+ },
+
+ wrapMethod: function (name, wrappingStyle, func, notPresentCB) {
+
+ var orig = this.getMethodObject(name);
+ if(orig) {
+ this.addMethodObject( orig[wrappingStyle](func) )
+ } else {
+ if(notPresentCB) {
+ notPresentCB()
+ } else {
+ throw new Error("Unable to apply "+wrappingStyle+" method modifier because method "+name+" does not exist");
+ }
+ }
+ },
+
+ dispatch: function (name) {
+ return this.getMethodObject(name).asFunction()
+ },
+
+ hasMethod: function (name) {
+ return this.methods[name] != null || this.classMethods[name] != null
+ },
+
+ addMethod: function (name, func, props) {
+ var m = new Joose.Method(name, func, props);
+
+ this.addMethodObject(m)
+ },
+
+ addClassMethod: function (name, func, props) {
+ var m = new Joose.ClassMethod(name, func, props);
+
+ this.addMethodObject(m)
+ },
+
+ addMethodObject: function (method) {
+ var m = method;
+ // optimized because very heavily used
+ var name = m.getName === Joose.Method.prototype.getNname ? m._name : m.getName();
+
+ var body = m._body;
+ if(!body.displayName) { // never overwrite this. We want to know where the method is defined
+ var className = this.className === Joose.MetaClassBootstrap.prototype.className ? this._name : this.className()
+ body.displayName = className + "." + name+"()";
+ }
+
+ if(!this.methods[name] && !this.classMethods[name]) {
+ this.methodNames.push(name);
+ }
+ if(m._isClassMethod) {
+ this.classMethods[name] = m;
+ } else {
+ this.methods[name] = m;
+ }
+
+ method.addToClass(this.c)
+ },
+
+ attributeMetaclass: function () {
+ return Joose.Attribute
+ },
+
+ addAttribute: function (name, props) {
+
+ var metaclass = this.attributeMetaclass();
+
+ if(props && props.metaclass) {
+ metaclass = props.metaclass
+ }
+
+ var at = new metaclass(name, props);
+
+ at.apply(this.c)
+ },
+
+ getAttributes: function () {
+ return this.attributes
+ },
+
+ getAttribute: function (name) {
+ return this.attributes[name]
+ },
+
+ setAttribute: function (name, attributeObject) {
+ return this.attributes[name] = attributeObject
+ },
+
+ getMethodObject: function (name) {
+ return this.methods[name]
+ },
+
+ getClassMethodObject: function (name) {
+ return this.classMethods[name]
+ },
+
+ getAttributeNames: function () {
+ return this.attributeNames;
+ },
+
+ getInstanceMethods: function () {
+ var a = [];
+ Joose.O.eachSafe(this.methods, function (m) {
+ a.push(m)
+ })
+ return a
+ },
+
+ getClassMethods: function () {
+ var a = [];
+ Joose.O.eachSafe(this.classMethods, function (m) {
+ a.push(m)
+ })
+ return a
+ },
+
+ getSuperClasses: function () {
+ return this.parentClasses;
+ },
+
+ getSuperClass: function () {
+ return this.parentClasses[0];
+ },
+
+ getRoles: function () {
+ return this.roles;
+ },
+
+ getMethodNames: function () {
+ return this.methodNames;
+ },
+
+ makeAnonSubclass: function () {
+ var c = this.createClass(this.className()+"__anon__"+joose.anonymouseClassCounter++);
+ c.meta.addSuperClass(this.getClassObject());
+
+ return c;
+ },
+
+ addDetacher: function () {
+ this.addMethod("detach", function detach () {
+ var meta = this.meta;
+
+ if(meta.isDetached) {
+ return // no reason to do it again
+ }
+
+ var c = meta.makeAnonSubclass()
+
+ c.meta.isDetached = true;
+
+ // appy the role to the anonymous class
+ // swap meta class of object with new instance
+ this.meta = c.meta;
+ // swap __proto__ chain of object to its new class
+ // unfortunately this is not available in IE :(
+ // object.__proto__ = c.prototype
+
+ this.constructor = c;
+
+ var proto;
+
+ // Workaround for IE and opera to enable prototype extention via the meta class (by making them identical :)
+ // This however makes Role.unapply impossible
+ if(!this.__proto__) {
+ proto = this
+ } else {
+ proto = {};
+ Joose.copyObject(this, proto)
+ }
+
+
+ c.prototype = proto;
+ this.__proto__ = c.prototype
+ return
+ })
+ },
+
+ /**
+ * Throws an exception if the class does not implement all methods required by it's roles
+ * @function
+ * @name validateClass
+ * @memberof Joose.Class
+ */
+ validateClass: function () {
+ var c = this.getClassObject();
+ var me = this;
+
+ // Test whether all rows are fully implemented.
+ var throwException = true;
+ Joose.A.each(this.roles, function(role) {
+ role.meta.isImplementedBy(c, throwException)
+ })
+ },
+
+ /**
+ * Returns true if the class implements the method
+ * @function
+ * @name can
+ * @param {string} methodName The method
+ * @memberof Joose.Class
+ */
+ can: function (methodName) {
+ var method = this.methods[methodName];
+ if(!method) {
+ return false
+ }
+ return true
+ },
+
+ classCan: function (methodName) {
+ var method = this.classMethods[methodName];
+ if(!method) {
+ return false
+ }
+ return true
+ },
+
+
+ /**
+ * Returns true if the class implements a Role
+ * @function
+ * @name does
+ * @param {Joose.Class} methodName The class object
+ * @memberof Joose.Class
+ */
+ does: function (roleObject) {
+
+ for(var i = 0; i < this.roles.length; i++) {
+ if(roleObject === this.roles[i]) {
+ return true
+ }
+ }
+
+ // dive into roles to find roles implemented by my roles
+ for(var i = 0; i < this.roles.length; i++) {
+ if(this.roles[i].meta.does(roleObject)) {
+ return true
+ }
+ }
+
+ return false
+ // return classObject.meta.implementsMyMethods(this.getClassObject())
+ },
+
+ /**
+ * Returns true if the given class implements all methods of the class
+ * @function
+ * @name does
+ * @param {Joose.Class} methodName The class object
+ * @memberof Joose.Class
+ */
+ implementsMyMethods: function (classObject) {
+ var complete = true
+ // FIXME buggy if class methods are involved. Should roles have class methods?
+ Joose.A.each(this.getMethodNames(), function (value) {
+ var found = classObject.meta.can(value)
+ if(!found) {
+ complete = false
+ }
+ })
+ return complete
+ },
+
+ // Class builders:
+
+ /**
+ * Tells a role that the method name must be implemented by all classes that implement the role
+ * @function
+ * @param methodName {string} Name of the required method name
+ * @name requires
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handleProprequires: function (methodName) {
+ var me = this;
+ if(!this.meta.isa(Joose.Role)) {
+ throw("Keyword 'requires' only available classes with a meta class of type Joose.Role")
+ }
+ if(methodName instanceof Array) {
+ Joose.A.each(methodName, function (name) {
+ me.addRequirement(name)
+ })
+ } else {
+ me.addRequirement(methodName)
+ }
+ },
+
+ handlePropisAbstract: function (bool) {
+ this.isAbstract = bool
+ },
+
+
+ /**
+ * Class builder method
+ * Defines the super class of the class
+ * @function
+ * @param classObject {Joose.Class} The super class
+ * @name isa
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handlePropisa: function (classObject) {
+ if(classObject == null) {
+ throw new Error("Super class is null")
+ }
+ this.addSuperClass(classObject)
+ },
+ /**
+ * Class builder method
+ * Defines a role for the class
+ * @function
+ * @param classObject {Joose.Role} The role
+ * @name does
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handlePropdoes: function (role) {
+ var me = this;
+ if(role instanceof Array) {
+ Joose.A.each(role, function (aRole) {
+ me.addRole(aRole)
+ })
+ } else {
+ me.addRole(role)
+ }
+
+ },
+
+ /**
+ * Class builder method
+ * Defines attributes for the class
+ * @function
+ * @param classObject {object} Maps attribute names to properties (See Joose.Attribute)
+ * @name has
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handleProphas: function (map) {
+ var me = this;
+ if(typeof map == "string") {
+ var name = arguments[0];
+ var props = arguments[1];
+ me.addAttribute(name, props)
+ } else { // name is a map
+ Joose.O.eachSafe(map, function (props, name) {
+ me.addAttribute(name, props)
+ })
+ }
+ },
+
+ /**
+ * @ignore
+ */
+ handlePropmethod: function (name, func, props) {
+ this.addMethod(name, func, props)
+ },
+
+ /**
+ * Class builder method
+ * Defines methods for the class
+ * @function
+ * @param classObject {object} Maps method names to function bodies
+ * @name methods
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handlePropmethods: function (map) {
+ var me = this
+ Joose.O.eachSafe(map, function (func, name) {
+ // if func is already a method object, we use that
+ if(typeof func !== "function") {
+ var props = func; // the function must now be a property hash
+ var method;
+ if (props instanceof Array) {
+ var patterns = props; // the props are actually an array
+ // for MultiMethod dispatch.
+ method = new Joose.MultiMethod
+ .newFromPatterns(name, patterns);
+ } else {
+ method = Joose.TypedMethod.newFromProps(name, props)
+ }
+ me.addMethodObject(method)
+ }
+ // otherwise we create a method object from the function
+ else {
+ me.addMethod(name, func)
+ }
+ })
+ },
+
+ /**
+ * Class builder method
+ * Defines class methods for the class
+ * @function
+ * @param classObject {object} Maps class method names to function bodies
+ * @name classMethods
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handlePropclassMethods: function (map) {
+ var me = this;
+ Joose.O.eachSafe(map, function (func, name2) {
+ me.addMethodObject(new Joose.ClassMethod(name2, func))
+ })
+ },
+
+ /**
+ * Class builder method
+ * Defines workers for the class (The class must have the meta class Joose.Gears)
+ * @function
+ * @param classObject {object} Maps method names to function bodies
+ * @name workers
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handlePropworkers: function (map) {
+ var me = this;
+ Joose.O.eachSafe(map, function (func, name) {
+ me.addWorker(name, func)
+ })
+ },
+
+ /**
+ * Class builder method
+ * Defines before method modifieres for the class.
+ * The defined method modifiers will be called before the method of the super class.
+ * The return value of the method modifier will be ignored
+ * @function
+ * @param classObject {object} Maps method names to function bodies
+ * @name before
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handlePropbefore: function(map) {
+ var me = this
+ Joose.O.eachSafe(map, function (func, name) {
+ me.wrapMethod(name, "before", func);
+ })
+ },
+
+ /**
+ * Class builder method
+ * Defines after method modifieres for the class.
+ * The defined method modifiers will be called after the method of the super class.
+ * The return value of the method modifier will be ignored
+ * @function
+ * @param classObject {object} Maps method names to function bodies
+ * @name after
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handlePropafter: function(map) {
+ var me = this
+ Joose.O.eachSafe(map, function (func, name) {
+ me.wrapMethod(name, "after", func);
+ })
+ },
+
+ /**
+ * Class builder method
+ * Defines around method modifieres for the class.
+ * The defined method modifiers will be called instead of the method of the super class.
+ * The orginial function is passed as an initial parameter to the new function
+ * @function
+ * @param classObject {object} Maps method names to function bodies
+ * @name around
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handleProparound: function(map) {
+ var me = this
+ Joose.O.eachSafe(map, function (func, name) {
+ me.wrapMethod(name, "around", func);
+ })
+ },
+
+ /**
+ * Class builder method
+ * Defines override method modifieres for the class.
+ * The defined method modifiers will be called instead the method of the super class.
+ * You can call the method of the super class by calling this.SUPER(para1, para2)
+ * @function
+ * @param classObject {object} Maps method names to function bodies
+ * @name override
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handlePropoverride: function(map) {
+ var me = this
+ Joose.O.eachSafe(map, function (func, name) {
+ me.wrapMethod(name, "override", func);
+ })
+ },
+
+ /**
+ * Class builder method
+ * Defines augment method modifieres for the class.
+ * These method modifiers will be called in "most super first" order
+ * The methods may call this.INNER() to call the augement method in it's sup class.
+ * @function
+ * @param classObject {object} Maps method names to function bodies
+ * @name augment
+ * @memberof Joose.Builder
+ */
+ /** @ignore */
+ handlePropaugment: function(map) {
+ var me = this
+ Joose.O.eachSafe(map, function (func, name) {
+ me.wrapMethod(name, "augment", func, function () {
+ me.addMethod(name, func)
+ });
+ })
+ },
+
+ /**
+ * @ignore
+ */
+ handlePropdecorates: function(map) {
+ var me = this
+ Joose.O.eachSafe(map, function (classObject, attributeName) {
+ me.decorate(classObject, attributeName)
+ })
+ }
+};
+
+// See http://code.google.com/p/joose-js/wiki/JooseAttribute
+Joose.Attribute = function (name, props) {
+ this.initialize(name, props)
+}
+
+Joose.Attribute.prototype = {
+
+ _name: null,
+ _props: null,
+
+ getName: function () { return this._name },
+ getProps: function () { return this._props },
+
+ initialize: function (name, props) {
+ this._name = name;
+ this.setProps(props);
+ },
+
+ setProps: function (props) {
+ if(props) {
+ this._props = props
+ } else {
+ this._props = {};
+ }
+ },
+
+ getIsa: function () {
+ var props = this.getProps();
+ if("isa" in props && props.isa == null) {
+ throw new Error("You declared an isa property but the property is null.")
+ }
+ if(props.isa) {
+ if(!props.isa.meta) {
+ return props.isa()
+ }
+ return props.isa
+ }
+ return
+ },
+
+ addSetter: function (classObject) {
+ var meta = classObject.meta;
+ var name = this.getName();
+ var props = this.getProps();
+
+ var setterName = this.setterName();
+
+ if(meta.can(setterName)) { // do not override methods
+ return
+ }
+
+ var isa = this.getIsa();
+
+ var func;
+ if(isa) {
+
+ var checkerFunc = Joose.TypeChecker.makeTypeChecker(isa, props, "attribute", name)
+
+ // This setter is used if the attribute is constrained with an isa property in the attribute initializer
+ func = function setterWithIsaCheck (value, errorHandler) {
+ value = checkerFunc(value, errorHandler)
+ this[name] = value
+ return this;
+ }
+ } else {
+ func = function setter (value) {
+ this[name] = value
+ return this;
+ }
+ }
+ meta.addMethod(setterName, func);
+ },
+
+
+ addGetter: function (classObject) {
+ var meta = classObject.meta;
+ var name = this.getName();
+ var props = this.getProps()
+
+ var getterName = this.getterName();
+
+ if(meta.can(getterName)) { // never override a method
+ return
+ }
+
+ var func = function getter () {
+ return this[name]
+ }
+
+ var init = props.init;
+
+ if(props.lazy) {
+ func = function lazyGetter () {
+ var val = this[name];
+ if(typeof val == "function" && val === init) {
+ this[name] = val.apply(this)
+ }
+ return this[name]
+ }
+ }
+
+ meta.addMethod(getterName, func);
+ },
+
+ initializerName: function () {
+ return this.toPublicName()
+ },
+
+ getterName: function () {
+ if(this.__getterNameCache) { // Cache the getterName (very busy function)
+ return this.__getterNameCache
+ }
+ this.__getterNameCache = "get"+Joose.S.uppercaseFirst(this.toPublicName())
+ return this.__getterNameCache;
+ },
+
+ setterName: function () {
+ if(this.__setterNameCache) { // Cache the setterName (very busy function)
+ return this.__setterNameCache
+ }
+ this.__setterNameCache = "set"+Joose.S.uppercaseFirst(this.toPublicName())
+ return this.__setterNameCache;
+ },
+
+ isPrivate: function () {
+ return this.getName().charAt(0) == "_"
+ },
+
+ toPublicName: function () {
+
+ if(this.__publicNameCache) { // Cache the publicName (very busy function)
+ return this.__publicNameCache
+ }
+
+ var name = this.getName();
+ if(this.isPrivate()) {
+ this.__publicNameCache = name.substr(1)
+ return this.__publicNameCache;
+ }
+ this.__publicNameCache = name
+ return this.__publicNameCache
+ },
+
+ handleIs: function (classObject) {
+ var meta = classObject.meta;
+ var name = this.getName();
+ var props = this.getProps();
+
+ var is = props.is;
+
+ if(is == "rw" || is == "ro") {
+ this.addGetter(classObject);
+ }
+ if(is == "rw") {
+ this.addSetter(classObject)
+ }
+ },
+
+ handleInit: function (classObject) {
+ var props = this.getProps();
+ var name = this.getName();
+
+ classObject.prototype[name] = null;
+ if(typeof props.init != "undefined") {
+ var val = props.init;
+ var type = typeof val;
+
+ classObject.prototype[name] = val;
+ }
+ },
+
+ handleProps: function (classObject) {
+ this.handleIs(classObject);
+ this.handleInit(classObject)
+ },
+
+ apply: function (classObject) {
+
+ var meta = classObject.meta;
+ var name = this.getName();
+
+ this.handleProps(classObject)
+
+ meta.attributeNames.push(name)
+
+ meta.setAttribute(name, this)
+ meta.attributes[name] = this;
+ }
+
+
+}
+
+// See http://code.google.com/p/joose-js/wiki/JooseMethod
+Joose.Method = function (name, func, props) {
+ this.initialize(name, func, props)
+}
+
+Joose.Method.prototype = {
+
+ _name: null,
+ _body: null,
+ _props: null,
+ _isFromSuperClass: false,
+ _isClassMethod: false,
+
+ getName: function () { return this._name },
+ getBody: function () { return this._body },
+ getProps: function () { return this._props },
+
+ isFromSuperClass: function () {
+ return this._isFromSuperClass
+ },
+
+ setIsFromSuperClass: function (bool) {
+ this._isFromSuperClass = bool
+ },
+
+ copy: function () {
+ // Hardcode class name because at this point this.meta.instantiate might not work yet
+ // this is later overridden in the file Joose/Method.js
+ return new Joose.Method(this.getName(), this.getBody(), this.getProps())
+ },
+
+ initialize: function (name, func, props) {
+ this._name = name;
+ this._body = func;
+ this._props = props;
+
+ func.name = name
+
+ func.meta = this
+ },
+
+ isClassMethod: function () { return this._isClassMethod },
+
+ apply: function (thisObject, args) {
+ return this._body.apply(thisObject, args)
+ },
+
+ addToClass: function (c) {
+ // optimized due to heavy calls
+ var base = Joose.Method.prototype;
+ var name = this.getName === base.getName ? this._name : this.getName();
+ var func = this.asFunction === base.asFunction ? this._body : this.asFunction()
+ c.prototype[name] = func
+ },
+
+
+ // direct call
+ asFunction: function () {
+ return this._body
+ }
+}
+
+
+
+Joose.bootstrap()
+
+
+// ##########################
+// File: Joose/Builder.js
+// ##########################
+// Could be refactored to a Joose.Class (by manually building the class)
+
+/**
+ * Assorted tools to build a class
+ *
+ * The functions Class(), Module() and joosify() are global. All other methods
+ * may be used inside Class definitons like this:
+ *
+ * <pre>
+ * Module("com.test.me", function () {
+ * Class("MyClass", {
+ * isa: SuperClass,
+ * methods: {
+ * hello: function () { alert('world') }
+ * }
+ * })
+ * })
+ * </pre>
+ * @constructor
+ */
+
+
+
+Joose.Builder = function () {
+ /** @ignore */
+ this.globalize = function () {
+ Joose.O.each(Joose.Builder.Globals, function (func, name) {
+ var globalName = "Joose"+name
+ if(typeof joose.top[name] == "undefined") {
+ joose.top[name] = func
+ }
+
+ joose.top[globalName] = func
+ });
+ }
+}
+
+/** @ignore */
+Joose.Builder.Globals = {
+ /**
+ * Global function that creates or extends a module
+ * @function
+ * @param name {string} Name of the module
+ * @param functionThatCreatesClassesAndRoles {function} Pass a function reference that calls Class(...) as often as you want. The created classes will be put into the module
+ * @name Module
+ */
+ /** @ignore */
+ Module: function (name, functionThatCreatesClassesAndRoles) {
+ return Joose.Module.setup(name, functionThatCreatesClassesAndRoles)
+ },
+
+ Role: function (name, props) {
+ if(!props.meta) {
+ props.meta = Joose.Role;
+ }
+ return JooseClass(name, props)
+ },
+
+ Prototype: function (name, props) {
+ if(!props.meta) {
+ props.meta = Joose.Prototype;
+ }
+ return JooseClass(name, props);
+ },
+
+ /**
+ * Global function that creates a class (If the class already exists it will be extended)
+ * @function
+ * @param name {string} Name of the the class
+ * @param props {object} Declaration if the class. The object keys are used as builder methods. The values are passed as arguments to the builder methods.
+ * @name Class
+ */
+ /** @ignore */
+ Class: function (name, props) {
+
+ var c = null;
+
+ if(name) {
+ var className = name;
+ if(joose.currentModule) {
+ className = joose.currentModule.getName() + "." + name
+ }
+ var root = joose.top;
+ var parts = className.split(".")
+
+ for(var i = 0; i < parts.length; i++) {
+ root = root[parts[i]]
+ }
+ c = root;
+ }
+
+ if(c == null) {
+
+ var metaClass;
+
+ /* Use the custom meta class if provided */
+ if(props && props.meta) {
+ metaClass = props.meta
+ delete props.meta
+ }
+ /* Otherwise use the meta class of the parent class (If there is one)
+ * If the parent class is Joose.Class, we don't change the meta class but use the default
+ * because that Joose.Class's meta class is only needed for bootstrapping
+ * purposes. */
+ else if(props && props.isa && props.isa != Joose.Class) {
+ metaClass = props.isa.meta.builder
+ //alert(name + metaClass + props.isa.meta)
+ }
+ /* Default meta class is Joose.Class */
+ else {
+ metaClass = Joose.Class;
+ }
+
+ var c = metaClass.create(name, null, joose.currentModule)
+
+ var className = c.meta.className()
+
+ if(name && className) {
+ var root = joose.top;
+ var n = new String(className);
+ var parts = n.split(".");
+ for(var i = 0; i < parts.length - 1; i++) {
+ if(root[parts[i]] == null) {
+ root[parts[i]] = {};
+ }
+ root = root[parts[i]];
+ }
+ root[parts[parts.length - 1]] = c
+ }
+
+ }
+
+ c.meta.initializeFromProps(props)
+
+ return c
+ },
+
+ Type: function (name, props) {
+ var isAnon = false
+ if(arguments.length == 1 && name instanceof Object) {
+ props = name;
+ isAnon = true;
+ }
+
+ if(props instanceof RegExp || props instanceof Function) {
+ props = {
+ where: props
+ }
+ }
+
+ if(isAnon) {
+ name = "AnonType: "+(props.where ? props.where.toString() : "");
+ }
+
+ var t = Joose.TypeConstraint.newFromTypeBuilder(name, props);
+
+ if(!isAnon) {
+ var m = joose.currentModule
+
+ if(!m) {
+ JooseModule("Joose.Type");
+ if(typeof joose.top.TYPE == "undefined") {
+ joose.top.TYPE = Joose.Type;
+ }
+ m = Joose.Type.meta;
+ }
+
+ m.addElement(t)
+ m.getContainer()[name] = t;
+ }
+ return t
+ },
+
+ /**
+ * Global function to turn a regular JavaScript constructor into a Joose.Class
+ * @function
+ * @param name {string} Name of the class
+ * @param props {function} The constructor
+ * @name joosify
+ */
+ /** @ignore */
+ joosify: function (standardClassName, standardClassObject) {
+ var c = standardClassObject;
+ var metaClass = new Joose.Class();
+ metaClass.builder = Joose.Class;
+
+ c.toString = function () { return this.meta.className() }
+ c = metaClass.createClass(standardClassName, c)
+
+ var meta = c.meta;
+
+ for(var name in standardClassObject.prototype) {
+ if(name == "meta") {
+ continue
+ }
+ var value = standardClassObject.prototype[name]
+ if(typeof(value) == "function") {
+ meta.addMethod(name, value)
+ } else {
+ var props = {};
+ if(typeof(value) != "undefined") {
+ props.init = value
+ }
+ meta.addAttribute(name, props)
+ }
+ }
+
+ return c
+ },
+
+ /** @ignore */
+ rw: "rw",
+ /** @ignore */
+ ro: "ro"
+};
+
+joose.init();
+Joose.bootstrapCompletedBuilder();
+
+
+// ##########################
+// File: Joose/Class.js
+// ##########################
+
+// ##########################
+// File: Joose/Method.js
+// ##########################
+/*
+ * A class for methods
+ * Originally defined in Joose.js
+ *
+ * See http://code.google.com/p/joose-js/wiki/JooseMethod
+ */
+
+(function (Class) {
+
+Class("Joose.Method", {
+ methods: {
+
+ copy: function () {
+ return this.meta.instantiate(this.getName(), this.getBody(), this.getProps())
+ },
+
+ // creates a new method object with the same name
+ _makeWrapped: function (func) {
+ return this.meta.instantiate(this.getName(), func); // Should there be , this.getProps() ???
+ },
+
+ around: function (func) {
+ var orig = this.getBody();
+ return this._makeWrapped(function aroundWrapper () {
+ var me = this;
+ var bound = function () { return orig.apply(me, arguments) }
+ return func.apply(this, Joose.A.concat([bound], arguments))
+ })
+ },
+ before: function (func) {
+ var orig = this.getBody();
+ return this._makeWrapped(function beforeWrapper () {
+ func.apply(this, arguments)
+ return orig.apply(this, arguments);
+ })
+ },
+ after: function (func) {
+ var orig = this.getBody();
+ return this._makeWrapped(function afterWrapper () {
+ var ret = orig.apply(this, arguments);
+ func.apply(this, arguments);
+ return ret
+ })
+ },
+
+ override: function (func) {
+ var orig = this.getBody();
+ return this._makeWrapped(function overrideWrapper () {
+ var me = this;
+ var bound = function () { return orig.apply(me, arguments) }
+ var before = this.SUPER;
+ this.SUPER = bound;
+ var ret = func.apply(this, arguments);
+ this.SUPER = before;
+ return ret
+ })
+ },
+
+ augment: function (func) {
+ var orig = this.getBody();
+ orig.source = orig.toString();
+ return this._makeWrapped(function augmentWrapper () {
+ var exe = orig;
+ var me = this;
+ var inner = func
+ inner.source = inner.toString();
+ if(!this.__INNER_STACK__) {
+ this.__INNER_STACK__ = [];
+ };
+ this.__INNER_STACK__.push(inner)
+ var before = this.INNER;
+ this.INNER = function () {return me.__INNER_STACK__.pop().apply(me, arguments) };
+ var ret = orig.apply(this, arguments);
+ this.INNER = before;
+ return ret
+ })
+ }
+ }
+})
+
+})(JooseClass);
+
+// ##########################
+// File: Joose/ClassMethod.js
+// ##########################
+(function (Class) {
+
+Class("Joose.ClassMethod", {
+ isa: Joose.Method,
+ after: {
+ initialize: function () {
+ this._isClassMethod = true
+ }
+ },
+ methods: {
+ addToClass: function (c) {
+ c[this.getName()] = this.asFunction()
+ },
+
+ copy: function () {
+ return new Joose.ClassMethod(this.getName(), this.getBody(), this.getProps())
+ }
+ }
+})
+
+Joose.bootstrapCompletedClassMethod()
+
+})(JooseClass);
+
+// ##########################
+// File: Joose/Attribute.js
+// ##########################
+/*
+ * This handles the following attribute properties
+ * * init with function value in non-lazy initialization
+ * * required attributes in initializaion
+ * * handles for auto-decoration
+ * * predicate for attribute availability checks
+ *
+ *
+ * See http://code.google.com/p/joose-js/wiki/JooseAttribute
+ */
+
+(function (Class) {
+Class("Joose.Attribute", {
+ after: {
+ handleProps: function (classObject) {
+ this.handleHandles(classObject);
+ this.handlePredicate(classObject);
+ }
+ },
+ methods: {
+
+ isPersistent: function () {
+ var props = this.getProps()
+ if(props.persistent == false) {
+ return false
+ }
+ return true
+ },
+
+ doInitialization: function (object, paras) {
+ var name = this.initializerName();
+ var _name = this.getName();
+ var value;
+ var isSet = false;
+ if(typeof paras != "undefined" && typeof paras[name] != "undefined") {
+ value = paras[name];
+ isSet = true;
+ } else {
+ var props = this.getProps();
+
+ var init = props.init;
+
+ if(typeof init == "function" && !props.lazy) {
+ // if init is not a function, we have put it in the prototype, so it is already here
+ value = init.call(object)
+ isSet = true
+ } else {
+ // only enforce required property if init is not run
+ if(props.required) {
+ throw "Required initialization parameter missing: "+name + "(While initializing "+object+")"
+ }
+ }
+ }
+ if(isSet) {
+ var setterName = this.setterName();
+ if(object.meta.can(setterName)) { // use setter if available
+ object[setterName](value)
+ } else { // direct attribute access
+ object[_name] = value
+ }
+ }
+ },
+
+ handleHandles: function (classObject) {
+ var meta = classObject.meta;
+ var name = this.getName();
+ var props = this.getProps();
+
+ var handles = props.handles;
+ var isa = props.isa
+
+ if(handles) {
+ if(handles == "*") {
+ if(!isa) {
+ throw "I need an isa property in order to handle a class"
+ }
+
+ // receives the name and should return a closure
+ var optionalHandlerMaker = props.handleWith;
+
+ meta.decorate(isa, name, optionalHandlerMaker)
+ }
+ else {
+ throw "Unsupported value for handles: "+handles
+ }
+
+ }
+ },
+
+ handlePredicate: function (classObject) {
+ var meta = classObject.meta;
+ var name = this.getName();
+ var props = this.getProps();
+
+ var predicate = props.predicate;
+
+ var getter = this.getterName();
+
+ if(predicate) {
+ meta.addMethod(predicate, function () {
+ var val = this[getter]();
+ return val ? true : false
+ })
+ }
+ }
+ }
+})
+})(JooseClass);
+
+// ##########################
+// File: Joose/Role.js
+// ##########################
+
+/*
+ * An Implementation of Traits
+ * see http://www.iam.unibe.ch/~scg/cgi-bin/scgbib.cgi?query=nathanael+traits+composable+units+ecoop
+ *
+ * Current Composition rules:
+ * - At compile time we override existing (at the time of rule application) methods
+ * - At runtime we dont
+ */
+
+(function (Class) {
+
+Class("Joose.Role", {
+ isa: Joose.Class,
+ has: ["requiresMethodNames", "methodModifiers", "metaRoles"],
+ methods: {
+
+ // Add a method modifier that will be applied to classes implementing this role.
+ wrapMethod: function (name, wrappingStyle, func, notPresentCB) {
+ // queue arguments given to this function for later application to actual class
+ this.methodModifiers.push(arguments)
+ var test = this.methodModifiers
+ },
+
+ requiresMethod: function (methodName) {
+ var bool = false;
+ Joose.A.each(this.requiresMethodNames, function (name) {
+ if(methodName == name) {
+ bool = true
+ }
+ })
+
+ return bool
+ },
+
+ addInitializer: Joose.emptyFunction,
+
+ // Roles can not be instantiated
+ defaultClassFunctionBody: function () {
+ var f = function () {
+ throw new Error("Roles may not be instantiated.")
+ };
+ joose.addToString(f, function () { return this.meta.className() })
+ return f
+ },
+
+ // Roles can not be instantiated
+ addSuperClass: function () {
+ throw new Error("Roles may not inherit from a super class.")
+ },
+
+ initialize: function () {
+ this._name = "Joose.Role"
+ this.requiresMethodNames = [];
+ this.methodModifiers = [];
+ },
+
+ // Class implementing this role must implement a method named methodName
+ addRequirement: function (methodName) {
+ this.requiresMethodNames.push(methodName)
+ },
+
+ // Experimental method to unapply classes from roles.
+ // Only works on roles that were applied at runtime
+ // Currently does not work in IE (depends on __proto__)
+ unapply: function (object) {
+ if(!joose.isInstance(object)) {
+ throw new Error("You way only remove roles from instances.")
+ }
+ if(!object.meta.isDetached) {
+ throw new Error("You may only remove roles that were applied at runtime")
+ }
+
+ var role = this.getClassObject()
+
+ var roles = object.meta.myRoles; // myRoles!!!
+ var found = false;
+ var otherRoles = [];
+ for(var i = 0; i < roles.length; i++) {
+ if(roles[i] === role) {
+ found = true;
+ } else {
+ otherRoles.push(roles[i])
+ }
+ }
+ if(!found) {
+ throw new Error("The role "+this.className()+" was not applied to the object at runtime")
+ }
+
+ var superClass = object.meta.getSuperClass();
+ var c = superClass.meta.makeAnonSubclass();
+
+
+ // rebless object
+ /*if(typeof(object.__proto__) != "undefined") {
+ object.__proto__ = c.prototype
+ } else { // Workaround for IE:
+ */
+
+ var test = new c()
+
+ // add all roles except the one that we are removing
+ for(var i = 0; i < otherRoles.length; i++) {
+ var role = otherRoles[i]
+ c.meta.addRole(role)
+ }
+
+ c.prototype = test
+
+ object.meta = c.meta;
+ object.constructor = c;
+ object.__proto__ = test
+ },
+
+ addMethodToClass: function (method, classObject) {
+ var name = method.getName()
+ var cur;
+ if(method.isClassMethod()) {
+ cur = classObject.meta.getClassMethodObject(name)
+ } else {
+ cur = classObject.meta.getMethodObject(name)
+ }
+ // Methods from roles take precedence over methods from a super class
+ if(!cur || cur.isFromSuperClass()) {
+ classObject.meta.addMethodObject(method)
+ }
+ },
+
+ addAttributeToClass: function(attr, classObject) {
+ var name = attr.getName();
+ //don't add the attribute if it already exists in the class
+ if (!classObject.meta.getAttribute(name)) {
+ this.getAttribute(name).apply(classObject);
+ }
+ },
+
+ apply: function (object) {
+
+ // XXX ask in #moose whether this is correct
+ // A Role should not be applied twice
+ if(object.meta.does(this.getClassObject())) {
+ return false
+ }
+
+ if(joose.isInstance(object)) {
+ // Create an anonymous subclass ob object's class
+
+ object.detach();
+ object.meta.addRole(this.getClassObject());
+ this.applyMethodModifiers(object);
+ var throwException = true;
+ this.isImplementedBy(object, throwException)
+ } else {
+ // object is actually a class
+ var me = this;
+ var names = me.getMethodNames();
+ var attrs = me.getAttributes();
+ //alert("Super"+me.name + " -> "+classObject.meta.name +"->" + names)
+ Joose.O.each(attrs, function applyAttrs (attr) {
+ me.addAttributeToClass(attr, object);
+ });
+
+ Joose.A.each(names, function applyMethod (name) {
+
+ var m = me.getMethodObject(name)
+ if(m) {
+ me.addMethodToClass(m, object)
+ }
+
+ m = me.getClassMethodObject(name)
+ if(m) {
+ me.addMethodToClass(m, object)
+ }
+ })
+
+
+ // Meta roles are applied to the meta class of the class that implements us
+ if(this.metaRoles) {
+ Joose.A.each(this.metaRoles, function applyMetaRole (role) {
+ role.meta.apply(object.meta)
+ })
+ }
+ }
+ return true
+ },
+
+ // should be called by class builder after class has been initialized from props
+ applyMethodModifiers: function (object) {
+
+ // Apply method modifiers
+ Joose.A.each(this.methodModifiers, function applyMethodModifier (paras) {
+ object.meta.wrapMethod.apply(object.meta, paras)
+ })
+ },
+
+ // Checks whether classObject (can also be any Joose object) implements this role.
+ // If second para is true, throws an exception when a method is missing.
+ hasRequiredMethods: function (classObject, throwException) {
+ var me = this
+ var complete = true
+ Joose.A.each(this.requiresMethodNames, function (value) {
+ var found = classObject.meta.can(value)
+ if(!found) {
+ if(throwException) {
+ throw("Class "+classObject.meta.className()+" does not fully implement the role "+me.className()+". The method is "+value+" missing.")
+ }
+ complete = false
+ return
+ }
+ })
+ return complete
+ },
+
+ // This is called by validateClass in Joose.Class.
+ // This is not part of apply because apply might be called way before class construction is complete.
+ isImplementedBy: function (classObject, throwException) {
+
+ var complete = this.hasRequiredMethods(classObject, throwException);
+ if(complete) {
+ complete = this.implementsMyMethods(classObject);
+ }
+ return complete
+ },
+
+ // the metaRoles prop allows a role to apply roles to the meta class of the class using the role
+ handlePropmetaRoles: function (arrayOfRoles) {
+ this.metaRoles = arrayOfRoles;
+ }
+ }
+})
+
+Joose.Role.anonymousClassCounter = 0;
+
+})(JooseClass);
+
+// ##########################
+// File: Joose/Singleton.js
+// ##########################
+(function (Role) {
+
+ var registry = {};
+ var locked = true;
+
+ /**
+ * Joose.Singleton
+ * Role for singleton classes.
+ * Gives a getInstance class method to classes using this role.
+ * The getInstance method will create a method on first invocation and return the same instance
+ * upon every consecutive invocation.
+ */
+ Role("Joose.Singleton", {
+
+ before: {
+ initialize: function () {
+ if(locked) {
+ var name = this.meta.className()
+ throw new Error("The class "+name+" is a singleton. Please use the class method getInstance().")
+ }
+ }
+ },
+
+ methods: {
+ singletonInitialize: function () {
+
+ }
+ },
+
+ classMethods: {
+ getInstance: function () {
+ var name = this.meta.className();
+ var instance = registry[name];
+ if(instance) {
+ return instance;
+ }
+ locked = false;
+ instance = this.meta.instantiate()
+ locked = true;
+ instance.singletonInitialize.apply(instance, arguments)
+ registry[name] = instance
+ return instance;
+ }
+ }
+ })
+})(JooseRole);
+
+// ##########################
+// File: Joose/SimpleRequest.js
+// ##########################
+/**
+ * Class to perform simple synchronous AJAX Requests used for component loading.
+ * @name Joose.SimpleRequest
+ * @class
+ */
+
+(function (Class) {
+
+Class("Joose.SimpleRequest", {
+
+ has: {_req: {}},
+ methods: {
+ initialize: function () {
+ if (window.XMLHttpRequest) {
+ this._req = new XMLHttpRequest();
+ } else {
+ this._req = new ActiveXObject("Microsoft.XMLHTTP");
+ }
+ },
+ /**
+ * Fetches text from an URL
+ * @name getText
+ * @param {string} url The URL
+ * @function
+ * @memberof Joose.SimpleRequest
+ */
+ getText: function (url) {
+ this._req.open("GET", url, false);
+ try {
+ this._req.send(null);
+ if (this._req.status == 200 || this._req.status == 0)
+ return this._req.responseText;
+ } catch (e) {
+ throw("File not found: " + url);
+ return null;
+ };
+
+ throw("File not found: " + url);
+ return null;
+ }
+ }
+})
+})(JooseClass);
+
+// ##########################
+// File: Joose/Gears.js
+// ##########################
+/**
+ * Joose.Gears is a meta class for classes that want to delegate work to gears workers
+ * @name Joose.Gears
+ * @extends Joose.Class
+ * @constructor
+ */
+
+(function (Class) {
+
+Class("Joose.Gears", {
+ isa: Joose.Class,
+ has: {
+ wp: { },
+ calls: { init: {} },
+ callIndex: { init: 0 }
+ },
+
+ methods: {
+ initialize: function () {
+ JooseGearsInitializeGears()
+ if(this.canGears()) {
+ this.wp = google.gears.factory.create('beta.workerpool');
+ var me = this;
+ this.wp.onmessage = function (a,b,message) {
+ me.handleGearsMessage(message)
+ }
+ }
+ },
+ handleGearsMessage: function (message) {
+ var paras = message.body
+ var cbName = paras.to;
+ var ret = paras.ret;
+ var object = this.calls[paras.index];
+ if(object.meta.can(cbName)) {
+ object[cbName].call(object, ret)
+ }
+ //delete this.calls[paras.index]
+ },
+
+ canGears: function () {
+ return this.meta.c.clientHasGears()
+ },
+
+ /**
+ * Adds a worker to the class
+ * @function
+ * @name addWorker
+ * @param {string} Name of the worker
+ * @param {function} Function body of the worker
+ * @param {props} Optional properties for the created method (ignored)
+ * @memberof Joose.Gears
+ */
+ addWorker: function (name, func, props) {
+
+ var cbName = "on"+Joose.S.uppercaseFirst(name)
+
+ var ajaxRequestFunc = this.meta.getClassObject().ajaxRequest;
+
+ // No gears, then work inline
+ if(!this.canGears()) {
+ var wrapped = function () {
+ var me = this;
+ var object = {
+ sendReturn: function (ret, cbName) { if(me.meta.can(cbName)) me[cbName].call(me, ret) },
+ clientHasGears: function () { return false },
+ ajaxRequest: ajaxRequestFunc
+ };
+ var ret = func.apply(object, arguments);
+ object.sendReturn(ret, cbName)
+ }
+ this.addMethod(name, wrapped, props)
+ return
+ }
+
+ // OK, we have gears support
+
+ var jsonUrl = this.can("jsonURL") ? this.c.jsonURL() : "json2.js";
+
+ var json = new Joose.SimpleRequest().getText(jsonUrl)
+
+ var source =
+ "var timer = google.gears.factory.create('beta.timer');\n"+ // always provide timer
+ "function aClass () {}; aClass.prototype."+name+" = "+func.toString()+"\n\n"+
+ "aClass.prototype.clientHasGears = function () { return true }\n"+
+ "aClass.prototype.ajaxRequest = "+ajaxRequestFunc.toString()+"\n\n"+
+ "var wp = google.gears.workerPool;\n" +
+ "wp.onmessage = function (a,b,message) {\n"+
+
+ "var paras = message.body;\n"+
+
+ "var o = new aClass();\n"+
+
+ "o.sendReturn = function (ret, cbName) { wp.sendMessage({ ret: ret, to: cbName, index: paras.index }, message.sender) } \n"+
+
+ "var ret = o."+name+".apply(o, paras.args); if(!ret) ret = null; \n"+
+ "o.sendReturn(ret, paras.cbName);"+
+ "\n}\n\n";
+
+
+
+ source += json
+
+ var wp = this.wp;
+
+ var childId = wp.createWorker(source)
+
+ var me = this
+
+ var wrapped = function () {
+ var args = [];
+ for(var i = 0; i < arguments.length; i++) {
+ args.push(arguments[i])
+ }
+ var message = { args: args, cbName: cbName, index: me.callIndex };
+ wp.sendMessage(message, childId);
+ me.calls[me.callIndex] = this
+ me.callIndex++
+
+ }
+ this.addMethod(name, wrapped, props)
+
+ }
+ },
+
+ classMethods: {
+ // builds an environment for non gears platform where the regular window looks more like a gears worker
+ // APIs implemented: Timer
+ setupGearsCompat: function () {
+ // setup gears timer api
+ window.timer = {
+ setTimeout: function (func, time) { return window.setTimeout(func, time) },
+ setInterval: function (func, time) { return window.setInterval(func, time) },
+ clearTimeout: function (timer) { return window.clearTimeout(timer) },
+ clearInterval: function (timer) { return window.clearInterval(timer) }
+ };
+ },
+
+ clientHasGears: function () { // XXX code dup with instance method
+ if(typeof this._canGears != "undefined") return this._canGears
+
+ if(window.google && window.google.gears && window.google.gears.factory) {
+ try {
+ google.gears.factory.create('beta.httprequest');
+ } catch(e) {
+ this._canGears = false;
+ return false
+ }
+ this._canGears = true;
+ return true
+ }
+ this._canGears = false;
+ return false
+ },
+
+ // a simple AJAX request that uses gears if available
+ ajaxRequest: function (method, url, data, callback, errorCallback) {
+
+ var request
+ if(this.clientHasGears()) {
+ request = google.gears.factory.create('beta.httprequest');
+ } else {
+ request = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
+ }
+ var dataString = ""
+ if(data) {
+ for(var i in data) {
+ dataString += encodeURIComponent(i)+"="+encodeURIComponent(data[i])+"&"
+ }
+ }
+ var theUrl = url;
+ if(data && method == "GET") {
+ theUrl += "?"+dataString
+ }
+ request.open(method, theUrl, true);
+
+ request.onreadystatechange = function onreadystatechange () {
+ if (request.readyState == 4) {
+ if(request.status >= 200 && request.status < 400) {
+ var res = request.responseText;
+ callback(res)
+ } else {
+ if(errorCallback) {
+ return errorCallback(request)
+ } else {
+ throw new Error("Error fetching url "+theUrl+". Response code: " + request.status + " Response text: "+request.responseText)
+ }
+ }
+ }
+ };
+ if(data && method == "POST") {
+ // FIXME determine page encoding instead of always using UTF8
+ request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
+ request.send(dataString)
+ } else {
+ dataString = ""
+ request.send(dataString);
+ }
+ }
+ }
+})
+
+})(JooseClass);
+
+// Copyright 2007, Google Inc.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+// this list of conditions and the following disclaimer in the documentation
+// and/or other materials provided with the distribution.
+// 3. Neither the name of Google Inc. nor the names of its contributors may be
+// used to endorse or promote products derived from this software without
+// specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// Sets up google.gears.*, which is *the only* supported way to access Gears.
+//
+// Circumvent this file at your own risk!
+//
+// In the future, Gears may automatically define google.gears.* without this
+// file. Gears may use these objects to transparently fix bugs and compatibility
+// issues. Applications that use the code below will continue to work seamlessly
+// when that happens.
+
+// Sorry Google for modifying this :)
+function JooseGearsInitializeGears() {
+ // We are already defined. Hooray!
+ if (window.google && google.gears) {
+ return;
+ }
+
+ var factory = null;
+
+ // Firefox
+ if (typeof GearsFactory != 'undefined') {
+ factory = new GearsFactory();
+ } else {
+ // IE
+ try {
+ factory = new ActiveXObject('Gears.Factory');
+ // privateSetGlobalObject is only required and supported on WinCE.
+ if (factory.getBuildInfo().indexOf('ie_mobile') != -1) {
+ factory.privateSetGlobalObject(this);
+ }
+ } catch (e) {
+ // Safari
+ if (navigator.mimeTypes["application/x-googlegears"]) {
+ factory = document.createElement("object");
+ factory.style.display = "none";
+ factory.width = 0;
+ factory.height = 0;
+ factory.type = "application/x-googlegears";
+ document.documentElement.appendChild(factory);
+ }
+ }
+ }
+
+ // *Do not* define any objects if Gears is not installed. This mimics the
+ // behavior of Gears defining the objects in the future.
+ if (!factory) {
+ return;
+ }
+
+ // Now set up the objects, being careful not to overwrite anything.
+ //
+ // Note: In Internet Explorer for Windows Mobile, you can't add properties to
+ // the window object. However, global objects are automatically added as
+ // properties of the window object in all browsers.
+ if (!window.google) {
+ google = {};
+ }
+
+ if (!google.gears) {
+ google.gears = {factory: factory};
+ }
+}
+
+
+
+// ##########################
+// File: Joose/Storage.js
+// ##########################
+
+
+(function (Class, Role) {
+
+Role("Joose.Storage", {
+
+ methods: {
+ // gets called by the JSON.stringify method
+ toJSON: function () {
+ // Evil global var TEMP_SEEN. See Joose.Storage.Unpacker.patchJSON
+ var packed = this.pack(Joose.Storage.TEMP_SEEN);
+ return packed;
+ },
+
+ // Generate an object identity (a unique integer for this object
+ // This is cached in a property called __ID__
+ // Override this in object representing values
+ identity: function () {
+ if(this.__ID__) {
+ return this.__ID__
+ } else {
+ return this.__ID__ = Joose.Storage.OBJECT_COUNTER++
+ }
+ },
+
+ pack: function (seen) {
+ return this.meta.c.storageEngine().pack(this, seen)
+ }
+ },
+
+ classMethods: {
+
+ storageEngine: function () {
+ return Joose.Storage.Engine
+ },
+
+ unpack: function (data) {
+ return this.storageEngine().unpack(this, data)
+ }
+ }
+
+})
+
+
+
+Role("Joose.Storage.jsonpickle", {
+ does: Joose.Storage,
+
+ classMethods: {
+ storageEngine: function () {
+ return Joose.Storage.Engine.jsonpickle
+ }
+ }
+})
+
+Joose.Storage.OBJECT_COUNTER = 1;
+
+// This storage engine is base on MooseX::Storage: http://search.cpan.org/~nuffin/MooseX-Storage-0.14/lib/MooseX/Storage.pm
+Class("Joose.Storage.Engine", {
+
+ classMethods: {
+
+ pack: function (object, seen) {
+
+ /*if(seen) {
+ var id = object.identity()
+ var obj = seen[id];
+ if(obj) {
+ return {
+ __ID__: id
+ }
+ }
+ }*/
+
+ if(object.meta.can("prepareStorage")) {
+ object.prepareStorage()
+ }
+
+ if(seen) {
+ seen[object.identity()] = true
+ }
+
+ var o = {
+ __CLASS__: this.packedClassName(object),
+ __ID__: object.identity()
+ };
+
+ var attrs = object.meta.getAttributes();
+
+ Joose.O.eachSafe(attrs, function packAttr (attr, name) {
+ if(attr.isPersistent()) {
+ o[name] = object[name];
+ }
+ });
+
+ return o
+ },
+
+ unpack: function (classObject, data) {
+ var meta = classObject.meta
+ var me = meta.instantiate();
+ var seenClass = false;
+ Joose.O.eachSafe(data, function unpack (value,name) {
+ if(name == "__CLASS__") {
+ var className = Joose.Storage.Unpacker.packedClassNameToJSClassName(value)
+ if(className != me.meta.className()) {
+ throw new Error("Storage data is of wrong type "+className+". I am "+me.meta.className()+".")
+ }
+ seenClass = true
+ return
+ }
+ me[name] = value
+ })
+ if(!seenClass) {
+ throw new Error("Serialized data needs to include a __CLASS__ attribute.: "+data)
+ }
+
+ // Unpacked id may come from another global counter and thus must be discarded
+ delete me.__ID__
+
+ if(me.meta.can("finishUnpack")) {
+ me.finishUnpack()
+ }
+
+ return me
+ },
+
+ packedClassName: function (object) {
+ if(object.meta.can("packedClassName")) {
+ return object.packedClassName();
+ }
+ var name = object.meta.className();
+ var parts = name.split(".");
+ return parts.join("::");
+ }
+ }
+
+})
+
+Class("Joose.Storage.Engine.jsonpickle", {
+
+ classMethods: {
+
+ pack: function (object, seen) {
+
+
+ /*if(seen) {
+ var id = object.identity()
+ var obj = seen[id];
+ if(obj) {
+ return {
+ objectid__: id
+ }
+ }
+ }*/
+
+ if(object.meta.can("prepareStorage")) {
+ object.prepareStorage()
+ }
+
+ if(seen) {
+ seen[object.identity()] = true
+ }
+
+ var o = {
+ classname__: this.packedClassName(object),
+ classmodule__: this.packedModuleName(object),
+ objectid__: object.identity()
+ };
+
+ var attrs = object.meta.getAttributes();
+
+ Joose.O.eachSafe(attrs, function packAttr (attr, name) {
+ if(attr.isPersistent()) {
+ o[name] = object[name];
+ }
+ })
+
+ return o
+ },
+
+ unpack: function (classObject, data) {
+ var meta = classObject.meta
+ var me = meta.instantiate();
+ var seenClass = false;
+ Joose.O.eachSafe(data, function unpack (value,name) {
+ if(name == "classname__") {
+ var className = value;
+ var module = data.classmodule__
+ if(module) {
+ className = "" + module + "." + value
+ }
+ if(className != me.meta.className()) {
+ throw new Error("Storage data is of wrong type "+className+". I am "+me.meta.className()+".")
+ }
+ seenClass = true
+ return
+ }
+ if(name == "classmodule__") {
+ return
+ }
+ me[name] = value
+ })
+ if(!seenClass) {
+ throw new Error("Serialized data needs to include a __CLASS__ attribute.: "+data)
+ }
+
+ if(me.meta.can("finishUnpack")) {
+ me.finishUnpack()
+ }
+
+ return me
+ },
+
+ packedClassName: function (object) {
+ var name = object.meta.className();
+ var parts = name.split(".");
+ return parts.pop()
+ },
+
+ packedModuleName: function (object) {
+ var name = object.meta.className();
+ var parts = name.split(".");
+ parts.pop();
+ return parts.join(".");
+ }
+ }
+
+})
+
+Joose.Storage.storageEngine = Joose.Storage.Engine
+Joose.Storage.jsonpickle.storageEngine = Joose.Storage.Engine.jsonpickle
+
+})(JooseClass, JooseRole);
+
+// ##########################
+// File: Joose/Storage/Unpacker.js
+// ##########################
+(function (Class) {
+
+Class("Joose.Storage.Unpacker", {
+ classMethods: {
+ unpack: function (data) {
+ var name = data.__CLASS__;
+ if(!name) {
+ throw("Serialized data needs to include a __CLASS__ attribute.")
+ }
+ var jsName = this.packedClassNameToJSClassName(name)
+
+ var co = this.meta.classNameToClassObject(jsName);
+
+ var obj = co.unpack(data);
+
+ var id;
+ if(Joose.Storage.CACHE && (id = data.__ID__)) {
+ Joose.Storage.CACHE[id] = obj
+ }
+
+ return obj
+ },
+
+ // Format My::Class::Name-0.01 We ignore the version
+ packedClassNameToJSClassName: function (packed) {
+ var parts = packed.split("-");
+ parts = parts[0].split("::");
+ return parts.join(".");
+ },
+
+ jsonParseFilter: function (key, value) {
+ if(value != null && typeof value == "object") {
+ if(value.__ID__ && Joose.Storage.CACHE && Joose.Storage.CACHE[value.__ID__]) {
+ return Joose.Storage.CACHE[value.__ID__]
+ }
+ if(value.__CLASS__) {
+ return Joose.Storage.Unpacker.unpack(value)
+ }
+ }
+ return value
+ },
+
+ patchJSON: function () {
+ var orig = JSON.parse;
+ var storageFilter = this.jsonParseFilter
+ JSON.parse = function (s, filter) {
+ Joose.Storage.CACHE = {}
+ return orig(s, function JooseJSONParseFilter (key, value) {
+ var val = value;
+ if(filter) {
+ val = filter(key, value)
+ }
+ return storageFilter(key,val)
+ })
+ }
+
+ var stringify = JSON.stringify;
+ JSON.stringify = function () {
+ Joose.Storage.TEMP_SEEN = {}
+ return stringify.apply(JSON, arguments)
+ }
+ }
+ }
+})
+
+
+
+Class("Joose.Storage.Unpacker.jsonpickle", {
+ isa: Joose.Storage.Unpacker,
+ classMethods: {
+ unpack: function (data) {
+ var name = data.classname__;
+ if(!name) {
+ throw("Serialized data needs to include a classname__ attribute.")
+ }
+ var jsName = this.packedClassNameToJSClassName(name, data.classmodule__)
+
+ var co = this.meta.classNameToClassObject(jsName);
+
+ var obj = co.unpack(data);
+
+ var id;
+ if(Joose.Storage.CACHE && (id = data.objectid__)) {
+ Joose.Storage.CACHE[id] = obj
+ }
+
+ return obj
+ },
+
+ // Format My::Class::Name-0.01 We ignore the version
+ packedClassNameToJSClassName: function (className, moduleName) {
+ var name = "";
+ if(moduleName) {
+ name += moduleName + "."
+ }
+ name += className;
+ return name
+ },
+
+ jsonParseFilter: function (key, value) {
+ if(value != null && typeof value == "object") {
+ if(value.objectid__ && Joose.Storage.CACHE && Joose.Storage.CACHE[value.objectid__]) {
+ return Joose.Storage.CACHE[value.objectid__]
+ }
+ if(value.classname__) {
+ return Joose.Storage.Unpacker.jsonpickle.unpack(value)
+ }
+ }
+ return value
+ }
+ }
+})
+
+})(JooseClass);
+
+// ##########################
+// File: Joose/Decorator.js
+// ##########################
+(function (Class) {
+
+Class("Joose.Decorator", {
+ meta: Joose.Role,
+ methods: {
+ decorate: function (classObject, attributeName, optionalDelegatorFuncMaker) {
+ var me = this;
+ var methods = classObject.meta.getInstanceMethods();
+ Joose.A.each(methods, function (m) {
+ var name = m.getName();
+ var argName = attributeName;
+ // only override non existing methods
+ if(!me.can(name)) {
+
+ var func = function () {
+ var d = this[argName];
+ return d[name].apply(d, arguments)
+ }
+
+ if(optionalDelegatorFuncMaker) {
+ func = optionalDelegatorFuncMaker(name)
+ }
+
+ me.addMethod(name, func);
+ }
+ })
+ }
+ }
+})
+
+Joose.Decorator.meta.apply(Joose.Class)
+
+})(JooseClass);
+
+// ##########################
+// File: Joose/Module.js
+// ##########################
+
+/*
+Module("my.namespace", function () {
+ Class("Test", {
+
+ })
+})
+*/
+(function (Class) {
+
+// Joose.NameSpace is a pseudo class that makes namespace spots created by Joose.Module discoverable
+Joose.NameSpace = function () {}
+
+Class("Joose.Module", {
+ has: {
+ _name: {
+ is: "rw"
+ },
+ _elements: {
+ is: "rw"
+ },
+ _container: {
+ is: "rw"
+ }
+ },
+ classMethods: {
+ setup: function (name, functionThatCreatesClassesAndRoles) {
+ var me = this;
+ var parts = name.split(".");
+ var object = joose.top;
+ var soFar = []
+ var module;
+ for(var i = 0, len = parts.length; i < len; ++i) {
+ var part = parts[i];
+ if(part == "meta") {
+ throw "Module names may not include a part called 'meta'."
+ }
+ var cur = object[part];
+ soFar.push(part)
+ var subName = soFar.join(".")
+ if(typeof cur == "undefined") {
+ object[part] = new Joose.NameSpace();
+ module = new Joose.Module(subName)
+ module.setContainer(object[part])
+ object[part].meta = module
+ Joose.Module._allModules.push(object[part])
+
+ } else {
+ module = cur.meta;
+ if(
+ i === (len-1) && // only check on last iteration
+ !(module && module.meta && (module.meta.isa(Joose.Module)))) {
+ throw "Trying to setup module "+name+" failed. There is already something else: "+cur
+ }
+ }
+ object = object[part]
+ }
+ var before = joose.currentModule
+ joose.currentModule = module
+ if(functionThatCreatesClassesAndRoles) {
+ functionThatCreatesClassesAndRoles(object);
+ }
+ joose.currentModule = before;
+ return object
+ },
+
+ getAllModules: function () {
+ return this._allModules
+ }
+ },
+ methods: {
+ alias: function (destination) {
+ var me = this;
+
+ if(arguments.length == 0) {
+ return this
+ }
+
+ Joose.A.each(this.getElements(), function (thing) {
+ var global = me.globalName(thing.meta.className());
+
+ if(destination[global] === thing) { // already there
+ return
+ }
+ if(typeof destination[global] != "undefined") {
+ throw "There is already something else in the spot "+global
+ }
+
+ destination[global] = thing;
+ })
+ },
+
+ globalName: function (name) {
+ var moduleName = this.getName();
+ if(name.indexOf(moduleName) != 0) {
+ throw "All things inside me should have a name that starts with "+moduleName+". Name is "+name
+ }
+ var rest = name.substr(moduleName.length + 1); // + 1 to remove the trailing dot
+ if(rest.indexOf(".") != -1) {
+ throw "The things inside me should have no more dots in there name. Name is "+rest
+ }
+ return rest
+ },
+
+ removeGlobalSymbols: function () {
+ Joose.A.each(this.getElements(), function (thing) {
+ var global = this.globalName(thing.getName());
+ delete joose.top[global]
+ })
+ },
+
+ initialize: function (name) {
+ this.setElements([])
+ this.setName(name);
+ },
+
+ isEmpty: function () {
+ return this.getElements().length == 0
+ },
+
+ addElement: function (ele) {
+ if(!(ele || ele.meta)) {
+ throw "You may only add things that are Joose objects"
+ }
+ this._elements.push(ele)
+ },
+
+ getNames: function () {
+ var names = [];
+ Joose.A.each(this.getElements(), function (ele) { names.push(ele.meta.getName()) });
+ return names
+ }
+ }
+})
+})(JooseClass);
+
+__global__ = {};
+__global__.meta = new Joose.Module();
+__global__.meta.setName("__global__");
+__global__.meta.setContainer(__global__);
+
+Joose.Module._allModules = [__global__];
+
+JooseModule("__global__.nomodule", function () {})
+__global__.nomodule.meta._elements = joose.globalObjects;
+
+
+// ##########################
+// File: Joose/TypeChecker.js
+// ##########################
+(function (Class, Type) {
+
+Class("Joose.TypeChecker", {
+
+ classMethods: {
+ makeTypeChecker: function (isa, props, thing, name) {
+ if(!isa.meta) {
+ throw new Error("Isa declarations in attribute declarations must be Joose classes, roles or type constraints")
+ }
+
+ var isRole = false;
+ var isType = false;
+ // We need to check whether Joose.Role and Joose.TypeContraint
+ // are there yet, because they might not have been compiled yet
+ if(Joose.Role && isa.meta.meta.isa(Joose.Role)) {
+ isRole = true;
+ }
+ else if(Joose.TypeConstraint && isa.meta.isa(Joose.TypeConstraint)) {
+ isType = true;
+ }
+
+ // TODO possible Optimization: Create distinct closures based on the type
+
+ // If the isa refers to a class, then the new value must be an instance of that class.
+ // If the isa refers to a role, then the new value must implement that role.
+ // If the isa refers to a type constraint, then the value must match that type contraint
+ // ...and if the coerce property is set, we try to coerce the new value into the type
+ // Throws an exception if the new value does not match the isa property.
+ // If errorHandler is given, it will be executed in case of an error with parameters (Exception, isa-Contraint)
+ func = function doTypeCheck (value, errorHandler) {
+ try {
+ if ( props.nullable === true && value == undefined) {
+ // Don't do anything here :)
+ } else if ( isType ) {
+ var newvalue = null;
+ if( props.coerce ) {
+ newvalue = isa.coerce(value);
+ }
+ if ( newvalue == null && props.nullable !== true) {
+ isa.validate(value);
+ } else {
+ value = newvalue;
+ }
+ } else {
+ if(!value || !value.meta) {
+ throw new ReferenceError("The "+thing+" "+name+" only accepts values that have a meta object.")
+ }
+ var typeCheck = isRole ? value.meta.does(isa) : value.meta.isa(isa);
+ if( ! typeCheck ) {
+ throw new ReferenceError("The "+thing+" "+name+" only accepts values that are objects of type "+isa.meta.className()+".")
+ }
+ }
+ } catch (e) {
+ if(errorHandler) {
+ errorHandler.call(this, e, isa)
+ } else {
+ throw e
+ }
+ };
+ return value
+ }
+
+ return func
+ }
+ }
+})
+
+})(JooseClass, JooseType);
+
+// ##########################
+// File: Joose/TypeConstraint.js
+// ##########################
+(function (Class) {
+
+Class("Joose.TypeConstraint", {
+ has: {
+ _constraints: {
+ is: "ro",
+ init: function () { return [] }
+ },
+ _coercions: {
+ is: "ro",
+ init: function () { return [] }
+ },
+ _messages: {
+ is: "ro",
+ init: function () { return [] }
+ },
+ _callback: {
+ is: "ro",
+ init: function() {
+ return function (msg) {
+ throw new ReferenceError(msg);
+ };
+ }
+ },
+ _name: {
+ is: "ro"
+ },
+ _uses: {
+ is: "ro"
+ },
+ props: {
+ is: "rw"
+ }
+ },
+
+ classMethods: {
+ // name is name of type
+ // props may include: uses (Supertype), where (func) and coerce
+ newFromTypeBuilder: function (name, props) {
+ var t = new Joose.TypeConstraint({ name: name });
+ if ( props.uses
+ && typeof props.uses.meta != 'undefined'
+ && props.uses.meta.isa(Joose.TypeConstraint) ) {
+ t._uses = props.uses;
+ }
+
+ if(props.where) {
+ t.addConstraint(props.where, props.message)
+ }
+
+ t.setProps(props)
+
+ // coerce needs props from (Type) and via (func that takes current value as para and returns coerced value)
+ if(props.coerce) {
+ for(var i = 0; i < props.coerce.length; i++) {
+ var coercionProps = props.coerce[i];
+ t.addCoercion(new Joose.TypeCoercion({
+ from: coercionProps.from,
+ via: coercionProps.via
+ }))
+ }
+ }
+
+ return t
+ }
+ },
+
+ methods: {
+
+ stringify: function () {
+ return this._name
+ },
+
+ makeSubType: function (name) {
+ var t = new Joose.TypeConstraint({ name: name })
+ Joose.A.each(this._constraints, function (con) {
+ t.addConstraint(con)
+ })
+ return t
+ },
+
+ addCoercion: function (coercion) {
+ this._coercions.push(coercion);
+ },
+
+ addConstraint: function (func, message) {
+ this._constraints.push(func);
+ this._messages.push(message)
+ },
+
+ getConstraintList: function () {
+ var cons = this._constraints;
+ if ( this._uses ) {
+ var parentcons = this._uses.getConstraintList();
+ return parentcons.concat(cons);
+ }
+ return cons;
+ },
+
+ getMessageList: function () {
+ var msg = this._messages;
+ if ( this._uses ) {
+ var parentmsg = this._uses.getMessageList();
+ return parentmsg.concat(msg);
+ }
+ return msg;
+ },
+
+ validateBool: function (value) {
+ var i = this._validate(value);
+ if(i == -1) {
+ return true
+ }
+ return false
+ },
+
+ validate: function (value) {
+ var i = this._validate(value);
+ if(i == -1) {
+ return true
+ }
+ var messages = this.getMessageList();
+ var message = messages[i]
+ ? messages[i].call(this,value)
+ : "The passed value ["+value+"] is not a "+this;
+ this._callback(message);
+ },
+
+ _validate: function (value) {
+ var con = this.getConstraintList();
+ var i, len;
+ for(i = 0, len = con.length; i < len; i++) {
+ var func = con[i];
+ var result = false;
+ if(func instanceof RegExp) {
+ result = func.test(value)
+ } else {
+ result = func.call(this, value)
+ }
+
+ if(!result) {
+ return i
+
+ }
+ }
+ return -1
+ },
+
+ coerce: function (value) {
+ if(this.validateBool(value)) {
+ return value
+ }
+ //alert("in constraints coerce call: "+value);
+ var coercions = this._coercions;
+ for(var i = 0, len = coercions.length; i < len; i++) {
+ var coercion = coercions[i];
+ var result = coercion.coerce(value);
+ if(result !== null) {
+ return result
+ }
+ }
+ return null
+ }
+ }
+});
+
+})(JooseClass);
+
+// ##########################
+// File: Joose/TypeCoercion.js
+// ##########################
+(function (Class, Type) {
+
+//TODO this is a hack to fix the conflict between
+//type constraints and isa object constraints. It
+//probably needs more elegant solution.
+Type('CoercionFrom', {
+ where: function(o) {
+ if ( o.meta && o.meta.isa(Joose.TypeConstraint) ) {
+ return true;
+ }
+ return false;
+ }
+});
+
+Class("Joose.TypeCoercion", {
+ has: {
+ _from: {
+ isa: TYPE.CoercionFrom,
+ is: "rw"
+ },
+ _via: {
+ is: "rw"
+ }
+ },
+
+ methods: {
+ coerce: function (value) {
+ if(this._from.validateBool(value)) {
+ return this._via(value)
+ }
+ return null
+ }
+ }
+})
+
+})(JooseClass, JooseType);
+
+// ##########################
+// File: Joose/Types.js
+// ##########################
+(function (Type) {
+ Type('Any', {
+ // Returns true for any type
+ where: function(o) {
+ return true
+ }
+ });
+
+
+ Type('Null', {
+ uses: Joose.Type.Any,
+ where: function(o) {
+ if (o === null) {
+ return true;
+ }
+ return false;
+ }
+ });
+
+ Type('NotNull', {
+ uses: Joose.Type.Any,
+ where: function(o) {
+ if (o === null) {
+ return false;
+ }
+ return true;
+ }
+ });
+
+ Type('Enum', {
+ uses: Joose.Type.NotNull,
+ message: function(v) {
+ return "The passed value ["+v+"] is not "+
+ (this.getProps().strictMatch?"*strictly* ":"")+
+ "one of ["+this.getProps().values.join(",")+"]";
+ },
+ where: function (v) {
+ var self = this;
+ var props = self.getProps()
+ if ( !props || props.values === undefined || !(props.values instanceof Array)) {
+ throw "Enum Type needs Array of values in 'values' property of Type declaration"
+ }
+ var eq = function(vv) {
+ if (props.strictMatch === true) return (vv === v);
+ return (vv == v);
+ }
+ if ( Joose.A.grep(props.values, eq).length != 0 ) {
+ return true;
+ }
+ return false;
+ }
+ });
+
+ Type('Obj', {
+ uses: Joose.Type.NotNull,
+ where: function (o) {
+ if ( o instanceof Object ) {
+ return true;
+ }
+ return false;
+ }
+ });
+
+ Type('Str', {
+ uses: Joose.Type.NotNull,
+ where: function(S) {
+ if ( typeof S == 'string' || S instanceof String ) {
+ return true;
+ }
+ return false
+ },
+ coerce: [{
+ from: Joose.Type.Any,
+ via: function (value) {
+ if(value == null) {
+ return ""
+ } else {
+ return "" + value
+ }
+ }
+ }]
+ });
+
+ Type('Num', {
+ uses: Joose.Type.NotNull,
+ where: function(N) {
+ if ( typeof N == 'number' || N instanceof Number ) {
+ return true;
+ }
+ return false
+ },
+ coerce: [{
+ from: Joose.Type.Str,
+ via: function (value) {
+ if(value == null || value == "") return undefined;
+ // TODO parse for valid format
+ return parseFloat(value, 10)
+ }
+ }]
+ });
+
+ Type('Bool', {
+ uses: Joose.Type.NotNull,
+ where: function(B) {
+ if (B === true || B === false) {
+ return true;
+ }
+ return false;
+ },
+ coerce: [{
+ from: Joose.Type.Any,
+ via: function (value) {
+ if(value == null || value === "") return false;
+ if(value == 1 || value == "1" || value == "true") {
+ return true
+ }
+ if(value == 0 || value == "0" || value == "false" ) {
+ return false
+ }
+ return null
+ }
+ }]
+ });
+
+ Type('Int', {
+ uses: Joose.Type.Num,
+ where: function(n) {
+ var sn = String(n);
+ if ( sn.match(/^\d*\.\d$/) ) {
+ return false;
+ }
+ return true;
+ },
+ coerce: [{
+ from: Joose.Type.Str,
+ via: function (value) {
+ if(value == null || value == "") return undefined;
+ if(value.match(/^-{0,1}\d+$/)) {
+ return parseInt(value, 10)
+ }
+ return
+ }
+ }]
+ });
+
+ //TODO(jwall): Float is starting to look superfluous Floats are a superset of Int
+ //and javascript has no good way to differentiate between Num and Float
+ //It's only benefit is semantic sugar. Joose.Type.Float = Joose.Type.Num?
+ Type('Float', {
+ uses: Joose.Type.Num,
+ where: function(n) {
+ return true
+ }
+ });
+
+ Type('Func', {
+ uses: Joose.Type.Obj,
+ where: function (f) {
+ if ( typeof f == 'function' ) {
+ return true;
+ }
+ return false;
+ }
+ });
+
+ Type('Array', {
+ uses: Joose.Type.Obj,
+ where: function (A) {
+ if ( Object.prototype.toString.call(A) === '[object Array]' ) {
+ return true;
+ }
+ return false;
+ }
+ });
+
+ Type('Date', {
+ uses: Joose.Type.Obj,
+ where: function (D) {
+ if ( D instanceof Date ) {
+ return true;
+ }
+ return false;
+ },
+ coerce: [{
+ from: Joose.Type.Str,
+ via: function (value) {
+ var match;
+ if(value == undefined || value == "") {
+ return undefined;
+ } else if(match = value.match(/\s*(\d+)-(\d+)-(\d+)/)) {
+ return new Date(match[1], match[2]-1, [match[3]])
+ }
+ return null
+ }
+ }]
+ });
+
+ Type('Joose', {
+ uses: Joose.Type.Obj,
+ where: function (o) {
+ //TODO not sure if this is correct yet.
+ if ( o.meta && o.meta.meta.isa(Joose.Class) ) {
+ return true;
+ }
+ return false;
+ }
+ });
+})(JooseType);
+
+// ##########################
+// File: Joose/Prototype.js
+// ##########################
+
+(function (Class) {
+
+Class("Joose.Prototype", {
+ isa: Joose.Class,
+ override: {
+ initializer: function () {
+ var init = this.SUPER()
+ return function () {
+ init.apply(this, arguments)
+ var meta = this.meta;
+ this.meta = new Joose.PrototypeLazyMetaObjectProxy();
+ this.meta.metaObject = meta
+ this.meta.object = this;
+ }
+ }
+ }
+})
+
+
+Class("Joose.PrototypeLazyMetaObjectProxy", {
+ has: {
+ metaObject: {
+ is: "rw",
+ isa: Joose.Class,
+ handles: "*",
+ handleWith: function (name) {
+ return function () {
+ // when we are called, turn the objects meta object into the original, detach yourself
+ // and call the original methods
+ var o = this.object;
+ o.meta = this.metaObject;
+ o.detach()
+ o.meta[name].apply(o.meta, arguments)
+ }
+ }
+ },
+ object: {
+ is: "rw"
+ }
+ }
+})
+
+Joose.bootstrap3()
+
+})(JooseClass);
+
+// ##########################
+// File: Joose/TypedMethod.js
+// ##########################
+(function (Class, Type) {
+
+Class("Joose.TypedMethod", {
+ isa: Joose.Method,
+
+ has: {
+ types: {
+ isa: Joose.Type.Array,
+ is: "rw",
+ init: function () { return [] }
+ },
+
+ typeCheckers: {
+ init: function () { return [] }
+ }
+ },
+
+ after: {
+ setTypes: function () {
+ var self = this;
+ var typeCheckers = [];
+ var props = this.getProps();
+
+ Joose.A.each(this.getTypes(), function (type, index) {
+ if(type === null) {
+ // if there is no type in a spot, dont push a type checker
+ typeCheckers.push(null)
+ } else {
+ typeCheckers.push(Joose.TypeChecker.makeTypeChecker(type, props, "parameter", index))
+ }
+ })
+
+ this.typeCheckers = typeCheckers
+ }
+ },
+
+ override: {
+ copy: function () {
+ var self = this.SUPER();
+ // copy types;
+ var copy = [].concat(this.types)
+ self.setTypes( copy );
+ return self;
+ }
+ },
+
+ methods: {
+
+ wrapTypeChecker: function(body) {
+ var self = this;
+ return function typeCheckWrapper () {
+ var checkers = self.typeCheckers;
+ var args = [];
+ // iterate over type checkers and arguments
+ for(var i = 0, len = checkers.length; i < len; ++i) {
+ var checker = checkers[i]
+ if(checker !== null) {
+ var argument = arguments[i]
+ args[i] = checker(argument)
+ }
+ // If the type checker is null, dont type check
+ else {
+ args[i] = arguments[i]
+ }
+ }
+ return body.apply(this, args)
+ }
+ },
+
+ // Returns the function that will later be added to objects
+ asFunction: function () {
+ return this.wrapTypeChecker(this._body)
+ }
+ },
+
+ classMethods: {
+ newFromProps: function (name, props) {
+ var method = props.method;
+ if(typeof method !== "function") {
+ throw new Error("Property method in method declaration ["+name+"] must be a function.")
+ }
+ var self = this.meta.instantiate(name, method, props);
+ self.setTypes(props.signature);
+ return self;
+ }
+ }
+
+})
+
+})(JooseClass, JooseType);
+
+// ##########################
+// File: Joose/MultiMethod.js
+// ##########################
+Module('Joose.Type', function() {
+ Type('MethodPatternList', {
+ uses: Joose.Type.Array,
+ where: function(p) {
+ var ok = 0;
+ for (var i in p) {
+ var pattern = p[i];
+ if (pattern.signature instanceof Array
+ && typeof pattern.method == 'function') {
+ ok++;
+ }
+ }
+ return p.length == ok;
+ }
+ });
+});
+
+Class('Joose.MultiMethod', {
+ isa: Joose.Method,
+
+ has: {
+ patterns: {
+ is: 'rw',
+ isa: Joose.Type.MethodPatternList,
+ init: function() { return [] }
+ }
+ },
+
+ override: {
+ copy: function() {
+ var self = this.SUPER();
+ var patternCopy = [].concat(this.getPatterns());
+ self.setPatterns( patternCopy );
+ return self;
+ }
+ },
+
+ methods: {
+ // return the correct signature for
+ // our argument list or a function that
+ // will throw an error
+ getFunForSignature: function() {
+ var args = arguments;
+ var self = this;
+ var patterns = self.getPatterns();
+ for (var item in patterns) {
+ if(patterns.hasOwnProperty(item)) {
+ var method = patterns[item];
+ var sig = method.signature;
+ var matches = 0;
+ if (sig.length == args.length) {
+ if (sig.length > 0) {
+ for (var i=0; i < sig.length; i++) {
+ if (sig[i] instanceof Joose.TypeConstraint
+ && sig[i].validateBool(args[i])) {
+ matches++;
+ } else if (sig[i] instanceof Object
+ && args[i] instanceof sig[i]) {
+ matches++;
+ } else if (args[i] == sig[i]) {
+ matches++;
+ }
+ }
+ }
+ if (matches == sig.length)
+ return method.method;
+ }
+ }
+ }
+ return function () {
+ throw new ReferenceError("multi-method type method call "
+ +"with no matching signature");
+ };
+ },
+ // returns a closure that will always dispatch on the correct method
+ // for our signature but can be attached to an object as a method
+ asFunction: function() {
+ var self = this;
+ //TODO(jwall): perform caching of method returns?
+ return function() {
+ var myself = this;
+ var args = arguments;
+ var fun = self.getFunForSignature.apply(self, args);
+ return fun.apply(myself, args);
+ }
+ }
+ },
+ classMethods: {
+ newFromPatterns: function(name, patterns) {
+ method = new Joose.MultiMethod(name, function() {}, {});
+ method.setPatterns(patterns);
+ return method;
+ }
+ }
+});
diff --git a/share/web/static/js/rulebuilder.js b/share/web/static/js/rulebuilder.js
index 6d4391e..d05c2b3 100644
--- a/share/web/static/js/rulebuilder.js
+++ b/share/web/static/js/rulebuilder.js
@@ -113,11 +113,11 @@ RuleBuilder.prototype.init = function () {
jQuery._div_({'class': 'context top-context'})
.appendTo(this.panel);
- this.top_context = new RuleBuilder.Context(
- 'Bool',
- jQuery(".top-context").get(0),
- null,
- this
+ this.top_context = new RuleBuilder2.Context(
+ { expected_type: 'Bool',
+ element: jQuery(".top-context").get(0),
+ parent: null,
+ rb: this }
);
ebuilder.append('<div class="library">');
@@ -329,319 +329,336 @@ RuleBuilder.prototype.filter_expression_type = function (type) {
jQuery(this.sel+' .expression:not(.ret_'+e_sel(type)+')').hide();
};
-
-RuleBuilder.Context = function(expected_type, element, parent, rb) {
- this.expected_type = expected_type;
- this.element = element;
- this.parent = parent;
- this.rb = rb;
-
- var that = this;
- jQuery(this.element).click(function(e) { rb.focus(that); return false });
- jQuery._span_({ 'class': 'return-type'})
- .text(expected_type)
- .appendTo(this.element);
-
- if (expected_type == 'Str' || expected_type == 'Num') { // self-evaluating
- jQuery._span_({ 'class': 'enter-value' })
- .text("Enter a value")
- .click(function(e) {
- jQuery(this).html('').unbind('click');
- jQuery._input_({ 'type': 'text', class: 'enter-value'})
- .change(function() { that.update_return_type(that.return_type_from_val(this.value)) } )
- .appendTo(this).trigger('focus');
- that.self_eval = true;
- return true;
- })
- .appendTo(this.element);
- }
-
- var matched = /^ArrayRef\[(.*)\]$/.exec(expected_type);
- if (matched) {
- this.inner_type = matched[1];
- this.children = [];
- var builder = jQuery._div({'class': 'arraybuilder'})
- ._div_({'class': 'array-item-container'})
- .div_()
- .appendTo(this.element)
- .hide();
- jQuery._span_({'class': 'arraybuilder-icon'})
- .text("Array builder")
- .appendTo(this.element)
- .click(function(e) {
- that.arraybuilder = builder;
- var child = that.mk_array_item_context(that.inner_type,
- jQuery('div.array-item-container', builder), 0);
- that.children.push(child);
- builder.show();
- jQuery(this).hide();
- that.rb.focus(child);
- return false;
- });
- }
-};
-
-RuleBuilder.Context.prototype.array_item_idx = function()
-{
- for (var i in this.parent.children) {
- if (this.parent.children[i] == this)
- return parseInt(i);
- }
- return -1;
-}
-
-RuleBuilder.Context.prototype.mk_array_item_context = function(type, container, insert_after) {
- var li = jQuery._div_({'class': 'array-item'});
- if (insert_after)
- li.insertAfter(jQuery(insert_after).parent(".array-item"));
- else
- li.appendTo(container);
- var x = jQuery._div_({'class': 'context'})
- .appendTo(li);
- var child = new RuleBuilder.Context(type, x.get(0), this, this.rb);
- jQuery._span_({'class': 'add-icon'})
- .text("+")
- .appendTo(li)
- .click(function(e) {
- var that = child.parent;
- var idx = child.array_item_idx()+1;
- var newchild = that.mk_array_item_context(that.inner_type,
- jQuery('div.array-item-container', that.arraybuilder), child.element);
- that.children.splice(idx, 0, newchild);
- });
- jQuery._span_({'class': 'delete-icon'})
- .text("-")
- .appendTo(li)
- .click(function(e) {
- var that = child.parent;
- var idx = child.array_item_idx();
- jQuery(child.element).parent('.array-item').remove();
- that.children.splice(idx, 1);
- });
-
- return child;
-};
-
-RuleBuilder.Context.prototype.return_type_from_val = function(val) {
- // XXX
- return 'Str';
-}
-
-RuleBuilder.Context.prototype.transform = function(func_name) {
- var rb = this.rb;
- var func = rb.functions[func_name];
- var new_element = jQuery._div_({'class': 'context'});
- var parent = new RuleBuilder.Context(this.expected_type,
- new_element.get(0), this.parent, rb);
- if (this.parent) {
- new_element.insertAfter(this.element);
- jQuery(this.element).remove();
-
- this.parent.children[this.array_item_idx()] = parent;
- }
- else {
- jQuery(rb.top_context.element).removeClass('top-context').remove();
- new_element.addClass('top-context').appendTo(rb.panel);
-
- rb.top_context = parent;
- }
-
- this.parent = parent;
-
- parent.set_application(func_name, func);
-
- jQuery(this.element).unbind('click');
- var first_param = parent.children[0];
- var second_param = parent.children.length > 1 ? parent.children[1] : null;
-
- if (first_param.inner_type) {
- // Bool "or" (......) <- parent
- // |--- ArrayRef[Bool] (parent) <- first_param
- // |- first_param <-
- // |- second_param <-
- parent = first_param;
- var builder = jQuery('div.arraybuilder', parent.element).show();
- jQuery("span.arraybuilder-icon", parent.element).hide();
- parent.arraybuilder = builder;
- var container = jQuery('div.array-item-container', builder);
- first_param = parent.mk_array_item_context(parent.inner_type,
- container, null);
- second_param = parent.mk_array_item_context(parent.inner_type,
- container, first_param.element);
- parent.children = [ first_param, second_param ];
- }
-
- parent.children[0] = this;
- this.parent = parent;
- this.expected_type = first_param.expected_type;
-
- jQuery('span.return-type:first', this.element)
- .text(first_param.expected_type);
- jQuery(first_param.element).replaceWith(this.element);
- first_param.element = this.element;
- var that = this;
- jQuery(this.element).click(function(e) { rb.focus(that); return false });
- this.update_return_type(this.return_type);
- if (second_param)
- this.rb.focus(second_param);
-}
-
-RuleBuilder.Context.prototype.transformMenu = function(el) {
- // this.return_type -> this.expected_type
- var that = this;
- var options = {
- onClick: function(e,item) {
- jQuery.Menu.closeAll();
- that.transform(item.src);
- return false;
+Module("RuleBuilder2", function(m) {
+ Class("Context", {
+ has: {
+ expected_type: { is: "rw" },
+ element: { is: "rw" },
+ parent: { is: "rw" },
+ rb: { is: "rw" }
},
- minWidth: 120,
- arrowSrc: '/images/arrow_right.gif',
- hoverOpenDelay: 500,
- hideDelay: 500 };
-
- jQuery.get('/rulebuilder/getfunctions.json',
- { parameters: [ this.return_type ],
- return_type: this.expected_type },
- function(response, status) {
- var entries = [];
- for (var name in response) {
- entries.push(name);
- }
+ after: { initialize: function() {
+ var expected_type = this.expected_type;
+ var rb = this.rb;
+
+ var that = this;
+ jQuery(this.element).click(function(e) { rb.focus(that); return false });
+ jQuery._span_({ 'class': 'return-type'})
+ .text(expected_type)
+ .appendTo(this.element);
+
+ if (expected_type == 'Str' || expected_type == 'Num') { // self-evaluating
+ jQuery._span_({ 'class': 'enter-value' })
+ .text("Enter a value")
+ .click(function(e) {
+ jQuery(this).html('').unbind('click');
+ jQuery._input_({ 'type': 'text', class: 'enter-value'})
+ .change(function() { that.update_return_type(that.return_type_from_val(this.value)) } )
+ .appendTo(this).trigger('focus');
+ that.self_eval = true;
+ return true;
+ })
+ .appendTo(this.element);
+ }
- jQuery(el)
- .menu(options,
- jQuery.map(entries,
- function(val) {
- return {src: val, data: { } }}
- ));
+ var matched = /^ArrayRef\[(.*)\]$/.exec(expected_type);
+ if (matched) {
+ this.inner_type = matched[1];
+ this.children = [];
+ var builder = jQuery._div({'class': 'arraybuilder'})
+ ._div_({'class': 'array-item-container'})
+ .div_()
+ .appendTo(this.element)
+ .hide();
+ jQuery._span_({'class': 'arraybuilder-icon'})
+ .text("Array builder")
+ .appendTo(this.element)
+ .click(function(e) {
+ that.arraybuilder = builder;
+ var child = that.mk_array_item_context(that.inner_type,
+ jQuery('div.array-item-container', builder), 0);
+ that.children.push(child);
+ builder.show();
+ jQuery(this).hide();
+ that.rb.focus(child);
+ return false;
+ });
+ }
+ }
},
- 'json');
+ methods: {
+ array_item_idx: function() {
+ for (var i in this.parent.children) {
+ if (this.parent.children[i] == this)
+ return parseInt(i);
+ }
+ return -1;
+ },
-}
+ mk_array_item_context: function(type, container, insert_after) {
+ var li = jQuery._div_({'class': 'array-item'});
+ if (insert_after)
+ li.insertAfter(jQuery(insert_after).parent(".array-item"));
+ else
+ li.appendTo(container);
+ var x = jQuery._div_({'class': 'context'})
+ .appendTo(li);
+ var child = new RuleBuilder2.Context({ expected_type: type,
+ element: x.get(0),
+ parent: this,
+ rb: this.rb });
+ jQuery._span_({'class': 'add-icon'})
+ .text("+")
+ .appendTo(li)
+ .click(function(e) {
+ var that = child.parent;
+ var idx = child.array_item_idx()+1;
+ var newchild = that.mk_array_item_context(that.inner_type,
+ jQuery('div.array-item-container', that.arraybuilder), child.element);
+ that.children.splice(idx, 0, newchild);
+ });
+ jQuery._span_({'class': 'delete-icon'})
+ .text("-")
+ .appendTo(li)
+ .click(function(e) {
+ var that = child.parent;
+ var idx = child.array_item_idx();
+ jQuery(child.element).parent('.array-item').remove();
+ that.children.splice(idx, 1);
+ });
+
+ return child;
+ },
+
+ return_type_from_val: function(val) {
+ // XXX
+ return 'Str';
+ },
+
+ transform: function(func_name) {
+ var rb = this.rb;
+ var func = rb.functions[func_name];
+ var new_element = jQuery._div_({'class': 'context'});
+ var parent = new RuleBuilder2.Context({ expected_type: this.expected_type,
+ element: new_element.get(0),
+ parent: this.parent,
+ rb: rb });
+
+ if (this.parent) {
+ new_element.insertAfter(this.element);
+ jQuery(this.element).remove();
+
+ this.parent.children[this.array_item_idx()] = parent;
+ }
+ else {
+ jQuery(rb.top_context.element).removeClass('top-context').remove();
+ new_element.addClass('top-context').appendTo(rb.panel);
-RuleBuilder.Context.prototype.state = function() {
- if( this.self_eval ) {
- }
- else if ( this.expression ) {
- }
- else if ( this.func_name ) {
- var type_complete = false;
- for (var i in this.children) {
- var child = this.children[i];
- var state = child.state();
- if (state == 'pending')
- return 'pending';
- if (state == 'type-complete')
- type_complete = true;
- }
- if (!type_complete)
- return "complete";
- }
- else {
- return 'pending';
- }
+ rb.top_context = parent;
+ }
- var el = jQuery("span.return-type", this.element);
- return el.hasClass('matched') ? 'type-complete' : 'complete';
-}
+ this.parent = parent;
+
+ parent.set_application(func_name, func);
+
+ jQuery(this.element).unbind('click');
+ var first_param = parent.children[0];
+ var second_param = parent.children.length > 1 ? parent.children[1] : null;
+
+ if (first_param.inner_type) {
+ // Bool "or" (......) <- parent
+ // |--- ArrayRef[Bool] (parent) <- first_param
+ // |- first_param <-
+ // |- second_param <-
+ parent = first_param;
+ var builder = jQuery('div.arraybuilder', parent.element).show();
+ jQuery("span.arraybuilder-icon", parent.element).hide();
+ parent.arraybuilder = builder;
+ var container = jQuery('div.array-item-container', builder);
+ first_param = parent.mk_array_item_context(parent.inner_type,
+ container, null);
+ second_param = parent.mk_array_item_context(parent.inner_type,
+ container, first_param.element);
+ parent.children = [ first_param, second_param ];
+ }
-RuleBuilder.Context.prototype.update_return_type = function(type) {
- // XXX: this should query the server for 'is-a-type-of'
- this.return_type = type;
- var el = jQuery("span.return-type", this.element);
- if (this.expected_type == type) {
- el.removeClass("unmatched").addClass("matched");
- }
- else {
- el.removeClass("matched").addClass("unmatched");
- }
- this.transformMenu(el);
-}
+ parent.children[0] = this;
+ this.parent = parent;
+ this.expected_type = first_param.expected_type;
+
+ jQuery('span.return-type:first', this.element)
+ .text(first_param.expected_type);
+ jQuery(first_param.element).replaceWith(this.element);
+ first_param.element = this.element;
+ var that = this;
+ jQuery(this.element).click(function(e) { rb.focus(that); return false });
+ this.update_return_type(this.return_type);
+ if (second_param)
+ this.rb.focus(second_param);
+ },
+
+ transformMenu: function(el) {
+ // this.return_type -> this.expected_type
+ var that = this;
+ var options = {
+ onClick: function(e,item) {
+ jQuery.Menu.closeAll();
+ that.transform(item.src);
+ return false;
+ },
+ minWidth: 120,
+ arrowSrc: '/images/arrow_right.gif',
+ hoverOpenDelay: 500,
+ hideDelay: 500 };
+
+ jQuery.get('/rulebuilder/getfunctions.json',
+ { parameters: [ this.return_type ],
+ return_type: this.expected_type },
+ function(response, status) {
+ var entries = [];
+ for (var name in response) {
+ entries.push(name);
+ }
+
+ jQuery(el)
+ .menu(options,
+ jQuery.map(entries,
+ function(val) {
+ return {src: val, data: { } }}
+ ));
+ },
+ 'json');
+
+ },
+
+ state: function() {
+ if( this.self_eval ) {
+ }
+ else if ( this.expression ) {
+ }
+ else if ( this.func_name ) {
+ var type_complete = false;
+ for (var i in this.children) {
+ var child = this.children[i];
+ var state = child.state();
+ if (state == 'pending')
+ return 'pending';
+ if (state == 'type-complete')
+ type_complete = true;
+ }
+ if (!type_complete)
+ return "complete";
+ }
+ else {
+ return 'pending';
+ }
-RuleBuilder.Context.prototype.clear = function() {
- jQuery('div.application', this.element).remove();
- jQuery('span.expression', this.element).remove();
- jQuery('span.transform', this.element).hide();
- jQuery('span.enter-value', this.element).hide();
- this.self_eval = false;
- this.expression = null;
- this.func_name = null;
-}
+ var el = jQuery("span.return-type", this.element);
+ return el.hasClass('matched') ? 'type-complete' : 'complete';
+ },
-RuleBuilder.Context.prototype.traverse = function(fn) {
- fn(this);
- if ( this.func_name ) {
- jQuery.each(this.children, function(idx, val) { fn(this) } );
- }
-}
+ update_return_type: function(type) {
+ // XXX: this should query the server for 'is-a-type-of'
+ this.return_type = type;
+ var el = jQuery("span.return-type", this.element);
+ if (this.expected_type == type) {
+ el.removeClass("unmatched").addClass("matched");
+ }
+ else {
+ el.removeClass("matched").addClass("unmatched");
+ }
+ this.transformMenu(el);
+ },
+
+ clear: function() {
+ jQuery('div.application', this.element).remove();
+ jQuery('span.expression', this.element).remove();
+ jQuery('span.transform', this.element).hide();
+ jQuery('span.enter-value', this.element).hide();
+ this.self_eval = false;
+ this.expression = null;
+ this.func_name = null;
+ },
+
+ traverse: function(fn) {
+ fn(this);
+ if ( this.func_name ) {
+ jQuery.each(this.children, function(idx, val) { fn(this) } );
+ }
+ },
-RuleBuilder.Context.prototype.serialize = function() {
- if( this.self_eval ) {
- var val = jQuery('input.enter-value', this.element).val();
- if (this.expected_type == 'Str') {
- return '"'+val+'"';
- }
- else {
- return val;
+ serialize: function() {
+ if( this.self_eval ) {
+ var val = jQuery('input.enter-value', this.element).val();
+ if (this.expected_type == 'Str') {
+ return '"'+val+'"';
+ }
+ else {
+ return val;
+ }
+ }
+ else if ( this.expression ) {
+ return this.expression;
+ }
+ else if ( this.func_name ) {
+ var args = jQuery.map(this.children, function(val) { return val.serialize() });
+ args.unshift(this.func_name);
+ return '('+args.join(' ')+')';
+ }
+ else if ( this.arraybuilder ) {
+ var args = jQuery.map(this.children, function(val) { return val.serialize() });
+ return args.join(' ');
+ }
+ },
+
+ set_expression: function(expression) {
+ this.clear();
+ this.expression = expression.expression;
+ this.update_return_type(expression.type);
+ jQuery('span.transform', this.element).show();
+
+ jQuery._span_({ 'class': 'expression'})
+ .text(this.expression)
+ .appendTo(this.element);
+ },
+
+ set_application: function(func_name, func) {
+ this.clear();
+ this.func_name = func_name;
+ this.children = [];
+ this.update_return_type(func.return_type);
+ jQuery('span.transform', this.element).show();
+ jQuery._div({'class': 'application'})
+ ._div_({'class': 'application-function function'})
+ ._div_({'class': 'application-params signature'})
+ .div_()
+ .appendTo(this.element);
+
+ jQuery('div.application-function',this.element).html(func_name);
+ jQuery('div.application', this.element).show();
+ jQuery('div.application-params', this.element).html('');
+ var params = jQuery('div.application-params', this.element);
+ var that = this;
+ jQuery.each(func.parameters,
+ function(idx, val) {
+ var x = jQuery._div_({'class': 'context'})
+ .appendTo(params);
+
+ var child = new RuleBuilder2.Context({ expected_type: val.type,
+ element: x.get(0),
+ parent: that,
+ rb: that.rb });
+
+ that.children.push(child);
+ });
+ if (this.children.length) {
+ this.rb.focus(this.children[0]);
+ }
+ }
}
- }
- else if ( this.expression ) {
- return this.expression;
- }
- else if ( this.func_name ) {
- var args = jQuery.map(this.children, function(val) { return val.serialize() });
- args.unshift(this.func_name);
- return '('+args.join(' ')+')';
- }
- else if ( this.arraybuilder ) {
- var args = jQuery.map(this.children, function(val) { return val.serialize() });
- return args.join(' ');
- }
-}
-
-RuleBuilder.Context.prototype.set_expression = function(expression) {
- this.clear();
- this.expression = expression.expression;
- this.update_return_type(expression.type);
- jQuery('span.transform', this.element).show();
-
- jQuery._span_({ 'class': 'expression'})
- .text(this.expression)
- .appendTo(this.element);
-}
-
-
-
-RuleBuilder.Context.prototype.set_application = function(func_name, func) {
- this.clear();
- this.func_name = func_name;
- this.children = [];
- this.update_return_type(func.return_type);
- jQuery('span.transform', this.element).show();
- jQuery._div({'class': 'application'})
- ._div_({'class': 'application-function function'})
- ._div_({'class': 'application-params signature'})
- .div_()
- .appendTo(this.element);
+ })
+});
- jQuery('div.application-function',this.element).html(func_name);
- jQuery('div.application', this.element).show();
- jQuery('div.application-params', this.element).html('');
- var params = jQuery('div.application-params', this.element);
- var that = this;
- jQuery.each(func.parameters,
- function(idx, val) {
- var x = jQuery._div_({'class': 'context'})
- .appendTo(params);
-
- var child = new RuleBuilder.Context(val.type, x.get(0), that, that.rb);
- that.children.push(child);
- });
- if (this.children.length) {
- this.rb.focus(this.children[0]);
- }
-};
jQuery.fn.sort = function() {
return this.pushStack( [].sort.apply( this, arguments ), []);
-----------------------------------------------------------------------
More information about the Rt-commit
mailing list