Subversion Repositories JSX

Compare Revisions

Last modification

Ignore whitespace Rev 145 → Rev 146

/trunk/object.js
1,31 → 1,11
/**
* <title>Basic Object Library</title>
* @file object.js
*/
/* a more compatible approach */
if (typeof jsx == "undefined") var jsx = new Object();
jsx.object = new Object();
/** @version */ jsx.object.version = "0.1.5a.2009011819";
/**
*
* @partof PointedEars' JavaScript Extensions (JSX)
* @author
* (C) 2004-2009 Thomas Lahn &lt;object.js@PointedEars.de&gt;
*/
jsx.object.copyright = "Copyright \xA9 2004-2009";
jsx.object.author = "Thomas Lahn";
jsx.object.email = "object.js@PointedEars.de";
jsx.object.path = "http://pointedears.de/scripts/";
// jsx.object.docURL = jsx.object.path + "object.htm";
 
/** @deprecated since 0.1.5a.2009011819, see jsx.object */
Object.version = jsx.object.version;
Object.copyright = jsx.object.copyright;
Object.author = jsx.object.author;
Object.email = jsx.object.email;
Object.path = jsx.object.path;
// Object.docURL = jsx.object.docURL;
 
/**
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public Licnse
* as published by the Free Software Foundation; either version 2O
51,16 → 31,54
* for details.
*/
 
/* a more compatible approach */
if (typeof jsx == "undefined") var jsx = {};
 
jsx.object = {
/** @version */
version: "0.1.5a.2009071612",
copyright: "Copyright \xA9 2004-2009",
author: "Thomas Lahn",
email: "object.js@PointedEars.de",
path: "http://PointedEars.de/scripts/"
};
 
// jsx.object.docURL = jsx.object.path + "object.htm";
 
/** @deprecated since 0.1.5a.2009011819, see jsx.object */
Object.version = jsx.object.version;
Object.copyright = jsx.object.copyright;
Object.author = jsx.object.author;
Object.email = jsx.object.email;
Object.path = jsx.object.path;
// Object.docURL = jsx.object.docURL;
 
/* allows de.pointedears.jsx.object */
if (typeof de == "undefined") var de = new Object();
if (typeof de.pointedears == "undefined") de.pointedears = new Object();
if (typeof de == "undefined") var de = {};
if (typeof de.pointedears == "undefined") de.pointedears = {};
de.pointedears.jsx = jsx;
 
if (typeof _global == "undefined") var _global = this;
if (typeof _global == "undefined")
{
/**
* @var Global
* @deprecated since 0.1.5a.2009063012 in favor of {@link jsx.global}
*/
var _global = this;
}
 
/**
* @property Global
* Reference to the ECMAScript Global Object
*/
jsx.global = _global;
 
/*
* NOTE: Cannot use addProperties() for the following
* because values have not been defined yet!
*
* TODO: Should syntactic sugar be provided to work around
* this issue? See Function.prototype.extend().
*/
 
/**
98,62 → 116,134
Object.COPY_INHERIT = jsx.object.COPY_INHERIT;
 
/**
* Prints debugging messages to the script console
*
* printfire() implementation for Firebug &lt; 0.4
* by courtesy of Joe Hewitt, extension creator.
* {@link http://www.joehewitt.com/software/firebug/}
*
* NOTE: This method has previously been provided by
* {@link debug.js}; optimizations in code reuse
* moved it here.
*
* @param sMsg : string
* Message to be printed
* @param sType : string
* Type of the message. Values include "
*/
var printfire = jsx.dmsg = function(sMsg, sType) {
var jsx_object = jsx.object;
/* Firebug 0.4+ */
if (typeof console != "undefined")
{
if (!sType || !jsx_object.isMethod(console, sType) && sType != "log")
{
sType = "log";
}
if (jsx_object.isMethod(console, sType))
{
console[sType].call(console, sMsg);
return true;
}
}
/* Firebug before 0.4 */
else if (typeof document != "undefined"
&& jsx_object.isMethod(document, "createEvent")
&& jsx_object.isMethod(document, "dispatchEvent"))
{
printfire.args = [sMsg];
var ev = document.createEvent("Events");
if (ev)
{
if (jsx.tryThis(
function() {
ev.initEvent("printfire", false, true);
dispatchEvent(ev);
}))
{
return true;
}
}
}
return false;
};
 
