/*
 * Objective-J.js
 * Objective-J
 *
 * Created by Francisco Tolmasky.
 * Copyright 2008, 280 North, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

var NO = false,
    YES = true,
    nil = null,
    Nil = null,
    NULL = null,
    ABS = Math.abs,
    ASIN = Math.asin,
    ACOS = Math.acos,
    ATAN = Math.atan,
    ATAN2 = Math.atan2,
    SIN = Math.sin,
    COS = Math.cos,
    TAN = Math.tan,
    EXP = Math.exp,
    POW = Math.pow,
    CEIL = Math.ceil,
    FLOOR = Math.floor,
    ROUND = Math.round,
    MIN = Math.min,
    MAX = Math.max,
    RAND = Math.random,
    SQRT = Math.sqrt,
    E = Math.E,
    LN2 = Math.LN2,
    LN10 = Math.LN10,
    LOG2E = Math.LOG2E,
    LOG10E = Math.LOG10E,
    PI = Math.PI,
    PI2 = Math.PI * 2.0,
    PI_2 = Math.PI / 2.0,
    SQRT1_2 = Math.SQRT1_2,
    SQRT2 = Math.SQRT2;
var objj_continue_alerting = NO;
function objj_alert(aString)
{
    if (!objj_continue_alerting)
        return;
    objj_continue_alerting = confirm(aString + "\n\nClick cancel to prevent further alerts.");
}
function objj_fprintf(stream, string)
{
    stream(string);
}
function objj_printf(string)
{
    objj_fprintf(alert, string);
}
var CLS_CLASS = 0x1,
    CLS_META = 0x2,
    CLS_INITIALIZED = 0x4,
    CLS_INITIALIZING = 0x8;
function objj_ivar( aName, aType)
{
    this.name = aName;
    this.type = aType;
}
function objj_method( aName, anImplementation, types)
{
    this.name = aName;
    this.method_imp = anImplementation;
    this.types = types;
}
function objj_class()
{
    this.isa = NULL;
    this.super_class = NULL;
    this.sub_classes = [];
    this.name = NULL;
    this.info = 0;
    this.ivars = [];
    this.method_list = [];
    this.method_hash = {};
    this.method_store = function() { };
    this.method_dtable = this.method_store.prototype;
    this.allocator = function() { };
    this.__address = -1;
}
function objj_object()
{
    this.isa = NULL;
    this.__address = -1;
}
var OBJECT_COUNT = 0;
function _objj_generateObjectHash()
{
    return OBJECT_COUNT++;
}
function class_getName( aClass)
{
    if (aClass == Nil)
        return "";
    return aClass.name;
}
function class_isMetaClass( aClass)
{
    if (!aClass)
        return NO;
    return ((aClass.info & (CLS_META)));
}
function class_getSuperclass( aClass)
{
    if (aClass == Nil)
        return Nil;
    return aClass.super_class;
}
function class_setSuperclass( aClass, aSuperClass)
{
}
function class_isMetaClass( aClass)
{
    return ((aClass.info & (CLS_META)));
}
function class_addIvar( aClass, aName, aType)
{
    var thePrototype = aClass.allocator.prototype;
    if (typeof thePrototype[aName] != "undefined")
        return NO;
    aClass.ivars.push(new objj_ivar(aName, aType));
    thePrototype[aName] = NULL;
    return YES;
}
function class_addIvars( aClass, ivars)
{
    var index = 0,
        count = ivars.length,
        thePrototype = aClass.allocator.prototype;
    for (; index < count; ++index)
    {
        var ivar = ivars[index],
            name = ivar.name;
        if (typeof thePrototype[name] == "undefined")
        {
            aClass.ivars.push(ivar);
            thePrototype[name] = NULL;
        }
    }
}
function class_copyIvarList( aClass)
{
    return aClass.ivars.slice(0);
}
function class_addMethod( aClass, aName, anImplementation, aType)
{
    if (aClass.method_hash[aName])
        return NO;
    var method = new objj_method(aName, anImplementation, aType);
    aClass.method_list.push(method);
    aClass.method_dtable[aName] = method;
    if (!((aClass.info & (CLS_META))) && (((aClass.info & (CLS_META))) ? aClass : aClass.isa).isa == (((aClass.info & (CLS_META))) ? aClass : aClass.isa))
        class_addMethods((((aClass.info & (CLS_META))) ? aClass : aClass.isa), methods);
    return YES;
}
function class_addMethods( aClass, methods)
{
    var index = 0,
        count = methods.length,
        method_list = aClass.method_list,
        method_dtable = aClass.method_dtable;
    for (; index < count; ++index)
    {
        var method = methods[index];
        if (aClass.method_hash[method.name])
            continue;
        method_list.push(method);
        method_dtable[method.name] = method;
    }
    if (!((aClass.info & (CLS_META))) && (((aClass.info & (CLS_META))) ? aClass : aClass.isa).isa == (((aClass.info & (CLS_META))) ? aClass : aClass.isa))
        class_addMethods((((aClass.info & (CLS_META))) ? aClass : aClass.isa), methods);
}
function class_getInstanceMethod( aClass, aSelector)
{
    if (!aClass || !aSelector)
        return NULL;
    var method = aClass.method_dtable[aSelector];
    return method ? method : NULL;
}
function class_getClassMethod( aClass, aSelector)
{
    if (!aClass || !aSelector)
        return NULL;
    var method = (((aClass.info & (CLS_META))) ? aClass : aClass.isa).method_dtable[aSelector];
    return method ? method : NULL;
}
function class_copyMethodList( aClass)
{
    return aClass.method_list.slice(0);
}
var _class_initialize = function( aClass)
{
    var meta = (((aClass.info & (CLS_META))) ? aClass : aClass.isa);
    if ((aClass.info & (CLS_META)))
        aClass = objj_getClass(aClass.name);
    if (aClass.super_class && !((((aClass.super_class.info & (CLS_META))) ? aClass.super_class : aClass.super_class.isa).info & (CLS_INITIALIZED)))
        _class_initialize(aClass.super_class);
    if (!(meta.info & (CLS_INITIALIZED)) && !(meta.info & (CLS_INITIALIZING)))
    {
        meta.info = (meta.info | (CLS_INITIALIZING)) & ~(0);
        objj_msgSend(aClass, "initialize");
        meta.info = (meta.info | (CLS_INITIALIZED)) & ~(CLS_INITIALIZING);
    }
}
var _objj_forward = new objj_method("forward", function(self, _cmd)
{
    return objj_msgSend(self, "forward::", _cmd, arguments);
});
function class_getMethodImplementation( aClass, aSelector)
{
    if (!((((aClass.info & (CLS_META))) ? aClass : aClass.isa).info & (CLS_INITIALIZED))) _class_initialize(aClass); var method = aClass.method_dtable[aSelector]; if (!method) method = _objj_forward; var implementation = method.method_imp;;
    return implementation;
}
var GLOBAL_NAMESPACE = this,
    REGISTERED_CLASSES = {};
function objj_allocateClassPair( superclass, aName)
{
    var classObject = new objj_class(),
        metaClassObject = new objj_class(),
        rootClassObject = classObject;
    if (superclass)
    {
        rootClassObject = superclass;
        while (rootClassObject.superclass)
            rootClassObject = rootClassObject.superclass;
        classObject.allocator.prototype = new superclass.allocator;
        classObject.method_store.prototype = new superclass.method_store;
        classObject.method_dtable = classObject.method_store.prototype;
        metaClassObject.method_store.prototype = new superclass.isa.method_store;
        metaClassObject.method_dtable = metaClassObject.method_store.prototype;
        classObject.super_class = superclass;
        metaClassObject.super_class = superclass.isa;
    }
    else
        classObject.allocator.prototype = new objj_object();
    classObject.isa = metaClassObject;
    classObject.name = aName;
    classObject.info = CLS_CLASS;
    classObject.__address = (OBJECT_COUNT++);
    metaClassObject.isa = rootClassObject.isa;
    metaClassObject.name = aName;
    metaClassObject.info = CLS_META;
    metaClassObject.__address = (OBJECT_COUNT++);
    return classObject;
}
function objj_registerClassPair( aClass)
{
    GLOBAL_NAMESPACE[aClass.name] = aClass;
    REGISTERED_CLASSES[aClass.name] = aClass;
}
function class_createInstance( aClass)
{
    if (!aClass)
        objj_exception_throw(new objj_exception(OBJJNilClassException, "*** Attempting to create object with Nil class."));
    var object = new aClass.allocator;
    object.__address = (OBJECT_COUNT++);
    object.isa = aClass;
    return object;
}
var prototype_bug = function() { }
prototype_bug.prototype.member = false;
with (new prototype_bug())
    member = true;
if (new prototype_bug().member)
{
var fast_class_createInstance = class_createInstance;
class_createInstance = function( aClass)
{
    var object = fast_class_createInstance(aClass);
    if (object)
    {
        var theClass = object.isa,
            actualClass = theClass;
        while (theClass)
        {
            var ivars = theClass.ivars;
                count = ivars.length;
            while (count--)
                object[ivars[count].name] = NULL;
            theClass = theClass.super_class;
        }
        object.isa = actualClass;
    }
    return object;
}
}
function object_getClassName( anObject)
{
    if (!anObject)
        return "";
    var theClass = anObject.isa;
    return theClass ? class_getName(theClass) : "";
}
function objj_lookUpClass( aName)
{
    var theClass = REGISTERED_CLASSES[aName];
    return theClass ? theClass : Nil;
}
function objj_getClass( aName)
{
    var theClass = REGISTERED_CLASSES[aName];
    if (!theClass)
    {
    }
    return theClass ? theClass : Nil;
}
function objj_getMetaClass( aName)
{
    var theClass = objj_getClass(aName);
    return (((theClass.info & (CLS_META))) ? theClass : theClass.isa);
}
function ivar_getName(anIvar)
{
    return anIvar.name;
}
function ivar_getTypeEncoding(anIvar)
{
    return anIvar.type;
}
function objj_msgSend( aReceiver, aSelector)
{
    if (aReceiver == nil)
        return nil;
    if (!((((aReceiver.isa.info & (CLS_META))) ? aReceiver.isa : aReceiver.isa.isa).info & (CLS_INITIALIZED))) _class_initialize(aReceiver.isa); var method = aReceiver.isa.method_dtable[aSelector]; if (!method) method = _objj_forward; var implementation = method.method_imp;;
    return implementation.apply(aReceiver, arguments);
}
function objj_msgSendSuper( aSuper, aSelector)
{
    var super_class = aSuper.super_class;
    arguments[0] = aSuper.receiver;
    if (!((((super_class.info & (CLS_META))) ? super_class : super_class.isa).info & (CLS_INITIALIZED))) _class_initialize(super_class); var method = super_class.method_dtable[aSelector]; if (!method) method = _objj_forward; var implementation = method.method_imp;;
    return implementation.apply(aSuper.receiver, arguments);
}
function method_getName( aMethod)
{
    return aMethod.name;
}
function method_getImplementation( aMethod)
{
    return aMethod.method_imp;
}
function method_setImplementation( aMethod, anImplementation)
{
    var oldImplementation = aMethod.method_imp;
    aMethod.method_imp = anImplementation;
    return oldImplementation;
}
function method_exchangeImplementations( lhs, rhs)
{
    var lhs_imp = method_getImplementation(lhs),
        rhs_imp = method_getImplementation(rhs);
    method_setImplementation(lhs, rhs_imp);
    method_setImplementation(rhs, lhs_imp);
}
function sel_getName(aSelector)
{
    return aSelector ? aSelector : "<null selector>";
}
function sel_getUid( aName)
{
    return aName;
}
function sel_isEqual( lhs, rhs)
{
    return lhs == rhs;
}
function sel_registerName(aName)
{
    return aName;
}
function objj_dictionary()
{
    this._keys = [];
    this.count = 0;
    this._buckets = {};
    this.__address = (OBJECT_COUNT++);
}
function dictionary_containsKey(aDictionary, aKey)
{
    return aDictionary._buckets[aKey] != NULL;
}
function dictionary_getCount(aDictionary)
{
    return aDictionary.count;
}
function dictionary_getValue(aDictionary, aKey)
{
    return aDictionary._buckets[aKey];
}
function dictionary_setValue(aDictionary, aKey, aValue)
{
    if (aDictionary._buckets[aKey] == NULL)
    {
        aDictionary._keys.push(aKey);
        ++aDictionary.count;
    }
    if ((aDictionary._buckets[aKey] = aValue) == NULL)
        --aDictionary.count;
}
function dictionary_removeValue(aDictionary, aKey)
{
    if (aDictionary._buckets[aKey] == NULL)
        return;
    --aDictionary.count;
    if (aDictionary._keys.indexOf)
        aDictionary._keys.splice(aDictionary._keys.indexOf(aKey), 1);
    else
    {
        var keys = aDictionary._keys,
            index = 0,
            count = keys.length;
        for (; index < count; ++index)
            if (keys[index] == aKey)
            {
                keys.splice(index, 1);
                break;
            }
    }
    delete aDictionary._buckets[aKey];
}
function dictionary_replaceValue(aDictionary, aKey, aValue)
{
    if (aDictionary[aKey] == NULL)
        return;
}
function dictionary_description(aDictionary)
{
    str = "{ ";
    for ( x in aDictionary._buckets)
        str += x + ":" + aDictionary._buckets[x] + ",";
    str += " }";
    return str;
}
kCFPropertyListOpenStepFormat = 1;
kCFPropertyListXMLFormat_v1_0 = 100;
kCFPropertyListBinaryFormat_v1_0 = 200;
kCFPropertyList280NorthFormat_v1_0 = -1000;
OBJJPlistParseException = "OBJJPlistParseException";
var kCFPropertyList280NorthMagicNumber = "280NPLIST";
function objj_data()
{
    this.string = "";
    this._plistObject = NULL;
    this.bytes = NULL;
    this.base64 = NULL;
}
var objj_markedStream = function(aString)
{
    var index = aString.indexOf(';');
    this._magicNumber = aString.substr(0, index);
    this._location = aString.indexOf(';', ++index);
    this._version = aString.substring(index, this._location++);
    this._string = aString;
}
objj_markedStream.prototype.magicNumber = function()
{
    return this._magicNumber;
}
objj_markedStream.prototype.version = function()
{
    return this._version;
}
objj_markedStream.prototype.getMarker = function()
{
    var string = this._string,
        location = this._location;
    if (location >= string.length)
        return NULL;
    var next = string.indexOf(';', location);
    if (next < 0)
        return NULL;
    var marker = string.substring(location, next);
    this._location = next + 1;
    return marker;
}
objj_markedStream.prototype.getString = function()
{
    var string = this._string,
        location = this._location;
    if (location >= string.length)
        return NULL;
    var next = string.indexOf(';', location);
    if (next < 0)
        return NULL;
    var size = parseInt(string.substring(location, next)),
        text = string.substr(next + 1, size);
    this._location = next + 1 + size;
    return text;
}
function CPPropertyListCreateData(aPlistObject, aFormat)
{
    if (aFormat == kCFPropertyListXMLFormat_v1_0)
        return CPPropertyListCreateXMLData(aPlistObject);
    if (aFormat == kCFPropertyList280NorthFormat_v1_0)
        return CPPropertyListCreate280NorthData(aPlistObject);
    return NULL;
}
function CPPropertyListCreateFromData(aData, aFormat)
{
    if (!aFormat)
    {
        if (aData instanceof objj_data)
        {
            var string = aData.string ? aData.string : objj_msgSend(aData, "string");
            if (string.substr(0, kCFPropertyList280NorthMagicNumber.length) == kCFPropertyList280NorthMagicNumber)
                aFormat = kCFPropertyList280NorthFormat_v1_0;
            else
                aFormat = kCFPropertyListXMLFormat_v1_0;
        }
        else
            aFormat = kCFPropertyListXMLFormat_v1_0;
    }
    if (aFormat == kCFPropertyListXMLFormat_v1_0)
        return CPPropertyListCreateFromXMLData(aData);
    if (aFormat == kCFPropertyList280NorthFormat_v1_0)
        return CPPropertyListCreateFrom280NorthData(aData);
    return NULL;
}
var _CPPropertyListSerializeObject = function(aPlist, serializers)
{
    var type = typeof aPlist,
        valueOf = aPlist.valueOf(),
        typeValueOf = typeof valueOf;
    if (type != typeValueOf)
    {
        type = typeValueOf;
        aPlist = valueOf;
    }
    if (type == "string")
        return serializers["string"](aPlist, serializers);
    else if (aPlist === true || aPlist === false)
        return serializers["boolean"](aPlist, serializers);
    else if (type == "number")
    {
        var integer = FLOOR(aPlist);
        if (integer == aPlist)
            return serializers["integer"](aPlist, serializers);
        else
            return serializers["real"](aPlist, serializers);
    }
    else if (aPlist.slice)
        return serializers["array"](aPlist, serializers);
    else
        return serializers["dictionary"](aPlist, serializers);
}
var XML_XML = "xml",
    XML_DOCUMENT = "#document",
    PLIST_PLIST = "plist",
    PLIST_KEY = "key",
    PLIST_DICTIONARY = "dict",
    PLIST_ARRAY = "array",
    PLIST_STRING = "string",
    PLIST_BOOLEAN_TRUE = "true",
    PLIST_BOOLEAN_FALSE = "false",
    PLIST_NUMBER_REAL = "real",
    PLIST_NUMBER_INTEGER = "integer";
var _plist_traverseNextNode = function(anXMLNode, stayWithin, stack)
{
    var node = anXMLNode;
    node = (node.firstChild); if (node != NULL && ((node.nodeType) == 8 || (node.nodeType) == 3)) while ((node = (node.nextSibling)) && ((node.nodeType) == 8 || (node.nodeType) == 3)) ;;
    if (node)
        return node;
    if ((anXMLNode.nodeName) == PLIST_ARRAY || (anXMLNode.nodeName) == PLIST_DICTIONARY)
        stack.pop();
    else
    {
        if (node == stayWithin)
            return NULL;
        node = anXMLNode;
        while ((node = (node.nextSibling)) && ((node.nodeType) == 8 || (node.nodeType) == 3)) ;;
        if (node)
            return node;
    }
    node = anXMLNode;
    while (node)
    {
        var next = node;
        while ((next = (next.nextSibling)) && ((next.nodeType) == 8 || (next.nodeType) == 3)) ;;
        if (next)
            return next;
        var node = (node.parentNode);
        if (stayWithin && node == stayWithin)
            return NULL;
        stack.pop();
    }
    return NULL;
}
function CPPropertyListCreateFromXMLData(XMLNodeOrData)
{
    var XMLNode = XMLNodeOrData;
    if (XMLNode.string)
    {
        if (window.ActiveXObject)
        {
            XMLNode = new ActiveXObject("Microsoft.XMLDOM");
            XMLNode.loadXML(XMLNodeOrData.string.substr(XMLNodeOrData.string.indexOf(".dtd\">") + 6));
        }
        else
            XMLNode = (new DOMParser().parseFromString(XMLNodeOrData.string, "text/xml").documentElement);
    }
    while (((XMLNode.nodeName) == XML_DOCUMENT) || ((XMLNode.nodeName) == XML_XML))
        XMLNode = (XMLNode.firstChild); if (XMLNode != NULL && ((XMLNode.nodeType) == 8 || (XMLNode.nodeType) == 3)) while ((XMLNode = (XMLNode.nextSibling)) && ((XMLNode.nodeType) == 8 || (XMLNode.nodeType) == 3)) ;;
    if (((XMLNode.nodeType) == 10))
        while ((XMLNode = (XMLNode.nextSibling)) && ((XMLNode.nodeType) == 8 || (XMLNode.nodeType) == 3)) ;;
    if (!((XMLNode.nodeName) == PLIST_PLIST))
        return NULL;
    var key = "",
        object = NULL,
        plistObject = NULL,
        plistNode = XMLNode,
        containers = [],
        currentContainer = NULL;
    while (XMLNode = _plist_traverseNextNode(XMLNode, plistNode, containers))
    {
        var count = containers.length;
        if (count)
            currentContainer = containers[count - 1];
        if ((XMLNode.nodeName) == PLIST_KEY)
        {
            key = (((XMLNode.firstChild).nodeValue));
            while ((XMLNode = (XMLNode.nextSibling)) && ((XMLNode.nodeType) == 8 || (XMLNode.nodeType) == 3)) ;;
        }
        switch ((XMLNode.nodeName))
        {
            case PLIST_ARRAY: object = []
                                        containers.push(object);
                                        break;
            case PLIST_DICTIONARY: object = new objj_dictionary();
                                        containers.push(object);
                                        break;
            case PLIST_NUMBER_REAL: object = parseFloat((((XMLNode.firstChild).nodeValue)));
                                        break;
            case PLIST_NUMBER_INTEGER: object = parseInt((((XMLNode.firstChild).nodeValue)));
                                        break;
            case PLIST_STRING: object = decodeURIComponent((XMLNode.firstChild) ? (((XMLNode.firstChild).nodeValue)) : "");
                                        break;
            case PLIST_BOOLEAN_TRUE: object = true;
                                        break;
            case PLIST_BOOLEAN_FALSE: object = false;
                                        break;
            default: objj_exception_throw(new objj_exception(OBJJPlistParseException, "*** " + (XMLNode.nodeName) + " tag not recognized in Plist."));
        }
        if (!plistObject)
            plistObject = object;
        else if (currentContainer)
            if (currentContainer.slice)
                currentContainer.push(object);
            else
                { if ((currentContainer)._buckets[key] == NULL) { (currentContainer)._keys.push(key); ++(currentContainer).count; } if (((currentContainer)._buckets[key] = object) == NULL) --(currentContainer).count;};
    }
    return plistObject;
}
function CPPropertyListCreateXMLData(aPlist)
{
    var data = new objj_data();
    data.string = "";
    data.string += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
    data.string += "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">";
    data.string += "<plist version = \"1.0\">";
    _CPPropertyListAppendXMLData(data, aPlist, "");
    data.string += "</plist>";
    return data;
}
var _CPArrayAppendXMLData = function(XMLData, anArray)
{
    var i = 0,
        count = anArray.length;
    XMLData.string += "<array>";
    for (; i < count; ++i)
        _CPPropertyListAppendXMLData(XMLData, anArray[i]);
    XMLData.string += "</array>";
}
var _CPDictionaryAppendXMLData = function(XMLData, aDictionary)
{
    var keys = aDictionary._keys,
        i = 0,
        count = keys.length;
    XMLData.string += "<dict>";
    for (; i < count; ++i)
    {
        XMLData.string += "<key>" + keys[i] + "</key>";
        _CPPropertyListAppendXMLData(XMLData, ((aDictionary)._buckets[keys[i]]));
    }
    XMLData.string += "</dict>";
}
var _CPPropertyListAppendXMLData = function(XMLData, aPlist)
{
    var type = typeof aPlist,
        valueOf = aPlist.valueOf(),
        typeValueOf = typeof valueOf;
    if (type != typeValueOf)
    {
        type = typeValueOf;
        aPlist = valueOf;
    }
    if (type == "string")
        XMLData.string += "<string>" + encodeURIComponent(aPlist) + "</string>";
    else if (aPlist === true)
        XMLData.string += "<true/>";
    else if (aPlist === false)
        XMLData.string += "<false/>";
    else if (type == "number")
    {
        var integer = FLOOR(aPlist);
        if (integer == aPlist)
            XMLData.string += "<integer>" + aPlist + "</integer>";
        else
            XMLData.string += "<real>" + aPlist + "</real>";
    }
    else if (aPlist.slice)
        _CPArrayAppendXMLData(XMLData, aPlist);
    else
        _CPDictionaryAppendXMLData(XMLData, aPlist);
}
var ARRAY_MARKER = "A",
    DICTIONARY_MARKER = "D",
    FLOAT_MARKER = "f",
    INTEGER_MARKER = "d",
    STRING_MARKER = "S",
    TRUE_MARKER = "T",
    FALSE_MARKER = "F",
    KEY_MARKER = "K",
    END_MARKER = "E";
function CPPropertyListCreateFrom280NorthData(aData)
{
    var stream = new objj_markedStream(aData.string),
        marker = NULL,
        key = "",
        object = NULL,
        plistObject = NULL,
        containers = [],
        currentContainer = NULL;
    while (marker = stream.getMarker())
    {
        if (marker == END_MARKER)
        {
            containers.pop();
            continue;
        }
        var count = containers.length;
        if (count)
            currentContainer = containers[count - 1];
        if (marker == KEY_MARKER)
        {
            key = stream.getString();
            marker = stream.getMarker();
        }
        switch (marker)
        {
            case ARRAY_MARKER: object = []
                                    containers.push(object);
                                    break;
            case DICTIONARY_MARKER: object = new objj_dictionary();
                                    containers.push(object);
                                    break;
            case FLOAT_MARKER: object = parseFloat(stream.getString());
                                    break;
            case INTEGER_MARKER: object = parseInt(stream.getString());
                                    break;
            case STRING_MARKER: object = stream.getString();
                                    break;
            case TRUE_MARKER: object = true;
                                    break;
            case FALSE_MARKER: object = false;
                                    break;
            default: objj_exception_throw(new objj_exception(OBJJPlistParseException, "*** " + marker + " marker not recognized in Plist."));
        }
        if (!plistObject)
            plistObject = object;
        else if (currentContainer)
            if (currentContainer.slice)
                currentContainer.push(object);
            else
                { if ((currentContainer)._buckets[key] == NULL) { (currentContainer)._keys.push(key); ++(currentContainer).count; } if (((currentContainer)._buckets[key] = object) == NULL) --(currentContainer).count;};
    }
    return plistObject;
}
function CPPropertyListCreate280NorthData(aPlist)
{
    var data = new objj_data();
    data.string = kCFPropertyList280NorthMagicNumber + ";1.0;" + _CPPropertyListSerializeObject(aPlist, _CPPropertyList280NorthSerializers);
    return data;
}
var _CPPropertyList280NorthSerializers = {};
_CPPropertyList280NorthSerializers["string"] = function(aString)
{
    return STRING_MARKER + ';' + aString.length + ';' + aString;
}
_CPPropertyList280NorthSerializers["boolean"] = function(aBoolean)
{
    return (aBoolean ? TRUE_MARKER : FALSE_MARKER) + ';';
}
_CPPropertyList280NorthSerializers["integer"] = function(anInteger)
{
    var string = "" + anInteger;
    return INTEGER_MARKER + ';' + string.length + ';' + string;
}
_CPPropertyList280NorthSerializers["real"] = function(aFloat)
{
    var string = "" + aFloat;
    return FLOAT_MARKER + ';' + string.length + ';' + string;
}
_CPPropertyList280NorthSerializers["array"] = function(anArray, serializers)
{
    var index = 0,
        count = anArray.length,
        string = ARRAY_MARKER + ';';
    for (; index < count; ++index)
        string += _CPPropertyListSerializeObject(anArray[index], serializers);
    return string + END_MARKER + ';';
}
_CPPropertyList280NorthSerializers["dictionary"] = function(aDictionary, serializers)
{
    var keys = aDictionary._keys,
        index = 0,
        count = keys.length,
        string = DICTIONARY_MARKER +';';
    for (; index < count; ++index)
    {
        var key = keys[index];
        string += KEY_MARKER + ';' + key.length + ';' + key;
        string += _CPPropertyListSerializeObject(((aDictionary)._buckets[key]), serializers);
    }
    return string + END_MARKER + ';';
}
OBJJFileNotFoundException = "OBJJFileNotFoundException";
OBJJExecutableNotFoundException = "OBJJExecutableNotFoundException";
var objj_files = { },
    objj_bundles = { },
    objj_bundlesForClass = { },
    objj_searches = { };
var OBJJ_NO_FILE = {},
    OBJJ_INCLUDE_PATHS = ["Frameworks", "SomethingElse"];
var OBJJ_BASE_URI = "";
if (window.opera) {
var DOMBaseElement = document.getElementsByTagName("base")[0];
if (DOMBaseElement)
    OBJJ_BASE_URI = (DOMBaseElement.getAttribute('href')).substr(0, (DOMBaseElement.getAttribute('href')).lastIndexOf('/') + 1);
}
function objj_file()
{
    this.path = NULL;
    this.bundle = NULL;
    this.included = NO;
    this.contents = NULL;
    this.fragments = NULL;
}
function objj_bundle()
{
    this.path = NULL;
    this.info = NULL;
    this.__address = (OBJECT_COUNT++);
}
function objj_getBundleWithPath(aPath)
{
    return objj_bundles[aPath];
}
function objj_bundleForClass(aClass)
{
    return objj_bundlesForClass[aClass.name];
}
function objj_addClassForBundle(aClass, aBundle)
{
    objj_bundlesForClass[aClass.name] = aBundle;
}
function objj_request_file(aFilePath, shouldSearchLocally, aCallback)
{
    new objj_search(aFilePath, shouldSearchLocally, aCallback).attemptNextSearchPath();
}
var objj_search = function(aFilePath, shouldSearchLocally, aCallback)
{
    this.filePath = aFilePath;
    this.bundle = NULL;
    this.bundleObservers = [];
    this.searchPath = NULL;
    this.searchedPaths = [];
    this.includePathsIndex = shouldSearchLocally ? -1 : 0;
    this.searchRequest = NULL;
    this.didCompleteCallback = aCallback;
}
objj_search.prototype.nextSearchPath = function()
{
    var path = objj_standardize_path((this.includePathsIndex == -1 ? "" : OBJJ_INCLUDE_PATHS[this.includePathsIndex] + '/') + this.filePath);
    ++this.includePathsIndex;
    return path;
}
objj_search.prototype.attemptNextSearchPath = function()
{
    var searchPath = this.nextSearchPath(),
        file = objj_files[searchPath];
    objj_alert("Will attempt to find " + this.filePath + " at " + searchPath);
    if (file)
    {
        objj_alert("The file request at " + this.filePath + " has already been downloaded at " + searchPath);
        if (this.didCompleteCallback)
            this.didCompleteCallback(file);
        return;
    }
    var existingSearch = objj_searches[searchPath];
    if (existingSearch)
    {
        if (this.didCompleteCallback)
            existingSearch.didCompleteCallback = this.didCompleteCallback;
        return;
    }
    this.searchedPaths.push(this.searchPath = searchPath);
    var infoPath = objj_standardize_path((searchPath).substr(0, (searchPath).lastIndexOf('/') + 1) + "Info.plist")
        bundle = objj_bundles[infoPath];
    if (bundle)
    {
        this.bundle = bundle;
        this.request(searchPath, this.didReceiveSearchResponse);
    }
    else
    {
        var existingBundleSearch = objj_searches[infoPath];
        if (existingBundleSearch)
        {
            --this.includePathsIndex;
            this.searchedPaths.pop();
             if (this.searchedPaths.length)
                 this.searchPath = this.searchedPaths[this.searchedPaths.length - 1];
             else
                 this.searchPath = NULL;
            existingBundleSearch.bundleObservers.push(this);
            return;
        }
        else
        {
            this.bundleObservers.push(this);
            this.request(infoPath, this.didReceiveBundleResponse);
            if (!this.searchReplaced)
                this.searchRequest = this.request(searchPath, this.didReceiveSearchResponse);
        }
    }
}
if (window.ActiveXObject) {
objj_search.responseCallbackLock = NO;
objj_search.responseCallbackQueue = [];
objj_search.removeResponseCallbackForFilePath = function(aFilePath)
{
    var queue = objj_search.responseCallbackQueue,
        index = queue.length;
    while (index--)
        if (queue[index][3] == aFilePath)
        {
            queue.splice(index, 1);
            return;
        }
}
objj_search.serializeResponseCallback = function(aMethod, aSearch, aResponse, aFilePath)
{
    var queue = objj_search.responseCallbackQueue;
    queue.push([aMethod, aSearch, aResponse, aFilePath]);
    if (objj_search.responseCallbackLock)
        return;
    objj_search.responseCallbackLock = YES;
    while (queue.length)
    {
        var callback = queue[0];
        queue.splice(0, 1);
        callback[0].apply(callback[1], [callback[2]]);
    }
    objj_search.responseCallbackLock = NO;
}
}
objj_search.prototype.request = function(aFilePath, aMethod)
{
    var search = this,
        isPlist = aFilePath.substr(aFilePath.length - 6, 6) == ".plist",
        request = objj_request_xmlhttp(),
        response = objj_response_xmlhttp();
    response.filePath = aFilePath;
    request.onreadystatechange = function()
    {
        if (request.readyState == 4)
        {
            if (response.success = (request.status != 404 && request.responseText && request.responseText.length) ? YES : NO)
            {
                if (window.files_total)
                {
                    if (!window.files_loaded)
                        window.files_loaded = 0;
                    window.files_loaded += request.responseText.length;
                    if (window.update_progress)
                        window.update_progress(window.files_loaded / window.files_total);
                }
                if (isPlist)
                    response.xml = objj_standardize_xml(request);
                else
                    response.text = request.responseText;
            }
            if (window.ActiveXObject)
                objj_search.serializeResponseCallback(aMethod, search, response, aFilePath);
            else
                aMethod.apply(search, [response]);
        }
    }
    objj_searches[aFilePath] = this;
    if (request.overrideMimeType && isPlist)
        request.overrideMimeType('text/xml');
    if (window.opera && aFilePath.charAt(0) != '/')
        aFilePath = OBJJ_BASE_URI + aFilePath;
    try
    {
        request.open("GET", aFilePath, YES);
        request.send("");
    }
    catch (anException)
    {
        response.success = NO;
        if (window.ActiveXObject)
            objj_search.serializeResponseCallback(aMethod, search, response, aFilePath);
        else
            aMethod.apply(search, [response]);
    }
    return request;
}
objj_search.prototype.didReceiveSearchResponse = function(aResponse)
{
    if (!this.bundle)
    {
        this.cachedSearchResponse = aResponse;
        return;
    }
    if (aResponse.success)
    {
        file = new objj_file();
        file.path = aResponse.filePath;
        file.bundle = this.bundle
        file.contents = aResponse.text;
        this.complete(file);
    }
    else if (this.includePathsIndex < OBJJ_INCLUDE_PATHS.length)
    {
        this.bundle = NULL;
        this.attemptNextSearchPath();
    }
    else
        objj_exception_throw(new objj_exception(OBJJFileNotFoundException, "*** Could not locate file named \"" + this.filePath + "\" in search paths."));
}
objj_search.prototype.didReceiveBundleResponse = function(aResponse)
{
    var bundle = new objj_bundle();
    bundle.path = aResponse.filePath;
    if (aResponse.success)
        bundle.info = CPPropertyListCreateFromXMLData(aResponse.xml);
    else
        bundle.info = new objj_dictionary();
    objj_bundles[aResponse.filePath] = bundle;
    var executablePath = ((bundle.info)._buckets["CPBundleExecutable"]);
    if (executablePath)
    {
        this.request((aResponse.filePath).substr(0, (aResponse.filePath).lastIndexOf('/') + 1) + executablePath, this.didReceiveExecutableResponse);
        var directory = (aResponse.filePath).substr(0, (aResponse.filePath).lastIndexOf('/') + 1),
            replacedFiles = ((bundle.info)._buckets["CPBundleReplacedFiles"]),
            index = 0,
            count = replacedFiles.length;
        for (; index < count; ++index)
        {
            objj_searches[directory + replacedFiles[index]] = this;
            if (directory + replacedFiles[index] == this.searchPath)
            {
                this.searchReplaced = YES;
                if (!this.cachedSearchResponse && this.searchRequest)
                    this.searchRequest.abort();
                if (window.ActiveXObject)
                    objj_search.removeResponseCallbackForFilePath(this.searchPath);
            }
        }
    }
    this.bundle = bundle;
    var observers = this.bundleObservers,
        index = 0,
        count = observers.length;
    for(; index < count; ++index)
    {
        var observer = observers[index];
        if (observer != this)
            observer.attemptNextSearchPath();
        else if (this.cachedSearchResponse && !this.searchReplaced)
            this.didReceiveSearchResponse(this.cachedSearchResponse);
    }
    this.bundleObservers = [];
}
objj_search.prototype.didReceiveExecutableResponse = function(aResponse)
{
    if (!aResponse.success)
        objj_exception_throw(new objj_exception(OBJJExecutableNotFoundException, "*** The specified executable could not be located at \"" + this.filePath + "\"."));
    var files = objj_decompile(aResponse.text, this.bundle),
        index = 0,
        count = files.length,
        length = this.filePath.length;
    for (; index < count; ++index)
    {
        var file = files[index],
            path = file.path;
        if (this.filePath == path.substr(path.length - length))
            this.complete(file);
        else
            objj_files[path] = file;
    }
}
objj_search.prototype.complete = function(aFile)
{
    var index = 0,
        count = this.searchedPaths.length;
    for (; index < count; ++index)
    {
        objj_files[this.searchedPaths[index]] = aFile;
    }
    if (this.didCompleteCallback)
        this.didCompleteCallback(aFile);
}
function objj_standardize_path(aPath)
{
    if (aPath.indexOf("/./") != -1 && aPath.indexOf("//") != -1 && aPath.indexOf("/../") != -1)
        return aPath;
    var index = 0,
        components = aPath.split('/');
    for(;index < components.length; ++index)
        if(components[index] == "..")
        {
            components.splice(index - 1, 2);
            index -= 2;
        }
        else if(index != 0 && !components[index].length || components[index] == '.' || components[index] == "..")
            components.splice(index--, 1);
    return components.join('/');
}
if (window.ActiveXObject) {
objj_standardize_xml = function(aRequest)
{
    var XMLData = new ActiveXObject("Microsoft.XMLDOM");
    XMLData.loadXML(aRequest.responseText.substr(aRequest.responseText.indexOf(".dtd\">") + 6));
    return XMLData;
}
} else {
objj_standardize_xml = function(aRequest)
{
    return aRequest.responseXML;
}
}
function objj_response_xmlhttp()
{
    return new Object;
}
if (window.XMLHttpRequest) {
objj_request_xmlhttp = function()
{
    return new XMLHttpRequest();
}
} else if (window.ActiveXObject) {
var MSXML_XMLHTTP_OBJECTS = [ "Microsoft.XMLHTTP", "Msxml2.XMLHTTP", "Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP.6.0" ],
    index = MSXML_XMLHTTP_OBJECTS.length;
while (index--)
{
    try
    {
        new ActiveXObject(MSXML_XMLHTTP_OBJECTS[index]);
        break;
    }
    catch (anException)
    {
    }
}
var MSXML_XMLHTTP = MSXML_XMLHTTP_OBJECTS[index];
delete index;
delete MSXML_XMLHTTP_OBJECTS;
objj_request_xmlhttp = function()
{
    return new ActiveXObject(MSXML_XMLHTTP);
}
}
var OBJJ_EXCEPTION_OUTPUT_STREAM = NULL;
function objj_exception(aName, aReason, aUserInfo)
{
    this.name = aName;
    this.reason = aReason;
    this.userInfo = aUserInfo;
    this.__address = (OBJECT_COUNT++);
}
objj_exception.prototype.toString = function()
{
    return this.reason;
}
function objj_exception_throw(anException)
{
    throw anException;
}
function objj_exception_report(anException, aSourceFile)
{
    objj_fprintf(OBJJ_EXCEPTION_OUTPUT_STREAM, aSourceFile.path + "\n" + anException);
    throw anException;
}
function objj_exception_setOutputStream(aStream)
{
    OBJJ_EXCEPTION_OUTPUT_STREAM = aStream;
}
objj_exception_setOutputStream(function(aString) { });
OBJJParseException = "OBJJParseException";
OBJJClassNotFoundException = "OBJJClassNotFoundException";
var TOKEN_NEW = "new",
    TOKEN_SUPER = "super",
    TOKEN_CLASS = "class",
    TOKEN_IMPORT = "import",
    TOKEN_FUNCTION = "function",
    TOKEN_SELECTOR = "selector",
    TOKEN_IMPLEMENTATION = "implementation",
    TOKEN_PLUS = '+',
    TOKEN_MINUS = '-',
    TOKEN_COLON = ':',
    TOKEN_COMMA = ',',
    TOKEN_PERIOD = '.',
    TOKEN_ASTERISK = '*',
    TOKEN_SEMICOLON = ';',
    TOKEN_LESS_THAN = '<',
    TOKEN_OPEN_BRACE = '{',
    TOKEN_CLOSE_BRACE = '}',
    TOKEN_GREATER_THAN = '>',
    TOKEN_OPEN_BRACKET = '[',
    TOKEN_DOUBLE_QUOTE = '"',
    TOKEN_PREPROCESSOR = '@',
    TOKEN_CLOSE_BRACKET = ']',
    TOKEN_QUESTION_MARK = '?',
    TOKEN_OPEN_PARENTHESIS = '(',
    TOKEN_CLOSE_PARENTHESIS = ')';
var SUPER_CLASSES = new objj_dictionary(),
    CURRENT_SUPER_CLASS = NULL,
    CURRENT_CLASS_NAME = NULL;
var OBJJ_CURRENT_BUNDLE = NULL;
var objj_lexer = function(aString, aSourceFile)
{
    this._index = 0;
    this._tokens = (aString + '\n').match(/\/\/.*(\r|\n)?|\/\*(?:.|\n|\r)*?\*\/|\w+\b|[+-]?\d+(([.]\d+)*([eE][+-]?\d+))?|"[^"\\]*(\\.[^"\\]*)*"|'[^'\\]*(\\.[^'\\]*)*'|\s+|./g);
    this.file = aSourceFile;
    return this;
}
objj_lexer.prototype.next = function()
{
    return this._tokens[this._index++];
}
objj_lexer.prototype.previous = function()
{
    return this._tokens[--this._index];
}
objj_lexer.prototype.last = function()
{
    if (this._index > 1)
        return this._tokens[this._index - 2];
    return NULL;
}
objj_lexer.prototype.skip_whitespace= function()
{
    var token;
    while((token = this.next()) && (!(/\S/).test(token) || token.substr(0,2) == "//" || token.substr(0,2) == "/*")) ;
    return token;
}
var objj_preprocess_method = function(tokens, count, array_name)
{
    var token,
        selector = "",
        parameters = new Array();
    while((token = tokens.skip_whitespace()) && token != TOKEN_OPEN_BRACE)
    {
        if (token == TOKEN_COLON)
        {
            selector += token;
            token = tokens.skip_whitespace();
            if (token == TOKEN_OPEN_PARENTHESIS)
            {
                while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) ;
                token = tokens.skip_whitespace();
            }
            parameters[parameters.length] = token;
        }
        else if (token == TOKEN_OPEN_PARENTHESIS)
            while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_PARENTHESIS) ;
        else if (token == TOKEN_COMMA)
        {
            if ((token = tokens.skip_whitespace()) != TOKEN_PERIOD || tokens.next() != TOKEN_PERIOD || tokens.next() != TOKEN_PERIOD)
                objj_exception_throw(new objj_exception(OBJJParseException, "*** Argument list expected after ','."));
        }
        else
            selector += token;
    }
    var i= 0,
        length = parameters.length,
        selectorDisplayName = "$"+CURRENT_CLASS_NAME+"__"+selector.replace(/:/g, "_"),
        preprocessed = array_name + "["+count+"] = new objj_method(sel_registerName(\""+selector+"\"), function "+selectorDisplayName+"(self, _cmd";
    for(; i < length; ++i)
        preprocessed += ", " + parameters[i];
    return preprocessed + ")\n{ with(self)\n{" + objj_preprocess_tokens(tokens, TOKEN_CLOSE_BRACE, TOKEN_OPEN_BRACE) + "}\n});\n";
}
var objj_preprocess_implementation= function(tokens)
{
    var token = "",
        category = NO,
        preprocessed = "",
        class_name = tokens.skip_whitespace(),
        superclass_name = "Nil",
        class_method_count = 0,
        instance_method_count = 0;
    if (!(/^\w/).test(class_name))
        objj_exception_throw(new objj_exception(OBJJParseException, "*** Expected class name, found \"" + class_name + "\"."));
    CURRENT_SUPER_CLASS = NULL;
    CURRENT_CLASS_NAME = class_name;
    if((token = tokens.skip_whitespace()) == TOKEN_OPEN_PARENTHESIS)
    {
        token = tokens.skip_whitespace();
        if(tokens.skip_whitespace() != TOKEN_CLOSE_PARENTHESIS)
            objj_exception_throw(new objj_exception(OBJJParseException, "*** Improper Category Definition for class \""+class_name+"\"."));
        preprocessed += "{\nvar the_class = objj_getClass(\"" + class_name + "\")\n";
        preprocessed += "if(!the_class) objj_exception_throw(new objj_exception(OBJJClassNotFoundException, \"*** Could not find definition for class \\\"" + class_name + "\\\"\"));\n";
        preprocessed += "var meta_class = the_class.isa;";
        var superclass_name = ((SUPER_CLASSES)._buckets[class_name]);
        if (!superclass_name)
            CURRENT_SUPER_CLASS = "objj_getClass(\"" + class_name + "\").super_class";
        else
            CURRENT_SUPER_CLASS = "objj_getClass(\"" + superclass_name + "\")";
    }
    else
    {
        if(token == TOKEN_COLON)
        {
            token = tokens.skip_whitespace();
            if (!(/^\w/).test(token))
                objj_exception_throw(new objj_exception(OBJJParseException, "*** Expected class name, found \"" + token + "\"."));
            superclass_name = token;
            CURRENT_SUPER_CLASS = "objj_getClass(\"" + superclass_name + "\")";
            { if ((SUPER_CLASSES)._buckets[class_name] == NULL) { (SUPER_CLASSES)._keys.push(class_name); ++(SUPER_CLASSES).count; } if (((SUPER_CLASSES)._buckets[class_name] = superclass_name) == NULL) --(SUPER_CLASSES).count;};
            token = tokens.skip_whitespace();
        }
        preprocessed += "{var the_class = objj_allocateClassPair(" + superclass_name + ", \"" + class_name + "\"),\nmeta_class = the_class.isa;";
        if (token == TOKEN_OPEN_BRACE)
        {
            var ivar = true,
                ivar_count = 0;
            while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_BRACE)
            {
                if (token != TOKEN_SEMICOLON && (ivar = !ivar))
                {
                    if (ivar_count++ == 0)
                        preprocessed += "class_addIvars(the_class, [";
                    else
                        preprocessed += ", ";
                    preprocessed += "new objj_ivar(\"" + token + "\")";
                }
            }
            if (ivar_count)
                preprocessed += "]);\n";
            if (!token)
                objj_exception_throw(new objj_exception(OBJJParseException, "*** Expected '}'"));
        }
        else tokens.previous();
        preprocessed += "objj_registerClassPair(the_class);\n";
        preprocessed += "objj_addClassForBundle(the_class, objj_getBundleWithPath(OBJJ_CURRENT_BUNDLE.path));\n";
    }
    while((token = tokens.skip_whitespace()))
    {
        if(token == TOKEN_PLUS) preprocessed += (class_method_count ? "" : "var class_methods = [];\n") + objj_preprocess_method(tokens, class_method_count++, "class_methods");
        else if(token == TOKEN_MINUS) preprocessed += (instance_method_count ? "" : "var instance_methods = [];\n") + objj_preprocess_method(tokens, instance_method_count++, "instance_methods");
        else if(token == TOKEN_PREPROCESSOR)
        {
            if((token = tokens.next()) == "end")
                break;
            else
                objj_exception_throw(new objj_exception(OBJJParseException, "*** Expected \"@end\", found \"@" + token + "\"."));
        }
    }
    if (instance_method_count) preprocessed += "class_addMethods(the_class, instance_methods);\n";
    if (class_method_count) preprocessed += "class_addMethods(meta_class, class_methods);\n";
    return preprocessed + '}';
}
var objj_preprocess_directive = function(tokens)
{
    token = tokens.next();
    if(token.charAt(0) == TOKEN_DOUBLE_QUOTE) return token;
    else if(token == TOKEN_CLASS) { tokens.skip_whitespace(); return ""; }
    else if(token == TOKEN_IMPLEMENTATION) return objj_preprocess_implementation(tokens);
    else if(token == TOKEN_SELECTOR)
    {
        if (tokens.skip_whitespace() != TOKEN_OPEN_PARENTHESIS)
            objj_exception_throw(new objj_exception(OBJJParseException, "*** Expected ')'"));
        return "sel_registerName(\"" + objj_preprocess_tokens(tokens, TOKEN_CLOSE_PARENTHESIS) +"\")";
    }
    return "";
}
var objj_preprocess_brackets = function(tokens)
{
    var literal = '[',
        receiver = "",
        selector = "",
        marg_list = new Array(),
        preprocessed = "objj_msgSend";
    var token = "",
        array = false,
        previous = "",
        braces = 0,
        tertiary = 0,
        parenthesis = 0;
    while((token = tokens.skip_whitespace()) && token != TOKEN_CLOSE_BRACKET)
    {
        var preprocess = !braces && !tertiary && !parenthesis && !array;
        if (token == TOKEN_SUPER)
        {
            if (!receiver.length)
            {
                preprocessed = "objj_msgSendSuper";
                token = "{ receiver:self, super_class:" + CURRENT_SUPER_CLASS + " }";
            }
            else
                objj_exception_throw(new objj_exception(OBJJParseException, "*** Can't use 'super' in this context."));
        }
        else if (token == TOKEN_OPEN_BRACE) ++braces;
        else if (token == TOKEN_CLOSE_BRACE) --braces;
        else if(token == TOKEN_QUESTION_MARK) ++tertiary;
        else if(token == TOKEN_OPEN_PARENTHESIS) ++parenthesis;
        else if(token == TOKEN_CLOSE_PARENTHESIS) --parenthesis;
        else if(token == TOKEN_OPEN_BRACKET) token = objj_preprocess_brackets(tokens);
        else if(token == TOKEN_PREPROCESSOR) token = objj_preprocess_directive(tokens);
        if(preprocess)
        {
            if(token == TOKEN_COMMA && !selector.length)
                array = true;
            if(token == TOKEN_COLON)
            {
                var last = tokens.last();
                if (last && (!(/\S/).test(last) || last.substr(0, 2) == "//" || last.substr(0, 2) == "/*"))
                {
                    selector += ':';
                    marg_list[marg_list.length - 1] += previous;
                    marg_list[marg_list.length] = previous = "";
                }
                else
                {
                    selector += previous + ":";
                    marg_list[marg_list.length] = previous = "";
                }
            }
            else
            {
                if (previous == TOKEN_NEW)
                    previous = "new ";
                if (selector.length)
                    marg_list[marg_list.length - 1] += previous;
                else
                    receiver += previous;
                previous = token;
            }
        }
        else
        {
            if(token == TOKEN_COLON && !braces)
                --tertiary;
            previous += token;
        }
        if (token == TOKEN_NEW)
            literal += "new ";
        else
            literal += token;
    }
    if (selector.length) marg_list[marg_list.length - 1] += previous;
    else if(!array && receiver.length && !((/[\:\+\-\*\/\=\<\>\&\|\!\.\%]/).test(receiver.charAt(receiver.length - 1))) &&
            receiver != TOKEN_NEW && !(/[\+\-\*\/\=\<\>\&\|\!\.\[\^\(]/).test(previous.charAt(0)))
        selector = previous;
    else return literal + ']';
    preprocessed += '(' + receiver + ", \"" + sel_registerName(selector) + "\"";
    var i = 0,
        length = marg_list.length;
    for(; i < length; ++i)
        preprocessed += ", " + marg_list[i];
    return preprocessed + ')';
}
function objj_preprocess_tokens(tokens, terminator, instigator, segment)
{
    var count = 0,
        token = "",
        fragments = [],
        preprocessed = "";
    while((token = tokens.next()) && ((token != terminator) || count))
    {
        if (instigator)
        {
            if (token == instigator) ++count;
            else if (token == terminator) --count;
        }
        if(token == TOKEN_IMPORT)
        {
            if ((/[^\s]/).test(preprocessed))
                fragments.push(fragment_create_code(preprocessed, OBJJ_CURRENT_BUNDLE, tokens.file));
            preprocessed = "";
            var path = "",
                token = tokens.skip_whitespace(),
                isLocal = token != TOKEN_LESS_THAN;
            if(token == TOKEN_LESS_THAN)
            {
                while((token= tokens.next()) && token != TOKEN_GREATER_THAN) path+= token;
                if(!token) objj_throw("Parser Error - Unterminated import statement.");
            }
            else if(token.charAt(0) == TOKEN_DOUBLE_QUOTE) path= token.substr(1, token.length-2);
            else
                objj_exception_throw(new objj_exception(OBJJParseException, "*** Expecting '<' or '\"', found \"" + token + "\"."));
            fragments.push(fragment_create_file(path, NULL, isLocal, tokens.file));
        }
        else if(token == TOKEN_FUNCTION)
        {
            var accumulator= "";
            while((token = tokens.next()) && token != TOKEN_OPEN_PARENTHESIS && !(/^\w/).test(token))
                accumulator += token;
            if(token == TOKEN_OPEN_PARENTHESIS)
                preprocessed+= "function"+accumulator+'(';
            else
            {
                preprocessed += token + "= function";
            }
        }
        else if(token == TOKEN_PREPROCESSOR)
            preprocessed+= objj_preprocess_directive(tokens);
        else if(token == TOKEN_OPEN_BRACKET)
            preprocessed += objj_preprocess_brackets(tokens);
        else
            preprocessed += token;
    }
    if (preprocessed.length && (/[^\s]/).test(preprocessed))
        fragments.push(fragment_create_code(preprocessed, OBJJ_CURRENT_BUNDLE, tokens.file));
    if (!segment)
        return fragments.length ? fragments[0].info : "";
    return fragments;
}
function objj_preprocess(aString, aBundle, aSourceFile)
{
    try
    {
        OBJJ_CURRENT_BUNDLE = aBundle;
        return objj_preprocess_tokens(new objj_lexer(aString, aSourceFile), nil, nil, YES);
    }
    catch (anException)
    {
        objj_exception_report(anException, aSourceFile);
    }
    return [];
}
var objj_included_files = { };
var FRAGMENT_CODE = 1,
    FRAGMENT_FILE = 1 << 2,
    FRAGMENT_LOCAL = 1 << 3;
function objj_fragment()
{
    this.info = NULL;
    this.type = 0;
    this.context = NULL;
    this.bundle = NULL;
    this.file = NULL;
}
function objj_context()
{
    this.fragments = [];
    this.scheduled = NO;
    this.blocked = NO;
}
function fragment_create_code(aCode, aBundle, aFile)
{
    var fragment = new objj_fragment();
    fragment.type = (FRAGMENT_CODE);
    fragment.info = (aCode);
    fragment.bundle = aBundle;
    fragment.file = aFile;
    return fragment;
}
function fragment_create_file(aPath, aBundle, isLocal, aFile)
{
    var fragment = new objj_fragment();
    fragment.type = (FRAGMENT_FILE | (FRAGMENT_LOCAL * isLocal));
    fragment.info = aPath;
    fragment.bundle = aBundle;
    fragment.file = aFile;
    return fragment;
}
objj_context.prototype.evaluate = function()
{
    this.scheduled = NO;
    if (this.blocked)
        return this.schedule();
    var sleep = NO,
        start = new Date(),
        fragments = this.fragments;
    while (!sleep && fragments.length)
    {
        var fragment = fragments.pop();
        if ((fragment.type & FRAGMENT_FILE))
            sleep = fragment_evaluate_file(fragment);
        else
            sleep = fragment_evaluate_code(fragment);
        sleep = sleep || ((new Date() - start) > 3000);
    }
    if (sleep)
        this.schedule();
    else if (this.didCompleteCallback)
        this.didCompleteCallback(this);
}
objj_context.prototype.schedule = function()
{
    if (this.scheduled)
        return;
    this.scheduled = YES;
    var context = this;
    window.setTimeout(function () { context.evaluate(); }, 0);
}
objj_context.prototype.pushFragment = function(aFragment)
{
    aFragment.context = this;
    this.fragments.push(aFragment);
}
function fragment_evaluate_code(aFragment)
{
    var compiled;
    OBJJ_CURRENT_BUNDLE = aFragment.bundle;
    try
    {
        compiled = new Function(aFragment.info);
    }
    catch(anException)
    {
        objj_exception_report(anException, aFragment.file);
    }
    try
    {
        compiled();
    }
    catch(anException)
    {
        objj_exception_report(anException, aFragment.file);
    }
    return NO;
}
function fragment_evaluate_file(aFragment)
{
    var context = aFragment.context,
        requiresSleep = YES;
    context.blocked = YES;
    objj_request_file(aFragment.info, (aFragment.type & FRAGMENT_LOCAL), function(aFile)
    {
        requiresSleep = NO;
        context.blocked = NO;
        if (aFile == OBJJ_NO_FILE)
            objj_alert("uh oh!");
        if (objj_included_files[aFile.path])
            return;
        objj_included_files[aFile.path] = YES;
        var fragments = aFile.fragments ? aFile.fragments : objj_preprocess(aFile.contents, aFile.bundle, aFile),
            count = fragments.length,
            directory = aFile.path.substr(0, aFile.path.lastIndexOf('/') + 1);
        while (count--)
        {
            var fragment = fragments[count];
            if ((fragment.type & FRAGMENT_FILE))
            {
                if ((fragment.type & FRAGMENT_LOCAL))
                    fragment.info = directory + fragment.info;
                objj_request_file(fragment.info, (fragment.type & FRAGMENT_LOCAL), NULL);
            }
            context.pushFragment(fragment);
        }
    });
    return requiresSleep;
}
function objj_import(aPath, isLocal, didCompleteCallback)
{
    var context = new objj_context();
    context.didCompleteCallback = didCompleteCallback;
    context.pushFragment(fragment_create_file(aPath, new objj_bundle(""), isLocal, NULL));
    context.evaluate();
}
OBJJUnrecognizedFormatException = "OBJJUnrecognizedFormatException";
var STATIC_MAGIC_NUMBER = "@STATIC",
    MARKER_PATH = "p",
    MARKER_CODE = "c",
    MARKER_IMPORT_STD = 'I',
    MARKER_IMPORT_LOCAL = 'i';
var STATIC_EXTENSION = "sj";
function objj_preprocess_file(aFilePath, fileContents)
{
    var fragments = objj_preprocess(fileContents, { path:"/x" }, { path:aFilePath}),
        index = 0,
        count = fragments.length,
        preprocessed = MARKER_PATH + ';' + aFilePath.length() + ';' + aFilePath;
    for (; index < count; ++index)
    {
        var fragment = fragments[index];
        if ((fragment.type & FRAGMENT_FILE))
            preprocessed += ((fragment.type & FRAGMENT_LOCAL) ? MARKER_IMPORT_LOCAL : MARKER_IMPORT_STD) + ';' + fragment.info.length + ';' + fragment.info;
        else
            preprocessed += MARKER_CODE + ';' + fragment.info.length + ';' + fragment.info;
    }
    return preprocessed;
}
function objj_decompile(aString, bundle)
{
    var stream = new objj_markedStream(aString);
    if (stream.magicNumber() != STATIC_MAGIC_NUMBER)
        objj_exception_throw(new objj_exception(OBJJUnrecognizedFormatException, "*** Could not recognize executable code format."));
    if (stream.version() != 1.0)
        objj_exception_throw(new objj_exception(OBJJUnrecognizedFormatException, "*** Could not recognize executable code format."));
    var file = NULL,
        files = [];
    while (marker = stream.getMarker())
    {
        var text = stream.getString();
        switch (marker)
        {
            case MARKER_PATH: file = new objj_file();
                                        file.path = (bundle.path).substr(0, (bundle.path).lastIndexOf('/') + 1) + text;
                                        file.bundle = bundle;
                                        file.fragments = [];
                                        files.push(file);
                                        break;
            case MARKER_CODE: file.fragments.push(fragment_create_code(text, bundle, file));
                                        break;
            case MARKER_IMPORT_STD: file.fragments.push(fragment_create_file(text, bundle, NO, file));
                                        break;
            case MARKER_IMPORT_LOCAL: file.fragments.push(fragment_create_file(text, bundle, YES, file));
                                        break;
        }
    }
    return files;
}
function objj_msgSend_Backtrace( aReceiver, aSelector)
{
    if (aReceiver == nil)
        return nil;
    objj_debug_backtrace.push("[" + (((aReceiver.info & (CLS_META))) ? aReceiver : aReceiver.isa).name + " " + aSelector + "]");
    try
    {
        var result = class_getMethodImplementation(aReceiver.isa, aSelector).apply(aReceiver, arguments);
    }
    catch (anException)
    {
        CPLog.error("Exception " + anException + " in [" + (((aReceiver.info & (CLS_META))) ? aReceiver : aReceiver.isa).name + " " + aSelector + "]");
        objj_debug_print_backtrace();
    }
    objj_debug_backtrace.pop();
    return result;
}
function objj_msgSendSuper_Backtrace( aSuper, aSelector)
{
    objj_debug_backtrace.push("[" + (((aSuper.receiver.info & (CLS_META))) ? aSuper.receiver : aSuper.receiver.isa).name + " " + aSelector + "]");
    var super_class = aSuper.super_class;
    arguments[0] = aSuper.receiver;
    try
    {
        var result = class_getMethodImplementation(super_class, aSelector).apply(aSuper.receiver, arguments);
    }
    catch (anException)
    {
        CPLog.error("Exception " + anException + " in [" + (((aSuper.receiver.info & (CLS_META))) ? aSuper.receiver : aSuper.receiver.isa).name + " " + aSelector + "]");
        objj_debug_print_backtrace();
    }
    objj_debug_backtrace.pop();
    return result;
}
function objj_msgSend_Profile( aReceiver, aSelector)
{
    if (aReceiver == nil)
        return nil;
    var profileRecord = {
        parent : objj_debug_profile,
        receiver : (((aReceiver.info & (CLS_META))) ? aReceiver : aReceiver.isa).name,
        selector : aSelector,
        calls : []
    }
    objj_debug_profile.calls.push(profileRecord);
    objj_debug_profile = profileRecord;
    profileRecord.start = new Date();
    var result = class_getMethodImplementation(aReceiver.isa, aSelector).apply(aReceiver, arguments);
    profileRecord.end = new Date();
    objj_debug_profile = profileRecord.parent;
    return result;
}
function objj_msgSendSuper_Profile( aSuper, aSelector)
{
    var profileRecord = {
        parent : objj_debug_profile,
        receiver : (((aReceiver.info & (CLS_META))) ? aReceiver : aReceiver.isa).name,
        selector : aSelector,
        calls : []
    }
    objj_debug_profile.calls.push(profileRecord);
    objj_debug_profile = profileRecord;
    profileRecord.start = new Date();
    var super_class = aSuper.super_class;
    arguments[0] = aSuper.receiver;
    var result = class_getMethodImplementation(super_class, aSelector).apply(aSuper.receiver, arguments);
    profileRecord.end = new Date();
    objj_debug_profile = profileRecord.parent;
    return result;
}
var objj_msgSend_Standard = objj_msgSend,
    objj_msgSendSuper_Standard = objj_msgSendSuper;
var objj_debug_backtrace;
function objj_backtrace_set_enable(enabled)
{
    if (enabled)
    {
        objj_debug_backtrace = [];
        objj_msgSend = objj_msgSend_Backtrace;
        objj_msgSendSuper = objj_msgSendSuper_Backtrace;
    }
    else
    {
        objj_msgSend = objj_msgSend_Standard;
        objj_msgSendSuper = objj_msgSendSuper_Standard;
    }
}
function objj_debug_print_backtrace()
{
    CPLog.trace(objj_debug_backtrace_string());
}
function objj_debug_backtrace_string()
{
    return objj_debug_backtrace.join("\n");
}
var objj_debug_profile = null,
    objj_currently_profiling = false,
    objj_profile_cleanup;
function objj_profile(title)
{
    if (objj_currently_profiling)
        return;
    var objj_msgSend_profile_saved = objj_msgSend,
        objj_msgSendSuper_profile_saved = objj_msgSendSuper;
    objj_msgSend = objj_msgSend_Profile;
    objj_msgSendSuper = objj_msgSendSuper_Profile;
    var root = { calls: [] };
    objj_debug_profile = root;
    var context = {
        start : new Date(),
        title : title,
        profile : root
    };
    objj_profile_cleanup = function() {
        objj_msgSend = objj_msgSend_profile_saved;
        objj_msgSendSuper = objj_msgSendSuper_profile_saved;
        context.end = new Date();
        return context;
    }
    objj_currently_profiling = true;
}
function objj_profileEnd()
{
    if (!objj_currently_profiling)
        return;
    objj_debug_profile = null;
    objj_currently_profiling = false;
    return objj_profile_cleanup();
}
