/*global window, document*/
/*jslint bitwise: true, unparam: true, regexp: true*/
(function (window, document) {
'use strict';
var BytePushers;
if (window.BytePushers !== undefined && window.BytePushers !== null) {
BytePushers = window.BytePushers;
} else {
window.BytePushers = {};
BytePushers = window.BytePushers;
}
/****************************************************************************************************
* BEGIN Array Extensions */
if (!Array.prototype.every) {
Array.prototype.every = function (fun, funParameter) {
var t = Object.create(this),
len = t.length >>> 0,
i;
if (this === null) {
throw new TypeError();
}
if (typeof fun !== "function") {
throw new TypeError();
}
for (i = 0; i < len; i = i + 1) {
if (t.hasOwnProperty(i) && !fun.call(funParameter, t[i], i, t)) {
return false;
}
}
return true;
};
}
// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.com/#x15.4.4.18
if (!Array.prototype.forEach) {
Array.prototype.forEach = function forEach(callback, thisArg) {
var T, k, O, len, obj = {}, kValue;
if (this === null) {
throw new TypeError("this is null or not defined");
}
// 1. Let O be the result of calling ToObject passing the |this| value as the argument.
O = Object.create(this);
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
// 3. Let len be ToUint32(lenValue).
len = O.length >>> 0; // Hack to convert O.length to a UInt32
// 4. If IsCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (obj.toString.call(callback) !== "[object Function]") {
throw new TypeError(callback + " is not a function");
}
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
if (thisArg) {
T = thisArg;
}
// 6. Let k be 0
k = 0;
// 7. Repeat, while k < len
while (k < len) {
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (Object.prototype.hasOwnProperty.call(O, k)) {
// i. Let kValue be the result of calling the Get internal method of O with argument Pk.
kValue = O[k];
// ii. Call the Call internal method of callback with T as the this value and
// argument list containing kValue, k, and O.
callback.call(T, kValue, k, O);
}
// d. Increase k by 1.
k = k + 1;
}
// 8. return undefined
};
}
if (!Array.prototype.some) {
Array.prototype.some = function (fun, functionParameter) {
var t = Object.create(this),
len = t.length >>> 0,
i;
if (this === null) {
throw new TypeError();
}
if (typeof fun !== "function") {
throw new TypeError();
}
for (i = 0; i < len; i = i + 1) {
if (t.hasOwnProperty(i) && fun.call(functionParameter, t[i], i, t)) {
return true;
}
}
return false;
};
}
if (!Array.prototype.isArray) {
Array.prototype.isArray = function (arg) {
var targetArray = (arg === true) ? arg : this;
return Object.prototype.toString.call(targetArray) === "[object Array]";
};
}
/* END Array Extensions *
****************************************************************************************************/
/****************************************************************************************************
* BEGIN Date Extensions */
/**
* <p>Function that is used to determine if two dates objects have the same date.</p>
* @function
* @param {@link Date} The date to evaluate against this object.
* @return <a href="http://www.w3schools.com/jsref/jsref_obj_boolean.asp">Boolean</a> True if the date passed in is equal the date object; otherwise return false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.prototype.isDateEqualTo = function (date) {
if (this.getFullYear() === date.getFullYear()) {
if (this.getMonth() === date.getMonth()) {
if (this.getDate() === date.getDate()) {
return true;
}
}
}
return false;
};
/**
* <p>Function that is used to determine if two dates objects have the same date and time.</p>
* @function
* @param {@link Date} The date to evaluate against this object.
* @return {@link <a href="http://www.w3schools.com/jsref/jsref_obj_boolean.asp">Boolean</a>} True if the date passed in is equal the date object; otherwise return false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.prototype.isDateEqualToDateAndTime = function (date) {
if (this.getFullYear() === date.getFullYear()) {
if (this.getMonth() === date.getMonth()) {
if (this.getDate() === date.getDate()) {
if (this.getHours() === date.getHours()) {
if (this.getMinutes() === date.getMinutes()) {
return true;
}
}
}
}
}
return false;
};
/**
* <p>Function that is used to determine a date is the day after another date.</p>
* @function
* @param {@link Date} The date to evaluate against this object.
* @return <a href="http://www.w3schools.com/jsref/jsref_obj_boolean.asp">Boolean</a> True if the date is the day after the original date; otherwise return false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.prototype.isDateEqualToTomorrow = function (date) {
if (this.getFullYear() === date.getFullYear()) {
if (this.getMonth() === date.getMonth()) {
if (this.getDate() + 1 === date.getDate()) {
return true;
}
} else if (this.getMonth() + 1 === date.getMonth()) {
if (this.isLastDayInMonth() && date.getDate() === 1) {
return true;
}
}
} else if (this.getFullYear() + 1 === date.getFullYear()) {
if (this.getMonth() === 11 && date.getMonth() === 0) {
if (this.getDate() === 31 && date.getDate() === 1) {
return true;
}
}
}
return false;
};
/**
* <p>Function that is used to determine a date is the day before another date.</p>
* @function
* @param {@link Date} The date to evaluate against this object.
* @return <a href="http://www.w3schools.com/jsref/jsref_obj_boolean.asp">Boolean</a> True if the date is the day before the original date; otherwise return false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.prototype.isDateEqualToYesterday = function (date) {
if (this.getFullYear() === date.getFullYear()) {
if (this.getMonth() === date.getMonth()) {
if (this.getDate() - 1 === date.getDate()) {
return true;
}
} else if (this.getMonth() === date.getMonth() + 1) {
if (this.getDate() === 1 && date.isLastDayInMonth()) {
return true;
}
}
} else if (this.getFullYear() - 1 === date.getFullYear()) {
if (this.getMonth() === 0 && date.getMonth() === 11) {
if (this.getDate() === 1 && date.getDate() === 31) {
return true;
}
}
}
return false;
};
/**
* <p>Tells you whether it is the last day in a month or not.</p>
* @private
* @returns <a href="http://www.w3schools.com/jsref/jsref_obj_boolean.asp">Boolean</a> True if it is the last day of the month, otherwise false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.prototype.isLastDayInMonth = function () {
var lastDayInMonth = this.getCurrentMonthTotalDays();
if (this.getDate() === lastDayInMonth) {
return true;
}
return false;
};
/**
* <p>Function that is used to get calendar total calendar days of the previous month.</p>
* @function
* @returns <a href="http://www.w3schools.com/jsref/jsref_obj_number.asp">Number</a> The total days in the previous month.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.prototype.getPreviousMonthTotalDays = function () {
if (this.getMonth() === 0) {
return Date.monthNames[11].getTotalDays(this.getFullYear());
}
return Date.monthNames[this.getMonth() - 1].getTotalDays(this.getFullYear());
};
/**
* <p>Function that is used to get the total calendar days of the next month.</p>
* @function
* @returns <a href="http://www.w3schools.com/jsref/jsref_obj_number.asp">Number</a> The total days in the next month.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.prototype.getNextMonthTotalDays = function () {
if (this.getMonth() === 11) {
return Date.monthNames[0].getTotalDays(this.getFullYear());
}
return Date.monthNames[this.getMonth() + 1].getTotalDays(this.getFullYear());
};
/**
* <p>Function that is used to get the total calendar days of the next month.</p>
* @function
* @returns <a href="http://www.w3schools.com/jsref/jsref_obj_number.asp">Number</a> The total days in the next month.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.prototype.getCurrentMonthTotalDays = function () {
if (this.getMonth() === 11) {
return Date.monthNames[0].getTotalDays(this.getFullYear());
}
return Date.monthNames[this.getMonth()].getTotalDays(this.getFullYear());
};
/**
* <p>Adds time to a date object.</p>
* @param <a href="http://www.w3schools.com/jsref/jsref_obj_number.asp">Number</a> time Represents the time you want to add to the date.
*
* @returns {String} A new Date object with the specified time added.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.prototype.addTime = function (time) {
var newDate = new Date(),
wholeNumber = (time > 0) ? Math.floor(time) : Math.ceil(time),
fraction = ((time - wholeNumber).toFixed(2) * 100),
hourInMilliseconds = 1000 * 60 * 60 * wholeNumber,
minutesInMilliseconds = 1000 * 60 * fraction;
newDate.setTime(this.getTime());
newDate.setTime(newDate.getTime() + hourInMilliseconds);
newDate.setTime(newDate.getTime() + minutesInMilliseconds);
return newDate;
};
/**
* <p>Static function that tells you whether a date is the last day in a month or not.</p>
* @private
* @static
* @param <a href="http://www.w3schools.com/jsref/jsref_obj_date.asp">Number</a> time Represents the time you want to add to the date.
* @returns <a href="http://www.w3schools.com/jsref/jsref_obj_boolean.asp">Boolean</a> True if it is the last day of the month, otherwise false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.isLastDayInMonth = function (date) {
var lastDayInMonth = date.getCurrentMonthTotalDays();
if (date.getDate() === lastDayInMonth) {
return true;
}
return false;
};
/**
* <p>Static function that gets month name.</p>
* @private
* @static
* @param <a href="http://www.w3schools.com/jsref/jsref_obj_number.asp">Number</a> index Represents the position of the month in a month array.
* @param <a href="http://www.w3schools.com/jsref/jsref_obj_boolean.asp">Boolean</a> useAbbr An optional boolean flag that governs whether the
* full name of the month is returned or its abbreviation.
* @returns {String} The name of the month.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.getMonthName = function (index, getAbbr) {
if (getAbbr) {
return this.monthNames[index].abbr;
}
return this.monthNames[index].name;
};
/**
* <p>Static field for the list of month.</p>
* @static
* @field
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Date.monthNames = [
{"name": "January", "abbr": "Jan", "getTotalDays": function () { return 31; } },
{"name": "February", "abbr": "Feb", "getTotalDays": function (year) {
if (year) {
return (year % 4 === 0) ? 29 : 28;
}
throw ("Expected parameter(Year) is not defined.");
}},
{"name": "March", "abbr": "Mar", "getTotalDays": function () { return 31; }},
{"name": "April", "abbr": "Apr", "getTotalDays": function () { return 30; }},
{"name": "May", "abbr": "May", "getTotalDays": function () { return 31; }},
{"name": "June", "abbr": "Jun", "getTotalDays": function () { return 30; }},
{"name": "July", "abbr": "Jul", "getTotalDays": function () { return 31; }},
{"name": "August", "abbr": "Aug", "getTotalDays": function () { return 31; }},
{"name": "September", "abbr": "Sep", "getTotalDays": function () { return 30; }},
{"name": "October", "abbr": "Oct", "getTotalDays": function () { return 31; }},
{"name": "November", "abbr": "Nov", "getTotalDays": function () { return 30; }},
{"name": "December", "abbr": "Dec", "getTotalDays": function () { return 31; }}
];
/* END Date Extensions *
****************************************************************************************************/
/****************************************************************************************************
* BEGIN Object Extensions */
/**
* <p>Static function that tells you whether an object is an array or not.</p>
* @static
* @param <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures">Object of some type</a> The object that will be tested to see if it is an array.
* @returns <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">Boolean</a> True if an object is an array, otherwise false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Object.isArray = function (someArray) {
var result = false;
if (Object.isDefined(someArray)) {
if (someArray.constructor.toString().indexOf("Array") > -1) {
result = true;
}
}
return result;
};
/**
* <p>Static function that tells you whether an object is a date or not.</p>
* @static
* @param <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures">Object of some type</a> The object that will be tested to see if it is a date.
* @returns <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">Boolean</a> True if an object is an date, otherwise false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Object.isDate = function (someDate) {
var result = false;
if (Object.isDefined(someDate)) {
if (typeof someDate === "object" && someDate instanceof Date) {
result = true;
}
}
return result;
};
/**
* <p>Static function that tells you whether an object is a string or not.</p>
* @static
* @param <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures">Object of some type</a> The object that will be tested to see if it is a string.
* @returns <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">Boolean</a> True if an object is an string, otherwise false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Object.isString = function (someString) {
var result = false;
if (Object.isDefined(someString)) {
if (typeof someString === "string" || (typeof someString === "object" && someString instanceof String)) {
result = true;
}
}
return result;
};
/**
* <p>Static function that tells you whether an object is numeric or not.</p>
* @static
* @param <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures">Object of some type</a> The object that will be tested to see if it is numeric.
* @returns <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">Boolean</a> True if an object is numeric, otherwise false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Object.isNumeric = function (someNumber) {
var result = false;
if (Object.isDefined(someNumber)) {
if (typeof someNumber === "number" || (typeof someNumber === "object" && someNumber instanceof Number)) {
result = true;
}
}
return result;
};
/**
* <p>Static function that tells you whether an object is a boolean or not.</p>
* @static
* @param <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures">Object of some type</a> The object that will be tested to see if it is a boolean.
* @returns <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">Boolean</a> True if an object is a boolean, otherwise false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Object.isBoolean = function (someBoolean) {
var result = false;
if (Object.isDefined(someBoolean)) {
if (typeof someBoolean === "boolean" || (typeof someBoolean === "object" && someBoolean instanceof Boolean)) {
result = true;
}
}
return result;
};
/**
* <p>Static function that tells you whether an object is defined or not.</p>
* @static
* @param <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures">Object of some type</a> The object that will be tested to see if it is defined.
* @returns <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean">Boolean</a> True if an object is defined, otherwise false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
Object.isDefined = function (target) {
var result = false;
if (target !== undefined && target !== null) {
result = true;
}
return result;
};
/* END Object Extensions *
****************************************************************************************************/
/****************************************************************************************************
* BEGIN String Extensions */
/**
* <p>Function that is used to trim the white spaces from the beginning and end of the string.</p>
* @function
* @return <a href="http://www.w3schools.com/jsref/jsref_obj_string.asp">String</a> The value of the string after it has been trimmed.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
String.prototype.trim = function () {
return this.replace(/^\s+|\s+$/g, '');
};
/**
* <p>Function that is used to determine if a string includes a certain character or string.</p>
* @function
* @param <a href="http://www.w3schools.com/jsref/jsref_obj_string.asp">String</a> The string we are checking if is included.
* @return <a href="http://www.w3schools.com/jsref/jsref_obj_boolean.asp">Boolean</a> True of the string is included, otherwise false.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
if (!String.prototype.includes) {
String.prototype.includes = function () {
return String.prototype.indexOf.apply(this, arguments) !== -1;
};
}
/**
* <p>Function that is used to format a sentence to camel case. (e.g. Hello world => helloWorld).</p>
* @function
* @return <a href="http://www.w3schools.com/jsref/jsref_obj_string.asp">String</a> The value of the string after it has been formatted to camel case.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
String.prototype.toCamelCase = function () {
return this.replace(/^([A-Z])|\s(\w)/g, function (match, p1, p2) {
if (p2) {
return p2.toUpperCase();
}
return p1.toLowerCase();
});
};
/**
* <p>Function that is used to turn a string that is in camel case format to a Normal sentence format. (e.g. helloWorld => Hello World)</p>
* @function
* @return <a href="http://www.w3schools.com/jsref/jsref_obj_string.asp">String</a> The value of the string after it has been formatted to a normal sentence format.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
String.prototype.toNormalCase = function () {
return this.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/([A-Z])([A-Z])/g, '$1 $2').replace(/^./, function (str) {return str.toUpperCase(); });
};
/**
* <p>Convenience function that will format a string with dynamic variables.</p>
* @static
* @param {...string} string - first argument is the string to be formatted. The remaining arguments are the format items (e.g. "{0}")
* @function
* @return <a href="http://www.w3schools.com/jsref/jsref_obj_string.asp">String</a> The value of the string after it has been formatted.
* @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a>
*/
String.format = function (someString) {
// The string containing the format items (e.g. "{0}")
// will and always has to be the first argument.
var theString = someString, i, regEx;
// start with the second argument (i = 1)
for (i = 0; i < arguments.length; i = i + 1) {
// "gm" = RegEx options for Global search (more than one instance)
// and for Multiline search
regEx = new RegExp("\\{" + i + "\\}", "gm");
theString = theString.replace(regEx, arguments[i]);
}
return theString;
};
/* END String Extensions *****************************************************************************************************/
BytePushers.namespace = function (ns_string) {
var parts = ns_string.split('.'), parent = BytePushers;
// strip redundant leading global
if (parts[0] === "BytePushers") {
parts = parts.slice(1);
}
parts.forEach(function (part, index) {
// create a property if it doesn't exist
if (parent[part] === undefined) {
parent[part] = {};
}
parent = parent[part];
});
return parent;
};
/**
* inherit() returns a newly created object that inherits properties from the prototype object p.
* It uses the ECMAScript 5 function Object.create() if it is defined, and otherwise falls.
*
* @param p
* @returns {*}
*/
BytePushers.inherit = function (p) {
var t;
if (p === null) { // p must be non-null object
throw new TypeError();
}
if (Object.create) { // if Object.create() is defined...
return Object.create(p); // then just use it.
}
t = typeof p; // Otherwise do some more type checking
if (t !== "object" && t !== "function") {
throw new TypeError();
}
function F() {// Define a dummy constructor function.
return;
}
F.prototype = p; // Set its prototype property to p.
return new F(); // Use f() to create an "heir" of p.
};
/**
* defineClass() -- a utility function for defining JavaScript classes.
*
* This function expects a single object as its only argument. It defines
* a new JavaScript class based on the data in that object and returns the
* constructor function of the new class. This function handles the repetitive
* tasks of defining classes: setting up the prototype object for correct
* inheritance, copying methods from other types, and so on.
*
* The object passed as an argument should have some or all of the
* following properties:
*
* name: the name of the class being defined.
* If specified, this value will be stored in the classname
* property of the prototype object.
*
* extend: The constructor of the class to be extended. If omitted,
* the Object() constructor will be used. This value will
* be stored in the superclass property of the prototype object.
*
* construct: The constructor function for the class. If omitted, a new
* empty function will be used. This value becomes the return
* value of the function, and is also stored in the constructor
* property of the prototype object.
*
* methods: An object that specifies the instance methods (and other shared
* properties) for the class. The properties of this object are
* copied into the prototype object of the class. If omitted,
* an empty object is used instead. Properties named
* "classname", "superclass", and "constructor" are reserved
* and should not be used in this object.
*
* statics: An object that specifies the static methods (and other static
* properties) for the class. The properties of this object become
* properties of the constructor function. If omitted, an empty
* object is used instead.
*
* borrows: A constructor function or array of constructor functions.
* The instance methods of each of the specified classes are copied
* into the prototype object of this new class so that the
* new class borrows the methods of each specified class.
* Constructors are processed in the order they are specified,
* so the methods of a class listed at the end of the array may
* overwrite the methods of those specified earlier. Note that
* borrowed methods are stored in the prototype object before
* the properties of the methods object above. Therefore,
* methods specified in the methods object can overwrite borrowed
* methods. If this property is not specified, no methods are
* borrowed.
*
* provides: A constructor function or array of constructor functions.
* After the prototype object is fully initialized, this function
* verifies that the prototype includes methods whose names and
* number of arguments match the instance methods defined by each
* of these classes. No methods are copied; this is simply an
* assertion that this class "provides" the functionality of the
* specified classes. If the assertion fails, this method will
* throw an exception. If no exception is thrown, any
* instance of the new class can also be considered (using "duck
* typing") to be an instance of these other types. If this
* property is not specified, no such verification is performed.
**/
BytePushers.defineClass = function (data) {
// Extract the fields we'll use from the argument object.
// Set up default values.
var classname = data.name,
Superclass = data.extend || Object,
constructor = data.construct || function () {return; },
methods = data.methods || {},
statics = data.statics || {},
borrows,
provides,
proto,
i1,
i2,
c1,
c2,
p1,
p2,
p3,
p4,
p5;
// Borrows may be a single constructor or an array of them.
if (!data.borrows) {
borrows = [];
} else if (data.borrows instanceof Array) {
borrows = data.borrows;
} else {
borrows = [ data.borrows ];
}
// Ditto for the provides property.
if (!data.provides) {
provides = [];
} else if (data.provides instanceof Array) {
provides = data.provides;
} else {
provides = [ data.provides ];
}
// Create the object that will become the prototype for our class.
proto = new Superclass();
// Delete any noninherited properties of this new prototype object.
for (p1 in proto) {
if (proto.hasOwnProperty(p1)) {
delete proto[p1];
}
}
// Borrow methods from "mixin" classes by copying to our prototype.
for (i1 = 0; i1 < borrows.length; i1 = i1 + 1) {
c1 = data.borrows[i1];
borrows[i1] = c1;
// Copy method properties from prototype of c to our prototype
for (p2 in c1.prototype) {
if (typeof c1.prototype[p2] === "function") {
proto[p2] = c1.prototype[p2];
}
}
}
// Copy instance methods to the prototype object
// This may overwrite methods of the mixin classes
for (p3 in methods) {
if (methods.hasOwnProperty(p3)) {
proto[p3] = methods[p3];
}
}
// Set up the reserved "constructor", "superclass", and "classname"
// properties of the prototype.
proto.constructor = constructor;
proto.Superclass = Superclass;
// classname is set only if a name was actually specified.
if (classname) {
proto.classname = classname;
}
// Verify that our prototype provides all of the methods it is supposed to.
for (i2 = 0; i2 < provides.length; i2 = i2 + 1) { // for each class
c2 = provides[i2];
for (p4 in c2.prototype) { // for each property
if (typeof c2.prototype[p4] === "function" && (p4 === "constructor" || p4 === "superclass")) { //methods only
// Check that we have a method with the same name and that
// it has the same number of declared arguments. If so, move on
if (proto.hasOwnProperty(p4) && typeof proto[p4] !== "function" && proto[p4].length !== c2.prototype[p4].length) {
// Otherwise, throw an exception
throw new Error("Class " + classname + " does not provide method " + c2.classname + "." + p4);
}
}
}
}
// Associate the prototype object with the constructor function
constructor.prototype = proto;
// Copy static properties to the constructor
for (p5 in statics) {
if (statics.hasOwnProperty(p5)) {
constructor[p5] = statics[p5];
}
}
// Finally, return the constructor function
return constructor;
};
BytePushers.isArrayLike = function (x) {
if (x instanceof Array) { // Real arrays are array-like
return true;
}
if (!x.hasOwnProperty("length")) { // Arrays must have a length property
return false;
}
if (typeof x.length !== "number") { // Length must be a number
return false;
}
if (x.length < 0) { // and nonnegative
return false;
}
if (x.length > 0) {
// If the array is nonempty, it must at a minimum
// have a property defined whose name is the number length-1
if (!x.hasOwnProperty((x.length - 1))) {
return false;
}
}
return true;
};
// Return true if O has methods with the same name and arity as all
// methods in I.prototype. Otherwise, return false. Throws an exception
// if I is a built-in type with nonenumerable methods.
BytePushers.provides = function (O, I) {
var proto = I.prototype,
p6;
// If O actually is an instance of I, it obviously looks like I
if (O instanceof I) {
return true;
}
// If a constructor was passed instead of an object, use its prototype
if (typeof O === "function") {
O = O.prototype;
}
// The methods of built-in types are not enumerable, and we return
// undefined. Otherwise any object would appear to provide any of
// the built-in types.
if (I === Array || I === Boolean || I === Date || I === Error || I === Function || I === Number || I === RegExp || I === String) {
return undefined;
}
for (p6 in proto) { // Loop through all properties in I.prototype
// Ignore properties that are not functions
if (typeof proto[p6] === "function") {
// If O does not have a property by the same name return false
if (!(O.hasOwnProperty(p6))) {
return false;
}
// If that property is not a function, return false
if (typeof O[p6] !== "function") {
return false;
}
// If the two functions are not declared with the same number
// of arguments return false.
if (O[p6].length !== proto[p6].length) {
return false;
}
}
}
// If all the methods check out, we can finally return true.
return true;
};
// This function creates a new enumerated type. The argument object specifies // the names and values of each instance of the class. The return value
// is a constructor function that identifies the new class. Note, however
// that the constructor throws an exception: you can't use it to create new
// instances of the type. The returned constructor has properties that // map the name of a value to the value itself, and also a values array, // a foreach() iterator function
BytePushers.enumeration = function (namesToValues) {
// This is the dummy constructor function that will be the return value.
var name,
e,
i3,
enumeration = function () { throw "Can't Instantiate Enumerations"; },
proto;
enumeration.prototype = { // Enumerated values inherit from this object.
constructor: enumeration, // Identify type
toString: function () { return this.name; }, // Return name
valueOf: function () { return this.value; }, // Return value
toJSON: function () { return this.name; } // For serialization
};
proto = enumeration;
enumeration.values = []; // An array of the enumerated value objects
// Now create the instances of this new type.
for (name in namesToValues) { // For each value
if (namesToValues.hasOwnProperty(name)) {
e = BytePushers.inherit(proto); // Create an object to represent it
e.name = name; // Give it a name
e.value = namesToValues[name]; // And a value
enumeration[name] = e; // Make it a property of constructor
enumeration.values.push(e); // And store in the values array
}
}
// A class method for iterating the instances of the class
enumeration.foreach = function (f, c) {
for (i3 = 0; i3 < this.values.length; i3 = i3 + 1) {
f.call(c, this.values[i3]);
}
};
// Return the constructor that identifies the new type
return enumeration;
};
}(window, document));