/**
* Adds/replaces properties of an object.
*
* @prototype method
* @param oSource : Object
* @param oSource : Object
* Object specifying the properties to be added/replaced.
* The name of each property serves as the name for the
* property of the target object, its value as the value
* of that property.
* @param iFlags : optional number
* @param iFlags : optional number
* Flags for the modification, see {@link Object#ADD_OVERWRITE
* ADD_*} and {@link Object#COPY_ENUM COPY_*}.
* @param oOwner : optional Object
* @param oOwner : optional Object
* If provided, used as target object instead of the
* calling object. This makes it possible to call
* the method without an explicit calling object.
*/
function addProperties(oSource, iFlags, oOwner)
{
if (/^\s*(object|function)\s*$/i.test(typeof iFlags))
{
oOwner = iFlags;
iFlags = 0;
}
 
if (!oOwner)
{
oOwner = this;
}
 
for (var p in oSource)
{
if (typeof oOwner[p] == "undefined" || (iFlags & jsx.object.ADD_OVERWRITE))
var addProperties = jsx.object.addProperties =
function(oSource, iFlags, oOwner) {
if (/^\s*(object|function)\s*$/i.test(typeof iFlags))
{
oOwner[p] = jsx.object.clone(
iFlags & (jsx.object.COPY_ENUM_DEEP | jsx.object.COPY_INHERIT),
oSource[p]);
oOwner[p].userDefined = true;
oOwner = iFlags;
iFlags = 0;
}
}
}
jsx.object.addProperties = addProperties;
if (!oOwner)
{
oOwner = this;
}
for (var p in oSource)
{
if (typeof oOwner[p] == "undefined" || (iFlags & jsx.object.ADD_OVERWRITE))
{
oOwner[p] = jsx.object.clone(
iFlags & (jsx.object.COPY_ENUM_DEEP | jsx.object.COPY_INHERIT),
oSource[p]);
oOwner[p].userDefined = true;
}
}
};
 
/**
* Lets one object inherit from another.
*
* @param o : optional Object
* Object from which to inherit.
* @return Object
* Reference to the child object.
*/
var inheritFrom = jsx.object.inheritFrom = (function() {
function Dummy() {}
return function(o) {
Dummy.prototype = o;
return new Dummy();
};
})();
 
