1 /*global window, document*/ 2 /*jslint bitwise: true, unparam: true, regexp: true*/ 3 4 (function (window, document) { 5 'use strict'; 6 var BytePushers; 7 8 if (window.BytePushers !== undefined && window.BytePushers !== null) { 9 BytePushers = window.BytePushers; 10 } else { 11 window.BytePushers = {}; 12 BytePushers = window.BytePushers; 13 } 14 /**************************************************************************************************** 15 * BEGIN Array Extensions */ 16 if (!Array.prototype.every) { 17 Array.prototype.every = function (fun, funParameter) { 18 var t = Object.create(this), 19 len = t.length >>> 0, 20 i; 21 22 if (this === null) { 23 throw new TypeError(); 24 } 25 26 if (typeof fun !== "function") { 27 throw new TypeError(); 28 } 29 30 for (i = 0; i < len; i = i + 1) { 31 if (t.hasOwnProperty(i) && !fun.call(funParameter, t[i], i, t)) { 32 return false; 33 } 34 } 35 36 return true; 37 }; 38 } 39 40 // Production steps of ECMA-262, Edition 5, 15.4.4.18 41 // Reference: http://es5.github.com/#x15.4.4.18 42 if (!Array.prototype.forEach) { 43 44 Array.prototype.forEach = function forEach(callback, thisArg) { 45 46 var T, k, O, len, obj = {}, kValue; 47 48 if (this === null) { 49 throw new TypeError("this is null or not defined"); 50 } 51 52 // 1. Let O be the result of calling ToObject passing the |this| value as the argument. 53 O = Object.create(this); 54 55 // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". 56 // 3. Let len be ToUint32(lenValue). 57 len = O.length >>> 0; // Hack to convert O.length to a UInt32 58 59 // 4. If IsCallable(callback) is false, throw a TypeError exception. 60 // See: http://es5.github.com/#x9.11 61 if (obj.toString.call(callback) !== "[object Function]") { 62 throw new TypeError(callback + " is not a function"); 63 } 64 65 // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. 66 if (thisArg) { 67 T = thisArg; 68 } 69 70 // 6. Let k be 0 71 k = 0; 72 73 // 7. Repeat, while k < len 74 while (k < len) { 75 // a. Let Pk be ToString(k). 76 // This is implicit for LHS operands of the in operator 77 // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. 78 // This step can be combined with c 79 // c. If kPresent is true, then 80 if (Object.prototype.hasOwnProperty.call(O, k)) { 81 82 // i. Let kValue be the result of calling the Get internal method of O with argument Pk. 83 kValue = O[k]; 84 85 // ii. Call the Call internal method of callback with T as the this value and 86 // argument list containing kValue, k, and O. 87 callback.call(T, kValue, k, O); 88 } 89 // d. Increase k by 1. 90 k = k + 1; 91 } 92 // 8. return undefined 93 }; 94 } 95 96 if (!Array.prototype.some) { 97 Array.prototype.some = function (fun, functionParameter) { 98 var t = Object.create(this), 99 len = t.length >>> 0, 100 i; 101 102 if (this === null) { 103 throw new TypeError(); 104 } 105 106 if (typeof fun !== "function") { 107 throw new TypeError(); 108 } 109 110 for (i = 0; i < len; i = i + 1) { 111 if (t.hasOwnProperty(i) && fun.call(functionParameter, t[i], i, t)) { 112 return true; 113 } 114 } 115 116 return false; 117 }; 118 } 119 120 if (!Array.prototype.isArray) { 121 Array.prototype.isArray = function (arg) { 122 var targetArray = (arg === true) ? arg : this; 123 return Object.prototype.toString.call(targetArray) === "[object Array]"; 124 }; 125 } 126 127 /* END Array Extensions * 128 ****************************************************************************************************/ 129 130 /**************************************************************************************************** 131 * BEGIN Date Extensions */ 132 /** 133 * <p>Function that is used to determine if two dates objects have the same date.</p> 134 * @function 135 * @param {@link Date} The date to evaluate against this object. 136 * @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. 137 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 138 */ 139 Date.prototype.isDateEqualTo = function (date) { 140 if (this.getFullYear() === date.getFullYear()) { 141 if (this.getMonth() === date.getMonth()) { 142 if (this.getDate() === date.getDate()) { 143 return true; 144 } 145 } 146 } 147 return false; 148 }; 149 150 /** 151 * <p>Function that is used to determine if two dates objects have the same date and time.</p> 152 * @function 153 * @param {@link Date} The date to evaluate against this object. 154 * @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. 155 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 156 */ 157 Date.prototype.isDateEqualToDateAndTime = function (date) { 158 if (this.getFullYear() === date.getFullYear()) { 159 if (this.getMonth() === date.getMonth()) { 160 if (this.getDate() === date.getDate()) { 161 if (this.getHours() === date.getHours()) { 162 if (this.getMinutes() === date.getMinutes()) { 163 return true; 164 } 165 } 166 } 167 } 168 } 169 return false; 170 }; 171 172 /** 173 * <p>Function that is used to determine a date is the day after another date.</p> 174 * @function 175 * @param {@link Date} The date to evaluate against this object. 176 * @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. 177 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 178 */ 179 Date.prototype.isDateEqualToTomorrow = function (date) { 180 if (this.getFullYear() === date.getFullYear()) { 181 if (this.getMonth() === date.getMonth()) { 182 if (this.getDate() + 1 === date.getDate()) { 183 return true; 184 } 185 } else if (this.getMonth() + 1 === date.getMonth()) { 186 if (this.isLastDayInMonth() && date.getDate() === 1) { 187 return true; 188 } 189 } 190 } else if (this.getFullYear() + 1 === date.getFullYear()) { 191 if (this.getMonth() === 11 && date.getMonth() === 0) { 192 if (this.getDate() === 31 && date.getDate() === 1) { 193 return true; 194 } 195 } 196 } 197 return false; 198 }; 199 200 /** 201 * <p>Function that is used to determine a date is the day before another date.</p> 202 * @function 203 * @param {@link Date} The date to evaluate against this object. 204 * @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. 205 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 206 */ 207 Date.prototype.isDateEqualToYesterday = function (date) { 208 if (this.getFullYear() === date.getFullYear()) { 209 if (this.getMonth() === date.getMonth()) { 210 if (this.getDate() - 1 === date.getDate()) { 211 return true; 212 } 213 } else if (this.getMonth() === date.getMonth() + 1) { 214 if (this.getDate() === 1 && date.isLastDayInMonth()) { 215 return true; 216 } 217 } 218 } else if (this.getFullYear() - 1 === date.getFullYear()) { 219 if (this.getMonth() === 0 && date.getMonth() === 11) { 220 if (this.getDate() === 1 && date.getDate() === 31) { 221 return true; 222 } 223 } 224 } 225 return false; 226 }; 227 228 /** 229 * <p>Tells you whether it is the last day in a month or not.</p> 230 * @private 231 * @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. 232 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 233 */ 234 Date.prototype.isLastDayInMonth = function () { 235 var lastDayInMonth = this.getCurrentMonthTotalDays(); 236 if (this.getDate() === lastDayInMonth) { 237 return true; 238 } 239 return false; 240 }; 241 242 /** 243 * <p>Function that is used to get calendar total calendar days of the previous month.</p> 244 * @function 245 * @returns <a href="http://www.w3schools.com/jsref/jsref_obj_number.asp">Number</a> The total days in the previous month. 246 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 247 */ 248 Date.prototype.getPreviousMonthTotalDays = function () { 249 if (this.getMonth() === 0) { 250 return Date.monthNames[11].getTotalDays(this.getFullYear()); 251 } 252 253 return Date.monthNames[this.getMonth() - 1].getTotalDays(this.getFullYear()); 254 }; 255 256 /** 257 * <p>Function that is used to get the total calendar days of the next month.</p> 258 * @function 259 * @returns <a href="http://www.w3schools.com/jsref/jsref_obj_number.asp">Number</a> The total days in the next month. 260 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 261 */ 262 Date.prototype.getNextMonthTotalDays = function () { 263 if (this.getMonth() === 11) { 264 return Date.monthNames[0].getTotalDays(this.getFullYear()); 265 } 266 267 return Date.monthNames[this.getMonth() + 1].getTotalDays(this.getFullYear()); 268 }; 269 270 /** 271 * <p>Function that is used to get the total calendar days of the next month.</p> 272 * @function 273 * @returns <a href="http://www.w3schools.com/jsref/jsref_obj_number.asp">Number</a> The total days in the next month. 274 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 275 */ 276 Date.prototype.getCurrentMonthTotalDays = function () { 277 if (this.getMonth() === 11) { 278 return Date.monthNames[0].getTotalDays(this.getFullYear()); 279 } 280 281 return Date.monthNames[this.getMonth()].getTotalDays(this.getFullYear()); 282 }; 283 284 /** 285 * <p>Adds time to a date object.</p> 286 * @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. 287 * 288 * @returns {String} A new Date object with the specified time added. 289 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 290 */ 291 Date.prototype.addTime = function (time) { 292 var newDate = new Date(), 293 wholeNumber = (time > 0) ? Math.floor(time) : Math.ceil(time), 294 fraction = ((time - wholeNumber).toFixed(2) * 100), 295 hourInMilliseconds = 1000 * 60 * 60 * wholeNumber, 296 minutesInMilliseconds = 1000 * 60 * fraction; 297 298 newDate.setTime(this.getTime()); 299 newDate.setTime(newDate.getTime() + hourInMilliseconds); 300 newDate.setTime(newDate.getTime() + minutesInMilliseconds); 301 302 return newDate; 303 }; 304 /** 305 * <p>Static function that tells you whether a date is the last day in a month or not.</p> 306 * @private 307 * @static 308 * @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. 309 * @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. 310 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 311 */ 312 Date.isLastDayInMonth = function (date) { 313 var lastDayInMonth = date.getCurrentMonthTotalDays(); 314 if (date.getDate() === lastDayInMonth) { 315 return true; 316 } 317 return false; 318 }; 319 320 /** 321 * <p>Static function that gets month name.</p> 322 * @private 323 * @static 324 * @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. 325 * @param <a href="http://www.w3schools.com/jsref/jsref_obj_boolean.asp">Boolean</a> useAbbr An optional boolean flag that governs whether the 326 * full name of the month is returned or its abbreviation. 327 * @returns {String} The name of the month. 328 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 329 */ 330 Date.getMonthName = function (index, getAbbr) { 331 if (getAbbr) { 332 return this.monthNames[index].abbr; 333 } 334 335 return this.monthNames[index].name; 336 }; 337 338 /** 339 * <p>Static field for the list of month.</p> 340 * @static 341 * @field 342 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 343 */ 344 Date.monthNames = [ 345 {"name": "January", "abbr": "Jan", "getTotalDays": function () { return 31; } }, 346 {"name": "February", "abbr": "Feb", "getTotalDays": function (year) { 347 if (year) { 348 return (year % 4 === 0) ? 29 : 28; 349 } 350 351 throw ("Expected parameter(Year) is not defined."); 352 }}, 353 {"name": "March", "abbr": "Mar", "getTotalDays": function () { return 31; }}, 354 {"name": "April", "abbr": "Apr", "getTotalDays": function () { return 30; }}, 355 {"name": "May", "abbr": "May", "getTotalDays": function () { return 31; }}, 356 {"name": "June", "abbr": "Jun", "getTotalDays": function () { return 30; }}, 357 {"name": "July", "abbr": "Jul", "getTotalDays": function () { return 31; }}, 358 {"name": "August", "abbr": "Aug", "getTotalDays": function () { return 31; }}, 359 {"name": "September", "abbr": "Sep", "getTotalDays": function () { return 30; }}, 360 {"name": "October", "abbr": "Oct", "getTotalDays": function () { return 31; }}, 361 {"name": "November", "abbr": "Nov", "getTotalDays": function () { return 30; }}, 362 {"name": "December", "abbr": "Dec", "getTotalDays": function () { return 31; }} 363 ]; 364 /* END Date Extensions * 365 ****************************************************************************************************/ 366 367 /**************************************************************************************************** 368 * BEGIN Object Extensions */ 369 /** 370 * <p>Static function that tells you whether an object is an array or not.</p> 371 * @static 372 * @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. 373 * @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. 374 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 375 */ 376 Object.isArray = function (someArray) { 377 var result = false; 378 if (Object.isDefined(someArray)) { 379 if (someArray.constructor.toString().indexOf("Array") > -1) { 380 result = true; 381 } 382 } 383 384 return result; 385 }; 386 387 /** 388 * <p>Static function that tells you whether an object is a date or not.</p> 389 * @static 390 * @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. 391 * @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. 392 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 393 */ 394 Object.isDate = function (someDate) { 395 var result = false; 396 if (Object.isDefined(someDate)) { 397 if (typeof someDate === "object" && someDate instanceof Date) { 398 result = true; 399 } 400 } 401 402 return result; 403 }; 404 /** 405 * <p>Static function that tells you whether an object is a string or not.</p> 406 * @static 407 * @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. 408 * @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. 409 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 410 */ 411 Object.isString = function (someString) { 412 var result = false; 413 if (Object.isDefined(someString)) { 414 if (typeof someString === "string" || (typeof someString === "object" && someString instanceof String)) { 415 result = true; 416 } 417 } 418 419 return result; 420 }; 421 422 /** 423 * <p>Static function that tells you whether an object is numeric or not.</p> 424 * @static 425 * @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. 426 * @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. 427 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 428 */ 429 Object.isNumeric = function (someNumber) { 430 var result = false; 431 if (Object.isDefined(someNumber)) { 432 if (typeof someNumber === "number" || (typeof someNumber === "object" && someNumber instanceof Number)) { 433 result = true; 434 } 435 } 436 437 return result; 438 }; 439 440 /** 441 * <p>Static function that tells you whether an object is a boolean or not.</p> 442 * @static 443 * @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. 444 * @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. 445 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 446 */ 447 Object.isBoolean = function (someBoolean) { 448 var result = false; 449 if (Object.isDefined(someBoolean)) { 450 if (typeof someBoolean === "boolean" || (typeof someBoolean === "object" && someBoolean instanceof Boolean)) { 451 result = true; 452 } 453 } 454 455 return result; 456 }; 457 458 /** 459 * <p>Static function that tells you whether an object is defined or not.</p> 460 * @static 461 * @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. 462 * @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. 463 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 464 */ 465 Object.isDefined = function (target) { 466 var result = false; 467 if (target !== undefined && target !== null) { 468 result = true; 469 } 470 return result; 471 }; 472 /* END Object Extensions * 473 ****************************************************************************************************/ 474 475 /**************************************************************************************************** 476 * BEGIN String Extensions */ 477 /** 478 * <p>Function that is used to trim the white spaces from the beginning and end of the string.</p> 479 * @function 480 * @return <a href="http://www.w3schools.com/jsref/jsref_obj_string.asp">String</a> The value of the string after it has been trimmed. 481 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 482 */ 483 String.prototype.trim = function () { 484 return this.replace(/^\s+|\s+$/g, ''); 485 }; 486 487 /** 488 * <p>Function that is used to determine if a string includes a certain character or string.</p> 489 * @function 490 * @param <a href="http://www.w3schools.com/jsref/jsref_obj_string.asp">String</a> The string we are checking if is included. 491 * @return <a href="http://www.w3schools.com/jsref/jsref_obj_boolean.asp">Boolean</a> True of the string is included, otherwise false. 492 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 493 */ 494 if (!String.prototype.includes) { 495 String.prototype.includes = function () { 496 return String.prototype.indexOf.apply(this, arguments) !== -1; 497 }; 498 } 499 500 /** 501 * <p>Function that is used to format a sentence to camel case. (e.g. Hello world => helloWorld).</p> 502 * @function 503 * @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. 504 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 505 */ 506 String.prototype.toCamelCase = function () { 507 return this.replace(/^([A-Z])|\s(\w)/g, function (match, p1, p2) { 508 if (p2) { 509 return p2.toUpperCase(); 510 } 511 return p1.toLowerCase(); 512 }); 513 }; 514 515 /** 516 * <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> 517 * @function 518 * @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. 519 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 520 */ 521 String.prototype.toNormalCase = function () { 522 return this.replace(/([a-z])([A-Z])/g, '$1 $2').replace(/([A-Z])([A-Z])/g, '$1 $2').replace(/^./, function (str) {return str.toUpperCase(); }); 523 }; 524 525 /** 526 * <p>Convenience function that will format a string with dynamic variables.</p> 527 * @static 528 * @param {...string} string - first argument is the string to be formatted. The remaining arguments are the format items (e.g. "{0}") 529 * @function 530 * @return <a href="http://www.w3schools.com/jsref/jsref_obj_string.asp">String</a> The value of the string after it has been formatted. 531 * @author <a href="mailto:pouncilt.developer@gmail.com">Tonté Pouncil</a> 532 */ 533 String.format = function (someString) { 534 // The string containing the format items (e.g. "{0}") 535 // will and always has to be the first argument. 536 var theString = someString, i, regEx; 537 538 // start with the second argument (i = 1) 539 for (i = 0; i < arguments.length; i = i + 1) { 540 // "gm" = RegEx options for Global search (more than one instance) 541 // and for Multiline search 542 regEx = new RegExp("\\{" + i + "\\}", "gm"); 543 theString = theString.replace(regEx, arguments[i]); 544 } 545 546 return theString; 547 }; 548 /* END String Extensions *****************************************************************************************************/ 549 550 551 BytePushers.namespace = function (ns_string) { 552 var parts = ns_string.split('.'), parent = BytePushers; 553 // strip redundant leading global 554 if (parts[0] === "BytePushers") { 555 parts = parts.slice(1); 556 } 557 parts.forEach(function (part, index) { 558 // create a property if it doesn't exist 559 if (parent[part] === undefined) { 560 parent[part] = {}; 561 } 562 parent = parent[part]; 563 }); 564 return parent; 565 }; 566 567 /** 568 * inherit() returns a newly created object that inherits properties from the prototype object p. 569 * It uses the ECMAScript 5 function Object.create() if it is defined, and otherwise falls. 570 * 571 * @param p 572 * @returns {*} 573 */ 574 BytePushers.inherit = function (p) { 575 var t; 576 if (p === null) { // p must be non-null object 577 throw new TypeError(); 578 } 579 if (Object.create) { // if Object.create() is defined... 580 return Object.create(p); // then just use it. 581 } 582 583 t = typeof p; // Otherwise do some more type checking 584 585 if (t !== "object" && t !== "function") { 586 throw new TypeError(); 587 } 588 589 function F() {// Define a dummy constructor function. 590 return; 591 } 592 F.prototype = p; // Set its prototype property to p. 593 return new F(); // Use f() to create an "heir" of p. 594 }; 595 596 /** 597 * defineClass() -- a utility function for defining JavaScript classes. 598 * 599 * This function expects a single object as its only argument. It defines 600 * a new JavaScript class based on the data in that object and returns the 601 * constructor function of the new class. This function handles the repetitive 602 * tasks of defining classes: setting up the prototype object for correct 603 * inheritance, copying methods from other types, and so on. 604 * 605 * The object passed as an argument should have some or all of the 606 * following properties: 607 * 608 * name: the name of the class being defined. 609 * If specified, this value will be stored in the classname 610 * property of the prototype object. 611 * 612 * extend: The constructor of the class to be extended. If omitted, 613 * the Object() constructor will be used. This value will 614 * be stored in the superclass property of the prototype object. 615 * 616 * construct: The constructor function for the class. If omitted, a new 617 * empty function will be used. This value becomes the return 618 * value of the function, and is also stored in the constructor 619 * property of the prototype object. 620 * 621 * methods: An object that specifies the instance methods (and other shared 622 * properties) for the class. The properties of this object are 623 * copied into the prototype object of the class. If omitted, 624 * an empty object is used instead. Properties named 625 * "classname", "superclass", and "constructor" are reserved 626 * and should not be used in this object. 627 * 628 * statics: An object that specifies the static methods (and other static 629 * properties) for the class. The properties of this object become 630 * properties of the constructor function. If omitted, an empty 631 * object is used instead. 632 * 633 * borrows: A constructor function or array of constructor functions. 634 * The instance methods of each of the specified classes are copied 635 * into the prototype object of this new class so that the 636 * new class borrows the methods of each specified class. 637 * Constructors are processed in the order they are specified, 638 * so the methods of a class listed at the end of the array may 639 * overwrite the methods of those specified earlier. Note that 640 * borrowed methods are stored in the prototype object before 641 * the properties of the methods object above. Therefore, 642 * methods specified in the methods object can overwrite borrowed 643 * methods. If this property is not specified, no methods are 644 * borrowed. 645 * 646 * provides: A constructor function or array of constructor functions. 647 * After the prototype object is fully initialized, this function 648 * verifies that the prototype includes methods whose names and 649 * number of arguments match the instance methods defined by each 650 * of these classes. No methods are copied; this is simply an 651 * assertion that this class "provides" the functionality of the 652 * specified classes. If the assertion fails, this method will 653 * throw an exception. If no exception is thrown, any 654 * instance of the new class can also be considered (using "duck 655 * typing") to be an instance of these other types. If this 656 * property is not specified, no such verification is performed. 657 **/ 658 BytePushers.defineClass = function (data) { 659 // Extract the fields we'll use from the argument object. 660 // Set up default values. 661 var classname = data.name, 662 Superclass = data.extend || Object, 663 constructor = data.construct || function () {return; }, 664 methods = data.methods || {}, 665 statics = data.statics || {}, 666 borrows, 667 provides, 668 proto, 669 i1, 670 i2, 671 c1, 672 c2, 673 p1, 674 p2, 675 p3, 676 p4, 677 p5; 678 679 // Borrows may be a single constructor or an array of them. 680 if (!data.borrows) { 681 borrows = []; 682 } else if (data.borrows instanceof Array) { 683 borrows = data.borrows; 684 } else { 685 borrows = [ data.borrows ]; 686 } 687 688 // Ditto for the provides property. 689 if (!data.provides) { 690 provides = []; 691 } else if (data.provides instanceof Array) { 692 provides = data.provides; 693 } else { 694 provides = [ data.provides ]; 695 } 696 697 // Create the object that will become the prototype for our class. 698 proto = new Superclass(); 699 700 // Delete any noninherited properties of this new prototype object. 701 for (p1 in proto) { 702 if (proto.hasOwnProperty(p1)) { 703 delete proto[p1]; 704 } 705 } 706 707 // Borrow methods from "mixin" classes by copying to our prototype. 708 for (i1 = 0; i1 < borrows.length; i1 = i1 + 1) { 709 c1 = data.borrows[i1]; 710 borrows[i1] = c1; 711 // Copy method properties from prototype of c to our prototype 712 for (p2 in c1.prototype) { 713 if (typeof c1.prototype[p2] === "function") { 714 proto[p2] = c1.prototype[p2]; 715 } 716 } 717 } 718 719 // Copy instance methods to the prototype object 720 // This may overwrite methods of the mixin classes 721 for (p3 in methods) { 722 if (methods.hasOwnProperty(p3)) { 723 proto[p3] = methods[p3]; 724 } 725 } 726 727 // Set up the reserved "constructor", "superclass", and "classname" 728 // properties of the prototype. 729 proto.constructor = constructor; 730 proto.Superclass = Superclass; 731 // classname is set only if a name was actually specified. 732 if (classname) { 733 proto.classname = classname; 734 } 735 736 // Verify that our prototype provides all of the methods it is supposed to. 737 for (i2 = 0; i2 < provides.length; i2 = i2 + 1) { // for each class 738 c2 = provides[i2]; 739 for (p4 in c2.prototype) { // for each property 740 if (typeof c2.prototype[p4] === "function" && (p4 === "constructor" || p4 === "superclass")) { //methods only 741 // Check that we have a method with the same name and that 742 // it has the same number of declared arguments. If so, move on 743 if (proto.hasOwnProperty(p4) && typeof proto[p4] !== "function" && proto[p4].length !== c2.prototype[p4].length) { 744 // Otherwise, throw an exception 745 throw new Error("Class " + classname + " does not provide method " + c2.classname + "." + p4); 746 } 747 } 748 } 749 } 750 751 // Associate the prototype object with the constructor function 752 constructor.prototype = proto; 753 754 // Copy static properties to the constructor 755 for (p5 in statics) { 756 if (statics.hasOwnProperty(p5)) { 757 constructor[p5] = statics[p5]; 758 } 759 } 760 761 // Finally, return the constructor function 762 return constructor; 763 }; 764 765 BytePushers.isArrayLike = function (x) { 766 if (x instanceof Array) { // Real arrays are array-like 767 return true; 768 } 769 if (!x.hasOwnProperty("length")) { // Arrays must have a length property 770 return false; 771 } 772 if (typeof x.length !== "number") { // Length must be a number 773 return false; 774 } 775 if (x.length < 0) { // and nonnegative 776 return false; 777 } 778 if (x.length > 0) { 779 // If the array is nonempty, it must at a minimum 780 // have a property defined whose name is the number length-1 781 if (!x.hasOwnProperty((x.length - 1))) { 782 return false; 783 } 784 } 785 return true; 786 }; 787 788 // Return true if O has methods with the same name and arity as all 789 // methods in I.prototype. Otherwise, return false. Throws an exception 790 // if I is a built-in type with nonenumerable methods. 791 BytePushers.provides = function (O, I) { 792 var proto = I.prototype, 793 p6; 794 // If O actually is an instance of I, it obviously looks like I 795 if (O instanceof I) { 796 return true; 797 } 798 799 // If a constructor was passed instead of an object, use its prototype 800 if (typeof O === "function") { 801 O = O.prototype; 802 } 803 804 // The methods of built-in types are not enumerable, and we return 805 // undefined. Otherwise any object would appear to provide any of 806 // the built-in types. 807 if (I === Array || I === Boolean || I === Date || I === Error || I === Function || I === Number || I === RegExp || I === String) { 808 return undefined; 809 } 810 811 for (p6 in proto) { // Loop through all properties in I.prototype 812 // Ignore properties that are not functions 813 if (typeof proto[p6] === "function") { 814 // If O does not have a property by the same name return false 815 if (!(O.hasOwnProperty(p6))) { 816 return false; 817 } 818 // If that property is not a function, return false 819 if (typeof O[p6] !== "function") { 820 return false; 821 } 822 // If the two functions are not declared with the same number 823 // of arguments return false. 824 if (O[p6].length !== proto[p6].length) { 825 return false; 826 } 827 } 828 } 829 // If all the methods check out, we can finally return true. 830 return true; 831 }; 832 833 // This function creates a new enumerated type. The argument object specifies // the names and values of each instance of the class. The return value 834 // is a constructor function that identifies the new class. Note, however 835 // that the constructor throws an exception: you can't use it to create new 836 // 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 837 BytePushers.enumeration = function (namesToValues) { 838 // This is the dummy constructor function that will be the return value. 839 var name, 840 e, 841 i3, 842 enumeration = function () { throw "Can't Instantiate Enumerations"; }, 843 proto; 844 845 enumeration.prototype = { // Enumerated values inherit from this object. 846 constructor: enumeration, // Identify type 847 toString: function () { return this.name; }, // Return name 848 valueOf: function () { return this.value; }, // Return value 849 toJSON: function () { return this.name; } // For serialization 850 }; 851 proto = enumeration; 852 enumeration.values = []; // An array of the enumerated value objects 853 854 // Now create the instances of this new type. 855 for (name in namesToValues) { // For each value 856 if (namesToValues.hasOwnProperty(name)) { 857 e = BytePushers.inherit(proto); // Create an object to represent it 858 e.name = name; // Give it a name 859 e.value = namesToValues[name]; // And a value 860 enumeration[name] = e; // Make it a property of constructor 861 enumeration.values.push(e); // And store in the values array 862 } 863 } 864 865 // A class method for iterating the instances of the class 866 enumeration.foreach = function (f, c) { 867 for (i3 = 0; i3 < this.values.length; i3 = i3 + 1) { 868 f.call(c, this.values[i3]); 869 } 870 }; 871 // Return the constructor that identifies the new type 872 return enumeration; 873 }; 874 }(window, document));