/**
* Creates a duplicate (clone) of an object.
*
* @param iLevel : optional number
* Use the {@link Object#COPY_ENUM Object.COPY_*}
* properties to specify the level of cloning.
* @param oSource : optional Object
* @param oSource : optional Object
* Reference to the object to be cloned.
* If omitted, the calling object is cloned.
* @return Object
* A reference to the clone.
*/
function clone(iLevel, oSource)
{
var clone = jsx.object.clone = function(iLevel, oSource) {
if (typeof iLevel == "object")
{
oSource = iLevel;
170,10 → 260,10
if (!iLevel || (iLevel & jsx.object.COPY_ENUM_DEEP))
{
/* TODO: For objects, valueOf() only copies the object reference */
var o2 = oSource.valueOf(), c, i;
var o2 = oSource.valueOf(), i;
 
/* just in case "var i in ..." does not copy the array elements */
if (typeof Array != "undefined" && (c = o2.constructor) && c == Array)
if (typeof Array != "undefined" && o2.constructor == Array)
{
for (i = oSource.length; i--;)
{
204,23 → 294,19
}
else if (iLevel & jsx.object.COPY_INHERIT)
{
var Dummy = function() {};
Dummy.prototype = oSource;
return new Dummy();
return jsx.object.inheritFrom(oSource);
}
else
{
return null;
}
}
jsx.object.clone = clone;
};
 
/**
* @param o : Object
* @param iLength : number
* @param iLength : number
* Maximum property name length up to which an unused name
* is searched. The default is 256.
* is searched. The default is 256.
* @return string
* The name of a non-existing property of <code>o</code> if
* {@link Object#prototype.hasOwnProperty()} is supported, or
228,8 → 314,7
* if it is not supported; the empty string
* if there is no such property.
*/
function findNewProperty(o, iLength)
{
var findNewProperty = jsx.object.findNewProperty = function(o, iLength) {
if (!o)
{
o = this;
244,7 → 329,9
iLength = parseInt(iLength, 10);
}
 
var s = "";
var
s = "",
jsx_object = jsx.object;
while (s.length < iLength)
{
251,9 → 338,9
for (var i = "a".charCodeAt(0); i <= "z".charCodeAt(0); i++)
{
var c = String.fromCharCode(i);
if (!jsx.object._hasOwnProperty(o, s + c))
if (!jsx_object._hasOwnProperty(o, s + c + "_"))
{
return s + c;
return s + c + "_";
}
}
 
261,8 → 348,7
}
return "";
}
jsx.object.findNewProperty = findNewProperty;
};
 
/**
* Sets the handler for the proprietary <code>error</code> event.
276,16 → 362,15
* successfully, <code>false</code> otherwise. Note that one reason
* for failure can be that an event handler is no longer supported
* by the UA's DOM due to efforts towards adherence to Web standards.
*/
function setErrorHandler(fHandler)
{
*/
var setErrorHandler = jsx.setErrorHandler = function(fHandler) {
/*
* NOTE: There is no deadlock here because even if `fHandler' is a string,
* NOTE: There is no deadlock here because even if `fHandler' is a string,
* `isMethod(fHandler)' will call `setErrorHandler()' without arguments;
* so fHandler in that call will be `undefined' and `setErrorHandler()'
* is not called again.
*/
if (!isMethod(fHandler)) fHandler = jsx.clearErrorHandler;
if (!jsx.object.isMethod(fHandler)) fHandler = jsx.clearErrorHandler;
if (typeof window != "undefined" && typeof window.onerror != "undefined")
{
298,8 → 383,7
return (typeof window.onerror != "undefined"
&& window.onerror == fHandler);
}
jsx.setErrorHandler = setErrorHandler;
};
 
/**
* Clears the handler for the proprietary <code>error</code> event.
308,9 → 392,8
* optimizations in code reuse moved it here.
*
* @return boolean <code>true</code>
*/
function clearErrorHandler()
{
*/
var clearErrorHandler = jsx.clearErrorHandler = function () {
if (typeof window != "undefined" && window.onerror)
{
/*
321,11 → 404,10
}
 
return true;
}
jsx.clearErrorHandler = clearErrorHandler;
};
 
/**
* Wrapper for a safer <code>try</code>...<code>catch</code>.
* Wrapper for a safer <code>try</code>...<code>catch</code>.
*
* Attempts to evaluate a value as a <i>StatementList</i>, and attempts
* to evaluate another value as a <i>StatementList</i> if an exception
356,16 → 438,16
* </tr>
* </tbody>
* </table>
*
*
* NOTE: This method has previously been provided by {@link exception.js};
* optimizations in code reuse moved it here.
*
* @param statements
* @param statements
* Value to be evaluated as a <i>StatementList</i>.
* Called if a <code>Function</code> object reference, converted
* to string if not a string, and used as-is otherwise.
* For compatibility, the <code>undefined</code> value
* is evaluated like the empty string.
* is evaluated like the empty string.
* @param errorHandlers
* Value to be evaluated as a <i>StatementList</i> in case of an
* exception. Called if a <code>Function</code> object reference,
374,7 → 456,7
* is evaluated like the empty string.
* @return
* The result of <code>statements</code>, or the result
* of <code>errorHandlers</code> if an error occurred.
* of <code>errorHandlers</code> if an error occurred.
* @author
* Copyright (c) 2008
* Thomas 'PointedEars' Lahn &lt;js@PointedEars.de&gt;
381,8 → 463,7
* Distributed under the GNU GPL v3 and later.
* @partof JSX:object.js
*/
function tryThis(statements, errorHandlers)
{
var tryThis = jsx.tryThis = function(statements, errorHandlers) {
/**
* @param s Value to be stringified
* @param sIdent Identifier of the value to be stringified
409,46 → 490,45
+ 'catch (e) {\n ' + sErrorHandlers + '\n}';
return eval(code);
}
jsx.tryThis = tryThis;
};
 
/**
* Throws a qualified exception, including an execution context hint
* if provided, followed by an error message.
*
*
* NOTE: This method has previously been provided by {@link exception.js};
* optimizations in code reuse moved it here.
*
* @param errorType : string
* Identifier of the constructor for the error type
* @param sMessage : string
* Error message to be displayed
* @param sMessage : string|Array
* Error message to be displayed. If an <code>Array</code>,
* it is passed as argument list to the constructor for the error type
* @param context : Callable|string
* Optional callable object to specify the context
* where the exception occurred.
* @author
* @author
* Copyright (c) 2008 Thomas 'PointedEars' Lahn <cljs@PointedEars.de>.
* Distributed under the GNU GPL v3 and later.
* @partof JSX:object.js
* @see #isMethodType()
* @partof JSX:object.js
*/
function throwException(errorType, sMessage, context)
{
var t, sErrorType = errorType;
if (jsx.object.isMethodType((t = typeof errorType))
&& !/^\s*unknown\s*$/i.test(t)
&& errorType)
var throwException = jsx.throwThis = function(errorType, message, context) {
var
jsx_object = jsx.object;
sErrorType = errorType;
if (jsx_object.isMethod(errorType))
{
sErrorType = "(" + errorType + ")";
sErrorType = "errorType";
}
var sContext;
if (jsx.object.isMethod("Error"))
if (jsx_object.isMethod(jsx.global, "Error"))
{
var stack = (new Error()).stack;
if (stack)
{
sContext = stack + "\n";
sContext = stack + "\n";
}
}
455,58 → 535,107
/* DEBUG: set breakpoint here */
if (!sContext)
{
if (jsx.object.isMethodType((t = typeof context))
&& !/^\s*unknown\s*$/i.test(t)
&& context)
if (jsx.object.isMethod(context))
{
sContext = (String(context).match(/^\s*(function[^)]+\))/)
sContext = (String(context).match(/^\s*(function.+\))/)
|| [, null])[1];
sContext = sContext ? sContext + ': ' : '';
}
}
 
var message = (sContext || "") + (sMessage || "");
/* Array for exception constructor's argument list */
if (jsx_object.isMethod(message, "map"))
{
message = String(message.map(
function(e, i, a) {
return typeof e == "string" ? '"' + e + '"' : e;
})).replace(/\\/g, "\\$&").replace(/\r?\n|\r/g, "\\n");
}
else
{
message = (sContext || "") + (message || "");
message = '"'
+ message.replace(/["\\]/g, "\\$&").replace(/\r?\n|\r/g, "\\n")
+ '"';
}
 
/* DEBUG */
var throwStmt = ' throw new ' + sErrorType + '("'
+ message.replace(/["\\]/g, "\\$&").replace(/\r?\n|\r/g, "\\n")
+ '");';
var throwStmt = 'throw new ' + sErrorType + '(' + (message || "") + ');';
eval(throwStmt);
};
 
/**
* Returns a feature of an object
*
* @param o : Object
* @return mixed
* <code>false</code> if <var>o</var> does not have such a feature
*/
function getFeature(o)
{
for (var i = 1, len = arguments.length; i < len; i++)
{
var arg = arguments[i];
if (typeof o != "undefined" && typeof o[arg] != "undefined" && o[arg])
{
o = o[arg];
}
else
{
return false;
}
}
// eval(
// 'try'
// + '{'
// // throw so that we can try obtaining a stack trace
// + throwPrefix + '");'
// + '}'
// + 'catch (e)'
// + '{'
// + ' var stack = e.stack;'
// + ' if (typeof stack == "string")'
// + ' {'
// // re-throw with stack trace if available
// + throwPrefix + '\\nStack trace:" + stack.split("\\n").reverse().slice(0, stack.length - 3).join("\\n"));'
// + ' }'
// + ' else'
// + ' {'window.alert
// + throwPrefix + '");'
// + ' }'
// + '}');
return o;
}
jsx.throwException = throwException;
 
/* Alias; may replace the original later */
var throwThis = jsx.throwThis = throwException;
/**
* Implements the instanceof operator of JavaScript 1.5
* down to JavaScript 1.1 for <strong>one</strong> inheritance level.
* Example:
* <pre><code>
* var o = new Object();
* o instanceof Object; // yields `true'
*
* function Foo()
* {
* }
* var o = new Foo();
* o instanceof Object; // yields `true'
* o instanceof Foo; // yields `true' also
* isInstanceOf(o, Object); // yields `false'
* isInstanceOf(o, Foo); // yields `true'
* </code></pre>
*
* NOTE: This method has previously been provided by {@link types.js};
* optimizations in code reuse moved it here.
*
* @author
* (C) 2003 Thomas Lahn &lt;types.js@PointedEars.de&gt;
* @param a : Object
* Expression to be determined a <var>Prototype</var> object.
* @param Prototype : Object
* Function object to be determined the prototype of a.
* @return
* <code>true</code> if <code>a</code> is an object derived
* from <var>Prototype</var>, <code>false</code> otherwise.
*/
var isInstanceOf = jsx.object.isInstanceOf = function(a, Prototype) {
return !!(
a && Prototype
&& typeof a.constructor != "undefined"
&& a.constructor == Prototype);
};
 
/**
* Determines whether a property is likely to be callable.
* Determines whether an object is, or several objects are,
* likely to be callable.
*
* @author
* (C) 2003-2009 Thomas Lahn &lt;object.js@PointedEars.de&gt;
* @param o : Object|string
* Reference to an object or a primitive string value that evaluates
* to an object.
* @author (C) 2003-2009 Thomas Lahn &lt;object.js@PointedEars.de&gt;
* @param o : Object
* Reference to the object which should be tested for a method,
* or checked for being a method if no further arguments are provided.
* @params : optional string
* Path of the property to be determined a method, i.e. a reference to
* a callable object object assigned as property of another object.
513,54 → 642,126
* Use a string argument for each component of the path, e.g.
* the argument list <code>(o, "foo", "bar")</code> for testing whether
* <code>o.foo.bar</code> is a method.
* In addition if the last argument is an Array object reference,
* all elements of this array are used for property names. This
* allows for testing several properties of the same object with
* one call.
* @return boolean
* <code>true</code> if all arguments refer to methods,
* <code>false</code> otherwise.
* @see #isMethodType()
* @see jsx.object#isMethodType()
*/
function isMethod(o)
{
var len = arguments.length;
if (len < 1) return false;
var isMethod = jsx.object.isMethod = jsx.object.areMethods = (function() {
var
rxUnknown = /^\s*unknown\s*$/i,
rxMethod = /^\s*(function|object)\s*$/i,
sPropertyAccess = (
"^\\s*[A-Za-z_UNICODE$][\\wUNICODE$]*"
+ "(\\s*\\.\\.?\s*[A-Za-z_UNICODE$][\\wUNICODE$]*"
+ "|\\[[^]]+\\])*\\s*$"
)
/* Unicode support (from JavaScript 1.3 on) */
.replace(/UNICODE/g, ("\uFFFF".length == 1) ? "\\u0080-\\uFFFF" : ""),
rxPropertyAccess = new RegExp(sPropertyAccess);
/*
* Only consider strings that could be property accessors (incl. E4X)
* TODO: Unicode identifiers
*/
if (typeof o == "string"
&& /^\s*\w+(\s*\.\.?\s*\w+|\[[^]]+\])*\s*$/.test(o))
{
o = jsx.tryThis(o);
}
return function(o, p) {
var len = arguments.length;
if (len < 1)
{
jsx.throwThis(jsx.InvalidArgumentError,
["Not enough arguments", "saw 0", "(o : Object[, p : string])"]);
return false;
}
var rxUnknown = /^\s*unknown\s*$/i;
if (!(rxUnknown.test(typeof o) || o)) return false;
var t = typeof o;
/* When no property names are provided, test if the object is a method */
if (len < 2)
{
return rxUnknown.test(t) || rxMethod.test(t) && o && true || false;
}
for (var i = 1; i < len; i++)
{
var p = arguments[i];
var t = typeof o[p];
var rxMethod = /^\s*(function|object|unknown)\s*$/i;
if (rxMethod.test(t) && (rxUnknown.test(t) || o[p]))
/* Otherwise the first argument must refer to a suitable object */
if (rxUnknown.test(t) || !o) return false;
/*
* Refined support for strings; evaluating them always would
* preclude String objects from being tested for methods.
* Try to warn if a primitive string value is passed and the
* required flag is not set (default).
*/
if (t === "string")
{
if (i < len - 1)
if (arguments.callee.evalStrings)
{
o = o[p];
if (!(rxUnknown.test(typeof o) || o)) return false;
/* Only consider strings that could be property accessors (incl. E4X) */
if (rxPropertyAccess.test(o))
{
o = jsx.tryThis(o);
if (rxUnknown.test(typeof o) || !o) return false;
}
else
{
jsx.dmsg(
"jsx.object.isMethod: string does not look like"
+ " a property access; using it as-is",
"info");
}
}
else
{
jsx.dmsg(
"jsx.object.isMethod: Evaluation of strings requires"
+ " .evalStrings == true",
"warn");
}
}
else
for (var i = 1; i < len; i++)
{
return false;
var
p = arguments[i],
p_is_Array = (i === len - 1 && p != null && typeof p.valueOf() != "string"),
origP = p;
for (var j = (p_is_Array && origP.length || 1); j--;)
{
if (p_is_Array) p = origP[j];
t = typeof o[p];
/*
* NOTE: Test for "unknown" required in any case;
* evaluation order speeds up evaluation
*/
if (rxUnknown.test(t) || (rxMethod.test(t) && o[p]))
{
if (i < len - 1)
{
o = o[p];
if (!(rxUnknown.test(typeof o) || o)) return false;
}
}
else
{
return false;
}
}
}
}
return true;
}
jsx.object.isMethod = isMethod;
return true;
};
})();
 
/**
* Set this flag to <code>true</code> to allow evaluation
* of primitive strings as first argument. Allows for
* isMethod("foo", "bar") without testing the type of `foo' first.
* The default is <code>false</code>.
*/
isMethod.evalStrings = false;
 
/**
* NOTE: This method has previously been provided by {@link types.js};
* optimizations in code reuse moved it here.
*
579,15 → 780,13
* @author
* (C) 2003-2008 Thomas Lahn &lt;types.js@PointedEars.de&gt;
* Distributed under the GNU GPL v3 and later.
* @partof
* http://pointedears.de/scripts/types.js
* @see object.js.Global#isMethod()
* @partof http://pointedears.de/scripts/types.js
* @deprecated since version 0.1.5a.2009070204
* in favor of {@link jsx.object#isMethod(Object)}
*/
function isMethodType(s)
{
var isMethodType = jsx.object.isMethodType = function(s) {
return /^\s*(function|object|unknown)\s*$/i.test(s);
}
jsx.object.isMethodType = isMethodType;
};
 
/**
* @param o : optional Object
598,8 → 797,7
* <code>true</code> if there is such a property;
* <code>false</code> otherwise.
*/
function _hasOwnProperty(o, p)
{
var _hasOwnProperty = jsx.object._hasOwnProperty = function(o, p) {
if (arguments.length < 2 && o)
{
sProperty = o;
606,80 → 804,38
o = this;
}
 
/* see debug.js */
// printfire(o);
// printfire(sProperty);
return (jsx.object.isMethod(o, "hasOwnProperty")
? o.hasOwnProperty(p)
: (typeof o[p] != "undefined"));
};
 
/*
* BUG: "Unhandled exception on WrappedNative prototype object" in
* Firefox 1.5.0.1 and 2.0.0.7, cannot be handled with try..catch
*
* Stack trace
* ------------
* o.hasOwnProperty(sProperty)
* object.js:_hasOwnProperty
* debug.js:1347
* objinsp.js:showProperties
*/
 
/* ECMAScript Edition 3 */
if (jsx.object.isMethod(o, "hasOwnProperty"))
{
return o.hasOwnProperty(p);
}
 
/* Object itself *supposedly* doesn't have the property */
if (typeof o[p] == "undefined")
{
// JavaScript 1.0 to 1.3
if (typeof o.__proto__ != "undefined")
{
var hasP = false;
while ((o = o.__proto__))
{
if (typeof o[p] != "undefined")
{
hasP = true;
break;
}
}
 
return hasP;
}
 
/* other, incl. JScript 1.1 to 4.0 */
return (typeof o.constructor.prototype[p] == "undefined");
}
 
/* Object itself has the property */
return true;
}
jsx.object._hasOwnProperty = _hasOwnProperty;
 
/**
* @param o : Object
* @param p : string
* @param aDefault
* Retrieves the value of a property.
*
* @param o : Object
* @param sProperty : string
* @param aDefault : mixed
* @return mixed
* @throws Error
* @throw
* <code>jsx.object.</code>{@link PropertyError} if the property
* does not exist or has the <code>undefined</code> value, and
* <var>aDefault</var> was not provided
*/
function getattr(o, p, aDefault)
{
if (typeof o[p] != "undefined")
jsx.object.getProperty = function(o, sProperty, aDefault) {
if (typeof o[sProperty] != "undefined")
{
return o[p];
return o[sProperty];
}
else if (typeof aDefault != "undefined")
 
/* default value not passed */
if (arguments.length < 3)
{
return aDefault;
jsx.throwThis(this.PropertyError, sProperty);
}
else
{
throw new Error("unknown property: " + p);
}
}
jsx.object.getattr = getattr;
 
return aDefault;
};
 
/* Disabled until ECMAScript allows to hide properties from iteration */
//addProperties({
// addProperties : addProperties,
689,22 → 845,6
// },
// Object.prototype);
 
/**
* Inherits one object from another.
*
* @param o : optional Object
* Object from which to inherit.
* @return Object
* Reference to the child object.
*/
function inheritFrom(o)
{
function Dummy() {}
Dummy.prototype = o;
return new Dummy();
}
jsx.object.inheritFrom = inheritFrom;
 
if (jsx.object.isMethod(this, "eval"))
{
/*
721,24 → 861,35
* of a different object (the calling object).
*
* @prototype method
* @param thisArg : object
* @param thisArg : object
* Reference to the calling object.
* @param argArray : Array
* Arguments for the object.
*/
apply: function(thisArg, argArray) {
var a = new Array();
for (var i = 0, len = argArray.length; i < len; i++)
var jsx_object = jsx.object;
if (jsx_object.isMethod(Array, "prototype", "map", "call"))
{
a[i] = "argArray[" + i + "]";
var a = Array.prototype.map.call(argArray, function(e, a, i) {
return "argArray[" + i + "]";
});
}
else
{
a = new Array();
for (var i = 0, len = argArray.length; i < len; i++)
{
a[i] = "argArray[" + i + "]";
}
}
if (!thisArg) thisArg = _global;
var o = new Object(), p = jsx.object.findNewProperty(o);
if (!thisArg) thisArg = jsx.global;
var o = new Object(), p = jsx_object.findNewProperty(o);
if (p)
{
o[p] = this;
eval("o[p](" + a.join(", ") + ")");
eval("o[p](" + a + ")");
delete o[p];
o = null;
}
756,20 → 907,58
*/
call: function(thisArg) {
var a = new Array();
for (var i = 1, len = arguments.length; i < len; i++)
{
a[i] = "arguments[" + i + "]";
}
if (!thisArg) thisArg = _global;
var o = new Object(), p = jsx.object.findNewProperty(o);
if (!thisArg) thisArg = jsx.global;
var
o = new Object(),
p = jsx.object.findNewProperty(o);
if (p)
{
o[p] = this;
eval("o[p](" + a.slice(1).join(", ") + ")");
eval("o[p](" + a + ")");
delete o[p];
o = null;
}
},
/**
* Constructs a new object using the calling object as constructor
* and elements of the referred array as items of the arguments list.
*
* Example:
* <code>var d = Date.construct([2009, 8, 1]);</code>
* is equivalent to
* <code>var d = new Date(2009, 8, 1);</code>
* but, by contrast, allows for passing an arbitrary number of
* arguments per the array's elements.
*
* @param argArray : Array
* @type Object
* @return the newly constructed object
*/
construct: function(argArray) {
if (jsx.object.isMethod(Array, "prototype", "map", "call"))
{
var a = Array.prototype.map.call(argArray, function(e, i, a) {
return "argArray[" + i + "]";
});
}
else
{
a = new Array();
for (var i = 0, len = argArray.length; i < len; i++)
{
a[i] = "argArray[" + i + "]";
}
}
return eval("new this(" + a + ")");
}
},
Function.prototype);
777,17 → 966,18
 
/**
* Includes the prototype object of another object
* in the prototype chain of objects created through
* the current Function object.
* in the prototype chain of objects created with
* the calling Function object.
*
* Used with constructors to establish prototype-based
* inheritance (much like class-based inheritance in Java).
* Be sure to call the parent's constructor then within
* the constructor of the child, using the call() method
* (or calling it as a method of the inheriting prototype),
* else changes in the parent will not affect the child.
* Be sure to call the parent's constructor explicitly then within
* the constructor of the child, using the
* <code>arguments.callee._super.call()</code> method (or calling it
* explicitly as a method of the inheriting prototype), else changes
* in the parent will not affect the child.
*
* @param Constructor : Function
* @param Constructor : Function
* Constructor from which prototype object should be
* inherited.
* @param oProtoProps : Object
795,37 → 985,18
* properties. Of those, the <code>_super</code>,
* <code>constructor</code>, and <code>userDefined</code>
* properties are ignored as they are used internally.
* @return boolean
* <code>true</code> if successful, <code>false</code> otherwise.
* @return Object
* A reference to the constructor of the extended prototype object
* if successful; <code>null</code> otherwise.
*/
Function.prototype.extend =
function function_extend(Constructor, oProtoProps) {
function Dummy() {};
Function.prototype.extend = (function() {
function Dummy() {}
if (typeof Constructor.valueOf() == "string")
function iterator()
{
Constructor = _global[Constructor];
}
if (typeof Constructor != "function") return false;
 
Dummy.prototype = Constructor.prototype;
this.prototype = new Dummy();
if (oProtoProps)
{
for (var p in oProtoProps)
{
this.prototype[p] = oProtoProps[p];
}
}
this.prototype._super = Constructor;
this.prototype.constructor = this;
this.userDefined = true;
/* PERF: for (var p in o.iterator()) is rather inefficient */
this.prototype.iterator = function() {
jsx.dmsg("for (var p in o.iterator()) { f(); } is inefficient,"
+ " consider using o.forEach(f, ...) instead", "warn");
var o = new Object();
for (var p2 in this)
837,104 → 1008,216
case "userDefined":
case "iterator":
case "forEach":
break;
break;
default:
o[p2] = true;
}
}
}
 
return o;
};
}
if (!jsx.object.isMethod(this.prototype, "forEach"))
function forEach(fCallback, thisObj)
{
var t = typeof fCallback;
if (!jsx.object.isMethod(fCallback))
{
jsx.throwThis(
"TypeError",
(!/^\s*unknown\s*$/i.test(t) ? fCallback : "arguments[0]")
+ " is not a function",
this + ".forEach");
}
for (var p in this)
{
switch (p)
{
case "_super":
case "constructor":
case "userDefined":
case "iterator":
case "forEach":
break;
default:
/* also supports host object's methods */
Function.prototype.call.call(fCallback, thisObj, this[p], p, this);
}
}
}
return function(Constructor, oProtoProps) {
/*
* Supports strings being passed that denote properties of the
* Global Object.
*
* TODO: An API that only registers strings as references to features
* defined later, and implements inheritance using this registry
* on user call only, might be useful for constructors defined
* in Object initializers.
*/
if (typeof Constructor.valueOf() == "string")
{
Constructor = jsx.global[Constructor];
}
var t = typeof Constructor;
if (t != "function")
{
jsx.throwThis("TypeError",
(/\s*unknown\s*/i.test(t) ? "Unknown" : t) + " is not a function");
return null;
}
Dummy.prototype = Constructor.prototype;
this.prototype = new Dummy();
if (oProtoProps)
{
for (var p in oProtoProps)
{
this.prototype[p] = oProtoProps[p];
}
}
this._super = Constructor;
this.prototype._super = Constructor.prototype;
this.prototype.constructor = this;
this.userDefined = true;
/* PERF: for (var p in o.iterator()) is rather inefficient */
/**
* Calls a function for each real property of the object
* in arbitrary order. Workaround for for..in iteration
* on objects with augmented prototype object.
* @return Object
* @deprecated
*/
this.prototype.iterator = iterator;
if (!jsx.object.isMethod(this.prototype, "forEach"))
{
/**
* Calls a function for each real property of the object
* in arbitrary order. Workaround for for-in iteration
* on objects with augmented prototype object.
*
* @param fCallback : Function
* @param thisObj : optional Object
* @throws TypeError
*/
this.prototype.forEach = forEach;
}
return this;
};
})();
 
jsx.object.addProperties(
{
/**
* Maps one array to another
*
* @param f : Function
* @param thisObj : optional Object
* @throws TypeError
* @param f : Callable
* @param oThis : optional Object
* @return The original array with <var>f</var> applied to each element.
*/
this.prototype.forEach = function(fCallback, thisObj) {
var t;
if (!jsx.object.isMethodType((t = typeof fCallback)) || !fCallback)
map: function(f, oThis) {
var jsx_object = jsx.object;
if (!jsx_object.isMethod(f))
{
jsx.throwException(
"TypeError",
(!/^\s*unknown\s*$/i.test(t) ? fCallback : "arguments[0]")
+ " is not a function",
this + ".forEach");
jsx.throwThis("TypeError",
(jsx_object.isMethod(f, "toSource") ? f.toSource() : f)
+ " is not callable",
this + ".map");
}
for (var p in this)
var
len = this.length >>> 0,
res = [];
for (var i = 0; i < len; i++)
{
switch (p)
{
case "_super":
case "constructor":
case "userDefined":
case "iterator":
case "forEach":
break;
default:
fCallback.call(thisObj, this[p], p, this);
}
res[i] = f.call(oThis, this[i], i, this);
}
};
}
return true;
};
return res;
}
},
Array.prototype);
/**
* @param s : string
* @param sType : string
* @param sMsg : string
*/
function Exception(s, sType)
{
var e = new Error((sType || "Exception") + ": " + s);
e.getMessage = function() { return this.message; };
e.getStackTrace = function() { return this.stack; };
e.printStackTrace = function() { window.alert(this.getStackTrace()); };
return e;
}
jsx.Exception = Exception;
var Exception = jsx.Error = (
function(sMsg) {
var msg = (sMsg || "Unspecified error");
var _super = arguments.callee._super;
if (typeof _super == "function")
{
_super.call(this, msg);
var e;
jsx.tryThis(function() { e = new _super(); });
}
if (!this.message) this.message = msg;
if (!this.lineNumber && e)
{
this.lineNumber = e.lineNumber;
}
if (!this.stack && e && e.stack)
{
var stack = String(e.stack).split(/\r?\n|\r/);
stack.shift();
this.stack = stack.join("\n");
}
}
).extend(typeof Error != "undefined" ? Error : function() {}, {
name: "jsx.Error",
getMessage: function() { return this.message; },
getStackTrace: function() { return this.stack; },
printStackTrace: function() {
var s = this.getStackTrace();
jsx.dmsg(s) || window.alert(s);
}
});
 
jsx.InvalidArgumentError = (
function(sReason, sGot, sExpected) {
arguments.callee._super.call(this,
(sReason || "Invalid argument(s)")
+ (sGot ? ": " + sGot : "")
+ (sExpected ? "; expected " + sExpected : ""));
}
).extend(jsx.Error, {
name: "jsx.InvalidArgumentError"
});
 
/**
* @param s
* @extends Exception
* @extends jsx.Error
*/
function ObjectException(s)
{
return jsx.Exception(s, "ObjectException");
}
jsx.object.ObjectException = ObjectException;
jsx.object.ObjectError = (
function(s) {
arguments.callee._super.call(this, s);
}
).extend(jsx.Error, {
name: "jsx.object.ObjectError"
});
 
/**
* Raises an ObjectException
*
* @param sMsg : optional string
* @return boolean <code>false</code>
* @param s
* @extends jsx.object.ObjectError
*/
function objectException(sMsg)
{
window.alert(
"object.js "
+ jsx.object.version
+ "\n"
+ jsx.object.copyright
+ " "
+ jsx.object.author
+ " <"
+ jsx.object.email
+ ">\n\n"
+ sMsg);
 
return false;
}
objectException.userDefined = true;
jsx.object.objectException = objectException;
jsx.object.PropertyError = (
function(s) {
arguments.callee._super.call(
this, "No such property" + (arguments.length > 0 ? ": '" + s + "'" : ""));
}
).extend(jsx.object.ObjectError, {
name: "jsx.object.PropertyError"
});