1 // 2 // Copyright 2010, Tridium, Inc. All Rights Reserved. 3 // 4 5 /** 6 * Core Object Architecture for BajaScript. 7 * 8 * @author Gareth Johnson 9 * @version 1.0.0.0 10 */ 11 12 //JsLint options (see http://www.jslint.com ) 13 /*jslint rhino: true, onevar: false, plusplus: true, white: true, undef: false, nomen: false, eqeqeq: true, 14 bitwise: true, regexp: true, newcap: true, immed: true, strict: false, indent: 2, vars: true, continue: true */ 15 16 // Globals for JsLint to ignore 17 /*global baja, BaseBajaObj*/ 18 19 (function obj(baja) { 20 21 // Use ECMAScript 5 Strict Mode 22 "use strict"; 23 24 // Create local for improved minification 25 var strictArg = baja.strictArg, 26 strictAllArgs = baja.strictAllArgs, 27 bajaDef = baja.def, 28 objectify = baja.objectify, 29 bajaHasType = baja.hasType; 30 31 //////////////////////////////////////////////////////////////// 32 // Baja Objects 33 //////////////////////////////////////////////////////////////// 34 35 /** 36 * @class Represents baja:Object in BajaScript. 37 * <p> 38 * Since this Constructor represents an abstract class, it should never 39 * be directly used to create a new Object. 40 * 41 * @name baja.Object 42 * @extend BaseBajaObj 43 */ 44 baja.Object = function () { 45 baja.Object.$super.apply(this, arguments); 46 }.$extend(BaseBajaObj); 47 48 // Default equals equivalent 49 var equalsEquivalent = function (obj) { 50 // By default, just perform equals 51 return this.equals(obj); 52 }; 53 54 /** 55 * Equivalence test. 56 * <p> 57 * Equivalent is used to compare if two objects have equivalent 58 * state, but might not want to return true for equals since it 59 * it has implied semantics for many operations. The default 60 * implementation returns the result of <code>equals()</code>. 61 * 62 * @param obj 63 * @returns {Boolean} 64 */ 65 baja.Object.prototype.equivalent = equalsEquivalent; 66 67 var objectGetIcon = function () { 68 return this.getType().getIcon(); 69 }; 70 71 /** 72 * Return the Object's Icon. 73 * 74 * @returns {baja.Icon} 75 */ 76 baja.Object.prototype.getIcon = objectGetIcon; 77 78 /** 79 * @class Represents baja:Singleton in BajaScript. 80 * <p> 81 * Since this Constructor represents an abstract class, it should never 82 * be directly used to create a new Object. 83 * 84 * @name baja.Singleton 85 * @extends baja.Object 86 */ 87 baja.Singleton = function () { 88 baja.Singleton.$super.apply(this, arguments); 89 }.$extend(baja.Object).registerType("baja:Singleton"); 90 91 /** 92 * @class Represents baja:Value in BajaScript. 93 * <p> 94 * Since this Constructor represents an abstract class, it should never 95 * be directly used to create a new Object. 96 * 97 * @name baja.Value 98 * @extends baja.Object 99 */ 100 baja.Value = function () { 101 baja.Value.$super.apply(this, arguments); 102 }.$extend(baja.Object); 103 104 // The default newCopy method 105 var defaultNewCopy = function (exact) { 106 return this; 107 }; 108 109 /** 110 * Every value may be cloned using the newCopy method. 111 * <p> 112 * Please note that Simples are immutable so they don't 113 * allocate a new instance. 114 * 115 * @see baja.Simple 116 * 117 * @param {Boolean} [exact] true if an exact copy of the value should be made (only valid in the Component architecture). 118 * @returns a copy of the value (or the same instance if the value is a Simple). 119 */ 120 baja.Value.prototype.newCopy = defaultNewCopy; 121 122 // A caching function wrapper for decodeFromString 123 var cacheDecode = function (decodeFunc) { 124 // Since Simples are immutable, this function will cache the String used to decode the Simple 125 // so when encodeToString is called, this string can be returned 126 return function decodeFromString(str) { 127 var v = decodeFunc.call(this, str); 128 v.$cEncStr = str; 129 return v; 130 }; 131 }; 132 133 // A caching function wrapper for encodeToString 134 var cacheEncode = function (encodeFunc) { 135 // Since Simples are immutable, this function will attempt to get or create a cached string 136 // encoded value 137 return function encodeToString() { 138 if (this.$cEncStr === undefined) { 139 this.$cEncStr = encodeFunc.call(this); 140 } 141 return this.$cEncStr; 142 }; 143 }; 144 145 // Removes caching wrapper for Simple constants (mainly needed for number Simples) 146 var uncacheConstantEncodeDecode = function (constant) { 147 // Take the encodeToString and decodeToString on the prototype chain 148 // and apply it directly as a property on the constant 149 var c = constant; // Workaround for JsLint 150 constant.decodeFromString = c.decodeFromString; 151 constant.encodeToString = c.encodeToString; 152 return constant; 153 }; 154 155 /** 156 * @class Represents baja:Simple in BajaScript. 157 * <p> 158 * Simples are immutable and represent primitive data types in Niagara. They are the 159 * basic building blocks of the architecture. Simples contain no slots themselves but do 160 * contain an implicit data value that can be encoded and decoded in a String format. 161 * <p> 162 * Simples must be immutable and under no circumstances should there be any attempt 163 * to modify the contents of a Simple. 164 * <p> 165 * all Simples must conform to the following conventions... 166 * <ul> 167 * <li>Define a DEFAULT instance on the Simple Constructor.</li> 168 * <li>Define a make method.</li> 169 * <li>Define a decodeFromString method on the Object's instance that takes a String and returns an instance of the Simple.</li> 170 * <li>Define an encodeToString method on the Object's instance that encodes the value to a String.</li> 171 * </ul> 172 * <p> 173 * Since this Constructor represents an abstract class, it should never 174 * be directly used to create a new Object. 175 * 176 * @name baja.Simple 177 * @extends baja.Value 178 */ 179 baja.Simple = function () { 180 baja.Simple.$super.apply(this, arguments); 181 }.$extend(baja.Value); 182 183 /** 184 * Equality test. 185 * 186 * @param obj 187 * @returns {Boolean} 188 */ 189 baja.Simple.prototype.equals = function (obj) { 190 // Comparing in via encodeToString is ok because most Simples 191 // lazily cache their string encoding (since they're all immutable) 192 return bajaHasType(obj) && 193 obj.getType().equals(this.getType()) && 194 obj.encodeToString() === this.encodeToString(); 195 }; 196 197 //////////////////////////////////////////////////////////////// 198 // Default Simple 199 //////////////////////////////////////////////////////////////// 200 201 /** 202 * @class DefaultSimple 203 * <p> 204 * Most of the core Simples in BajaScript are represented (i.e. 'baja:String', 'baja:Double' etc). However, they'll always 205 * be some Simples that BajaScript won't have support for. If a dedicated JS Constructor for a Simple can't be found, it'll 206 * default back to an instance of a DefaultSimple. 207 * <p> 208 * A DefaultSimple holds the decoded String representation of a Simple. 209 * <p> 210 * When creating a Simple, always use the 'make' method instead of creating a new Object. 211 * 212 * @name baja.DefaultSimple 213 * @extends baja.Simple 214 */ 215 baja.DefaultSimple = function (val) { 216 baja.DefaultSimple.$super.apply(this, arguments); 217 this.$val = strictArg(val || "", String); 218 }.$extend(baja.Simple); 219 220 /** 221 * Default DefaultSimple instance. 222 */ 223 baja.DefaultSimple.DEFAULT = new baja.DefaultSimple(); 224 225 /** 226 * Make a DefaultSimple. 227 * 228 * @param {String} str the String to be used for this Simple 229 * @returns {baja.DefaultSimple} an instance of the Simple 230 */ 231 baja.DefaultSimple.prototype.make = function (str) { 232 return this.decodeFromString(str); 233 }; 234 235 /** 236 * Decode a DefaultSimple from a String 237 * 238 * @param {String} str 239 * @returns {baja.DefaultSimple} 240 */ 241 baja.DefaultSimple.prototype.decodeFromString = function (str) { 242 var s = new baja.DefaultSimple(str); 243 s.getType = this.getType; 244 return s; 245 }; 246 247 /** 248 * Encode a DefaultSimple to a String. 249 * 250 * @returns {String} 251 */ 252 baja.DefaultSimple.prototype.encodeToString = function () { 253 return this.$val; 254 }; 255 256 // Register Type 257 baja.DefaultSimple.registerType("baja:Simple"); 258 259 /** 260 * Return the String encoding of a DefaultSimple. 261 * 262 * @returns {String} 263 */ 264 baja.DefaultSimple.prototype.valueOf = function () { 265 return this.$val; 266 }; 267 268 /** 269 * Return the String encoding of a DefaultSimple. 270 * 271 * @returns {String} 272 */ 273 baja.DefaultSimple.prototype.toString = function () { 274 return this.$val; 275 }; 276 277 //////////////////////////////////////////////////////////////// 278 // Augmented ECMAScript Baja Objects 279 //////////////////////////////////////////////////////////////// 280 281 // New copy that gets the value of the object for the clone 282 var valueOfNewCopy = function (exact) { 283 return this.valueOf(); 284 }; 285 286 // Equals that uses valueOf for the comparison 287 var valueOfEquals = function (obj) { 288 return bajaHasType(obj) && 289 obj.getType().equals(this.getType()) && 290 obj.valueOf() === this.valueOf(); 291 }; 292 293 /** 294 * @class Represents baja:String in BajaScript. 295 * <p> 296 * All JavaScript Strings are augmented to be baja:String objects. 297 * 298 * @name String 299 */ 300 301 /** 302 * Default String instance. 303 */ 304 String.DEFAULT = ""; 305 306 /** 307 * Make a String. 308 * 309 * @param {String} [str] 310 * @returns {String} 311 */ 312 String.make = function (str) { 313 if (str === undefined) { 314 return String.DEFAULT; 315 } 316 if (typeof str !== "string") { 317 throw new Error("Must supply a String when making BString instances: " + str); 318 } 319 return str; 320 }; 321 322 /** 323 * Make a String. 324 * 325 * @param {String} [str] 326 * @returns {String} 327 */ 328 String.prototype.make = function (str) { 329 return String.make(str); 330 }; 331 332 /** 333 * Decode a String. 334 * 335 * @param {String} str 336 * @returns {String} 337 */ 338 String.prototype.decodeFromString = function prototype(str) { 339 return str; 340 }; 341 342 /** 343 * Encode a String. 344 * 345 * @returns {String} 346 */ 347 String.prototype.encodeToString = function () { 348 return this; 349 }; 350 351 // Register String with a Baja Type 352 String.registerType("baja:String"); 353 354 /** 355 * Returns a new String with the first letter Capitalized. 356 * 357 * @returns {String} 358 */ 359 String.prototype.capitalizeFirstLetter = function () { 360 if (this.length > 0) { 361 return this.charAt(0).toUpperCase() + this.substring(1, this.length); 362 } 363 else { 364 return this; 365 } 366 }; 367 368 /** 369 * Replace patterned items in a string from an Object Map. 370 * <p> 371 * <pre> 372 * // For example... 373 * var str = "The weather is {state}!"; 374 * str = str.patternReplace({state: "really warm"}); 375 * </pre> 376 * 377 * @returns {String} 378 */ 379 String.prototype.patternReplace = function (obj) { 380 return this.replace(/\{[a-zA-Z0-9]*\}/g, function (match) { 381 match = match.substring(1, match.length - 1); 382 return typeof obj[match] === "string" ? obj[match] : match; 383 }); 384 }; 385 386 /** 387 * Return the Symbol used for encoding this data type (primarily used for facets). 388 * 389 * @returns {String} 390 */ 391 String.prototype.getDataTypeSymbol = function () { 392 return "s"; 393 }; 394 395 /** 396 * Equality test. 397 * 398 * @function 399 * 400 * @param obj 401 * @returns {Boolean} 402 */ 403 String.prototype.equals = valueOfEquals; 404 405 /** 406 * Equivalence test. 407 * <p> 408 * Equivalent is used to compare if two objects have equivalent 409 * state, but might not want to return true for equals since it 410 * it has implied semantics for many operations. The default 411 * implementation returns the result of <code>equals()</code>. 412 * 413 * @function 414 * 415 * @param obj 416 * @returns {Boolean} 417 */ 418 String.prototype.equivalent = equalsEquivalent; 419 420 /** 421 * New Copy. 422 * 423 * @function 424 * 425 * @returns {String} 426 */ 427 String.prototype.newCopy = valueOfNewCopy; 428 429 /** 430 * Return the Object's Icon. 431 * 432 * @function 433 * 434 * @returns {baja.Icon} 435 */ 436 String.prototype.getIcon = objectGetIcon; 437 438 /** 439 * @class Represents a baja:Boolean in BajaScript. 440 * <p> 441 * Augments Boolean to be a baja:Boolean. 442 * 443 * @name Boolean 444 */ 445 446 /** 447 * Default Boolean instance. 448 */ 449 Boolean.DEFAULT = false; 450 451 /** 452 * Make a Boolean. 453 * 454 * @param {Boolean} b 455 * @returns {Boolean} 456 */ 457 Boolean.make = function (b) { 458 if (b === undefined) { 459 return Boolean.DEFAULT; 460 } 461 if (typeof b !== "boolean") { 462 throw new Error("Must supply a Boolean when making BBoolean instances: " + b); 463 } 464 return b; 465 }; 466 467 /** 468 * Make a Boolean. 469 * 470 * @param {Boolean} b 471 * 472 * @returns {Boolean} 473 */ 474 Boolean.prototype.make = function (b) { 475 return Boolean.make(b); 476 }; 477 478 /** 479 * Decode a Boolean from a String. 480 * 481 * @param {String} str 482 * @returns {Boolean} 483 */ 484 Boolean.prototype.decodeFromString = function (str) { 485 return str === "true"; 486 }; 487 488 /** 489 * Encode a Boolean to a String. 490 * 491 * @returns {String} 492 */ 493 Boolean.prototype.encodeToString = function () { 494 return this.toString(); 495 }; 496 497 /** 498 * Return the data type symbol. 499 * 500 * @returns {String} the Symbol used for encoding this data type (primarily used for facets). 501 */ 502 Boolean.prototype.getDataTypeSymbol = function () { 503 return "b"; 504 }; 505 506 // Register Type 507 Boolean.registerType("baja:Boolean"); 508 509 /** 510 * Equality test. 511 * 512 * @function 513 * 514 * @param obj 515 * @returns {Boolean} 516 */ 517 Boolean.prototype.equals = valueOfEquals; 518 519 /** 520 * Equivalence test. 521 * <p> 522 * Equivalent is used to compare if two objects have equivalent 523 * state, but might not want to return true for equals since it 524 * it has implied semantics for many operations. The default 525 * implementation returns the result of <code>equals()</code>. 526 * 527 * @function 528 * 529 * @param obj 530 * @returns {Boolean} 531 */ 532 Boolean.prototype.equivalent = equalsEquivalent; 533 534 /** 535 * New Copy. 536 * 537 * @function 538 * 539 * @returns {Boolean} 540 */ 541 Boolean.prototype.newCopy = valueOfNewCopy; 542 543 // Boolean Enum Methods 544 545 /** 546 * Return true if this enum's value is considered to be in an active state. 547 * 548 * @returns {Boolean} whether the enum is active. 549 */ 550 Boolean.prototype.isActive = function () { 551 return this.valueOf(); 552 }; 553 554 /** 555 * Return the enum ordinal. 556 * 557 * @returns {Number} the enum ordinal. 558 */ 559 Boolean.prototype.getOrdinal = function () { 560 return this.valueOf() ? 1 : 0; 561 }; 562 563 /** 564 * Return the string identifier of this enum value. 565 * 566 * @returns {String} tag 567 */ 568 Boolean.prototype.getTag = function () { 569 return this.valueOf() ? "true" : "false"; 570 }; 571 572 var getBooleanDisplayTag = function (facets) { 573 var text; 574 575 if (facets) { 576 if (this.valueOf()) { 577 text = facets instanceof baja.Facets ? facets.get("trueText") : facets.trueText; 578 } 579 else { 580 text = facets instanceof baja.Facets ? facets.get("falseText") : facets.falseText; 581 } 582 } 583 584 if (text) { 585 // Handle Format text 586 return baja.Format.format(text); 587 } 588 else { 589 return baja.lex("baja").get(this.getTag()); 590 } 591 }; 592 593 /** 594 * Return the display tag of this enum value. 595 * 596 * @name Boolean#getDisplayTag 597 * @function 598 * 599 * @param {baja.Facets|Object} [facets] used to specify the true and false text. 600 * For true text the facets key is 'trueText' and 601 * false is 'falseText'. The argument can also be an 602 * Object Literal. 603 * 604 * @returns {String} display tag 605 */ 606 Boolean.prototype.getDisplayTag = getBooleanDisplayTag; 607 608 var oldBooleanToString = Boolean.prototype.toString; 609 610 /** 611 * Return the String representation of a Boolean. 612 * 613 * @param {baja.Facets|Object} [facets] used to specify the true and false text. 614 * For true text the facets key is 'trueText' and 615 * false is 'falseText'. The argument can also be an 616 * Object Literal. 617 * 618 * @returns String 619 */ 620 Boolean.prototype.toString = function () { 621 if (arguments.length === 1) { 622 return getBooleanDisplayTag.apply(this, arguments); 623 } 624 else { 625 return oldBooleanToString.apply(this, arguments); 626 } 627 }; 628 629 /** 630 * Return the enum range. 631 * 632 * @returns {baja.EnumRange} the enum range. 633 */ 634 Boolean.prototype.getRange = function () { 635 return baja.EnumRange.BOOLEAN_RANGE; 636 }; 637 638 // TODO: What about encoding/decoding numbers from different locales? 639 // How does this work in JavaScript? 640 641 /** 642 * Return the Object's Icon. 643 * 644 * @function 645 * 646 * @returns {baja.Icon} 647 */ 648 Boolean.prototype.getIcon = objectGetIcon; 649 650 /** 651 * Return the boolean from a BIBoolean. 652 * 653 * @return resolved boolean value. 654 */ 655 Boolean.getBooleanFromIBoolean = function (boolVal) { 656 var val = boolVal; 657 658 if (boolVal.getType().isComplex() && boolVal.has("out") && boolVal.getOut().getType().is("baja:StatusBoolean")) { 659 val = boolVal.getOut().getValue().valueOf(); 660 } 661 else if (boolVal.getType().is("baja:StatusBoolean")) { 662 val = boolVal.getValue().valueOf(); 663 } 664 else if (boolVal.getType().is("baja:Boolean")) { 665 val = boolVal.valueOf(); 666 } 667 668 return val; 669 }; 670 671 /** 672 * @class Represents a baja:Double in BajaScript. 673 * <p> 674 * Augments Number to be a Baja Double. ECMAScript only has one numeric type for 675 * Doubles, Floats, Longs and Integers. Therefore, this type naturally maps to 676 * baja:Double. 677 * 678 * @name Number 679 */ 680 681 /** 682 * Default Number instance. 683 */ 684 Number.DEFAULT = 0; 685 686 /** 687 * @name baja.Double 688 * @augments Number 689 */ 690 baja.Double = Number; 691 692 /** 693 * Make a Number. 694 * 695 * @param {Number} num the number for the Simple. 696 * @returns {Number} the Number. 697 */ 698 Number.make = function (num) { 699 return Number(num); 700 }; 701 702 /** 703 * Make a Number. 704 * 705 * @param {Number} num the number. 706 * 707 * @returns {Number} the Number. 708 */ 709 Number.prototype.make = function (num) { 710 return Number.make(num); 711 }; 712 713 /** 714 * Decode a Number from a String. 715 * 716 * @param {String} str 717 * @returns {Number} 718 */ 719 Number.prototype.decodeFromString = function (str) { 720 if (str === "0") { 721 return 0; 722 } 723 // Infinity and NaN 724 else if (str === "+inf") { 725 return Number.POSITIVE_INFINITY; 726 } 727 else if (str === "-inf") { 728 return Number.NEGATIVE_INFINITY; 729 } 730 else if (str === "nan") { 731 return Number["NaN"]; // Workaround for JsLint 732 } 733 734 // Parse number 735 return Number(str); 736 }; 737 738 /** 739 * Encode to the Number to a String. 740 * 741 * @returns {String} 742 */ 743 Number.prototype.encodeToString = function () { 744 745 // Infinity and NaN 746 if (this.valueOf() === Number.POSITIVE_INFINITY) { 747 return "+inf"; 748 } 749 else if (this.valueOf() === Number.NEGATIVE_INFINITY) { 750 return "-inf"; 751 } 752 else if (isNaN(this.valueOf())) { 753 return "nan"; 754 } 755 756 return this.toString(); 757 }; 758 759 /** 760 * Return the data type symbol for a Double. 761 * 762 * @returns {String} the Symbol used for encoding this data type (primarily used for facets). 763 */ 764 Number.prototype.getDataTypeSymbol = function () { 765 return "d"; 766 }; 767 768 // Register Type 769 Number.registerType("baja:Double"); 770 771 /** 772 * Equality test. 773 * 774 * @function 775 * 776 * @param obj 777 * @returns {Boolean} 778 */ 779 Number.prototype.equals = valueOfEquals; 780 781 /** 782 * Equivalence test. 783 * <p> 784 * Equivalent is used to compare if two objects have equivalent 785 * state, but might not want to return true for equals since it 786 * it has implied semantics for many operations. The default 787 * implementation returns the result of <code>equals()</code>. 788 * 789 * @function 790 * 791 * @param obj 792 * @returns {Boolean} 793 */ 794 Number.prototype.equivalent = equalsEquivalent; 795 796 /** 797 * New Copy. 798 * 799 * @function 800 * 801 * @returns {Number} 802 */ 803 Number.prototype.newCopy = valueOfNewCopy; 804 805 /** 806 * Return the Object's Icon. 807 * 808 * @function 809 * 810 * @returns {baja.Icon} 811 */ 812 Number.prototype.getIcon = objectGetIcon; 813 814 /** 815 * Return the number from a BINumeric. 816 * 817 * @returns resolved numeric value. 818 */ 819 Number.getNumberFromINumeric = function (numeric) { 820 var val = numeric; 821 822 if (numeric.getType().isComplex() && numeric.has("out") && numeric.getOut().getType().is("baja:StatusNumeric")) { 823 val = numeric.getOut().getValue().valueOf(); 824 } 825 else if (numeric.getType().is("baja:StatusNumeric")) { 826 val = numeric.getValue().valueOf(); 827 } 828 else if (numeric.getType().is("baja:Number")) { 829 val = numeric.valueOf(); 830 } 831 832 return val; 833 }; 834 835 var oldNumberToString = Number.prototype.toString; 836 837 var numberToString = function (facets) { 838 if (facets) { 839 // Attempt to get the precision from the facets or an Object Literal argument 840 var precision = null; 841 if (facets instanceof baja.Facets) { 842 precision = facets.get("precision"); 843 } 844 else if (typeof facets.precision === "number") { 845 precision = facets.precision; 846 } 847 if (precision !== null) { 848 return this.valueOf().toFixed(Number(precision.valueOf())); 849 } 850 } 851 852 // If no arguments are specified then use the old 853 return oldNumberToString.apply(this.valueOf(), arguments); 854 }; 855 856 /** 857 * Return the String representation of a Number. 858 * 859 * @name Number#toString 860 * @function 861 * 862 * @param {baja.Facets|Object} [facets] used to specify the 'precision'. 863 * The argument can also be an Object Literal. 864 * 865 * @returns String 866 */ 867 Number.prototype.toString = numberToString; 868 869 /** 870 * @class Represents a baja:Float in BajaScript. 871 * <p> 872 * Boxes JavaScript Number to represent a baja:Float. 873 * <p> 874 * When creating a Simple, always use the 'make' method instead of creating a new Object. 875 * 876 * @name baja.Float 877 * @extends baja.Simple 878 */ 879 baja.Float = function (val) { 880 baja.Float.$super.apply(this, arguments); 881 this.$val = strictArg(val, Number); 882 }.$extend(baja.Simple); 883 884 /** 885 * Make a Float. 886 * 887 * @param {Number} num the Float for the Simple. 888 * @returns {baja.Float} the Float. 889 */ 890 baja.Float.make = function (val) { 891 if (val === 0) { 892 return baja.Float.DEFAULT; 893 } 894 else if (isNaN(val)) { 895 return baja.Float.NAN; 896 } 897 else if (val === baja.Float.POSITIVE_INFINITY.valueOf()) { 898 return baja.Float.POSITIVE_INFINITY; 899 } 900 else if (val === baja.Float.NEGATIVE_INFINITY.valueOf()) { 901 return baja.Float.NEGATIVE_INFINITY; 902 } 903 else if (val >= baja.Float.MAX_VALUE.valueOf()) { 904 return baja.Float.MAX_VALUE; 905 } 906 else if (val > 0 && val <= baja.Float.MIN_VALUE.valueOf()) { 907 return baja.Float.MIN_VALUE; 908 } 909 910 return new baja.Float(val); 911 }; 912 913 /** 914 * Make a Float. 915 * 916 * @param {Number} num the Float for the Simple. 917 * @returns {baja.Float} the Float. 918 */ 919 baja.Float.prototype.make = function (val) { 920 return baja.Float.make(val); 921 }; 922 923 /** 924 * Decode a Float from a String. 925 * 926 * @param {String} str 927 * @returns {baja.Float} 928 */ 929 baja.Float.prototype.decodeFromString = function (str) { 930 if (str === "0") { 931 return baja.Float.DEFAULT; 932 } 933 // Infinity and NaN 934 else if (str === "+inf") { 935 return baja.Float.POSITIVE_INFINITY; 936 } 937 else if (str === "-inf") { 938 return baja.Float.NEGATIVE_INFINITY; 939 } 940 else if (str === "nan") { 941 return baja.Float.NAN; 942 } 943 return baja.Float.make(parseFloat(str)); 944 }; 945 946 /** 947 * Encode the Float to a String. 948 * 949 * @returns {String} 950 */ 951 baja.Float.prototype.encodeToString = function () { 952 953 // Infinity and NaN 954 if (this.$val === Number.POSITIVE_INFINITY) { 955 return "+inf"; 956 } 957 else if (this.$val === Number.NEGATIVE_INFINITY) { 958 return "-inf"; 959 } 960 else if (isNaN(this.$val)) { 961 return "nan"; 962 } 963 964 // Check range limits 965 if (this.$val >= baja.Float.MAX_VALUE.valueOf()) { 966 return baja.Float.MAX_VALUE.valueOf().toString(); 967 } 968 else if (this.$val > 0 && this.$val <= baja.Float.MIN_VALUE.valueOf()) { 969 return baja.Float.MIN_VALUE.valueOf().toString(); 970 } 971 972 return this.$val.toString(); 973 }; 974 975 /** 976 * Default Float instance. 977 */ 978 baja.Float.DEFAULT = uncacheConstantEncodeDecode(new baja.Float(0)); 979 980 /** 981 * Float Max Value. 982 */ 983 baja.Float.MAX_VALUE = uncacheConstantEncodeDecode(new baja.Float(3.4028235E38)); 984 985 /** 986 * Float Min Value. 987 */ 988 baja.Float.MIN_VALUE = uncacheConstantEncodeDecode(new baja.Float(1.4E-45)); 989 990 /** 991 * Float Positive Infinity. 992 */ 993 baja.Float.POSITIVE_INFINITY = uncacheConstantEncodeDecode(new baja.Float(Number.POSITIVE_INFINITY)); 994 995 /** 996 * Float Negative Infinity. 997 */ 998 baja.Float.NEGATIVE_INFINITY = uncacheConstantEncodeDecode(new baja.Float(Number.NEGATIVE_INFINITY)); 999 1000 /** 1001 * Float Not A Number. 1002 */ 1003 baja.Float.NAN = uncacheConstantEncodeDecode(new baja.Float(Number["NaN"])); 1004 1005 baja.Float.prototype.decodeFromString = cacheDecode(baja.Float.prototype.decodeFromString); 1006 baja.Float.prototype.encodeToString = cacheEncode(baja.Float.prototype.encodeToString); 1007 1008 // Register Type 1009 baja.Float.registerType("baja:Float"); 1010 1011 /** 1012 * Equality test. 1013 * 1014 * @function 1015 * 1016 * @param obj 1017 * @returns {Boolean} 1018 */ 1019 baja.Float.prototype.equals = valueOfEquals; 1020 1021 /** 1022 * Return the Number encapsulated in the Float. 1023 * 1024 * @returns {Number} Number for the Float. 1025 */ 1026 baja.Float.prototype.valueOf = function () { 1027 return this.$val; 1028 }; 1029 1030 /** 1031 * Return the Symbol used for encoding this data type (primarily used for facets). 1032 * 1033 * @returns {String} 1034 */ 1035 baja.Float.prototype.getDataTypeSymbol = function () { 1036 return "f"; 1037 }; 1038 1039 /** 1040 * Return the String representation of a Float. 1041 * 1042 * @name baja.Float#toString 1043 * @function 1044 * 1045 * @param {baja.Facets|Object} [facets] used to specify the 'precision'. 1046 * The argument can also be an Object Literal. 1047 * 1048 * @returns String 1049 */ 1050 baja.Float.prototype.toString = numberToString; 1051 1052 /** 1053 * @class Represents a baja:Integer in BajaScript. 1054 * <p> 1055 * Boxes JavaScript Number to represent a baja:Integer. 1056 * <p> 1057 * When creating a Simple, always use the 'make' method instead of creating a new Object. 1058 * 1059 * @name baja.Integer 1060 * @extends baja.Simple 1061 */ 1062 baja.Integer = function (val) { 1063 baja.Integer.$super.apply(this, arguments); 1064 this.$val = strictArg(val, Number); 1065 }.$extend(baja.Simple); 1066 1067 /** 1068 * Make an Integer. 1069 * 1070 * @param {Number} num the Integer for the Simple. 1071 * @returns {baja.Integer} the Integer. 1072 */ 1073 baja.Integer.make = function (val) { 1074 if (val === 0 || isNaN(val)) { 1075 return baja.Integer.DEFAULT; 1076 } 1077 else if (val >= baja.Integer.MAX_VALUE.valueOf() || val === Number.MAX_VALUE) { 1078 return baja.Integer.MAX_VALUE; 1079 } 1080 else if (val <= baja.Integer.MIN_VALUE.valueOf() || val === Number.MIN_VALUE) { 1081 return baja.Integer.MIN_VALUE; 1082 } 1083 1084 // Ensure we don't have a floating point 1085 val = Math.floor(val); 1086 1087 if (val === 0) { 1088 return baja.Integer.DEFAULT; 1089 } 1090 1091 return new baja.Integer(val); 1092 }; 1093 1094 /** 1095 * Make an Integer. 1096 * 1097 * @param {Number} num the Integer for the Simple. 1098 * @returns {baja.Integer} the Integer. 1099 */ 1100 baja.Integer.prototype.make = function (val) { 1101 return baja.Integer.make(val); 1102 }; 1103 1104 /** 1105 * Decode a String to an Integer. 1106 * 1107 * @param {String} str 1108 * @returns {baja.Integer} 1109 */ 1110 baja.Integer.prototype.decodeFromString = function (str) { 1111 if (str === "0") { 1112 return baja.Integer.DEFAULT; 1113 } 1114 // Min and max limits 1115 else if (str === "max") { 1116 return baja.Integer.MAX_VALUE; 1117 } 1118 else if (str === "min") { 1119 return baja.Integer.MIN_VALUE; 1120 } 1121 1122 return baja.Integer.make(Number(str)); 1123 }; 1124 1125 /** 1126 * Encode an Integer to a String. 1127 * 1128 * @returns {String} 1129 */ 1130 baja.Integer.prototype.encodeToString = function () { 1131 // Check range limits and NAN 1132 if (isNaN(this.$val)) { 1133 return "0"; 1134 } 1135 else if (this.$val >= baja.Integer.MAX_VALUE.valueOf()) { 1136 return "max"; 1137 } 1138 else if (this.$val <= baja.Integer.MIN_VALUE.valueOf()) { 1139 return "min"; 1140 } 1141 1142 return Math.floor(this.$val).toFixed(0); 1143 }; 1144 1145 /** 1146 * Default Integer instance. 1147 */ 1148 baja.Integer.DEFAULT = uncacheConstantEncodeDecode(new baja.Integer(0)); 1149 1150 /** 1151 * Integer Max Value. 1152 */ 1153 baja.Integer.MAX_VALUE = uncacheConstantEncodeDecode(new baja.Integer(2147483647)); 1154 1155 /** 1156 * Integer Min Value. 1157 */ 1158 baja.Integer.MIN_VALUE = uncacheConstantEncodeDecode(new baja.Integer(-2147483648)); 1159 1160 baja.Integer.prototype.encodeToString = cacheEncode(baja.Integer.prototype.encodeToString); 1161 baja.Integer.prototype.decodeFromString = cacheDecode(baja.Integer.prototype.decodeFromString); 1162 1163 // Register Type 1164 baja.Integer.registerType("baja:Integer"); 1165 1166 /** 1167 * Equality test. 1168 * 1169 * @function 1170 * 1171 * @param obj 1172 * @returns {Boolean} 1173 */ 1174 baja.Integer.prototype.equals = valueOfEquals; 1175 1176 /** 1177 * Return the Number encapsulated in the Integer. 1178 * 1179 * @returns {Number} Number for the Integer. 1180 */ 1181 baja.Integer.prototype.valueOf = function () { 1182 return this.$val; 1183 }; 1184 1185 /** 1186 * Returns a string representation of the integer. 1187 * @returns {String} 1188 */ 1189 baja.Integer.prototype.toString = function () { 1190 return String(this.valueOf()); 1191 }; 1192 1193 /** 1194 * Return the Symbol used for encoding this data type (primarily used for facets). 1195 * 1196 * @returns {String} the Symbol used for encoding this data type (primarily used for facets). 1197 */ 1198 baja.Integer.prototype.getDataTypeSymbol = function () { 1199 return "i"; 1200 }; 1201 1202 // TODO: Long can't really be represented in JavaScript. This needs more investigation 1203 1204 /** 1205 * @class Represents a baja:Long in BajaScript. 1206 * <p> 1207 * Boxes JavaScript Number to represent a baja:Long. 1208 * <p> 1209 * When creating a Simple, always use the 'make' method instead of creating a new Object. 1210 * 1211 * @name baja.Long 1212 * @extends baja.Simple 1213 */ 1214 baja.Long = function (val) { 1215 baja.Long.$super.apply(this, arguments); 1216 this.$val = strictArg(val, Number); 1217 }.$extend(baja.Simple); 1218 1219 /** 1220 * Make a Long. 1221 * 1222 * @param {Number} num the Long for the Simple. 1223 * @returns {baja.Long} the Long. 1224 */ 1225 baja.Long.make = function (val) { 1226 if (val === 0 || isNaN(val)) { 1227 return baja.Long.DEFAULT; 1228 } 1229 else if (val >= baja.Long.MAX_VALUE.valueOf() || val === Number.MAX_VALUE) { 1230 return baja.Long.MAX_VALUE; 1231 } 1232 else if (val <= baja.Long.MIN_VALUE.valueOf() || val === Number.MIN_VALUE) { 1233 return baja.Long.MIN_VALUE; 1234 } 1235 1236 val = Math.floor(val); 1237 1238 if (val === 0) { 1239 return baja.Long.DEFAULT; 1240 } 1241 1242 return new baja.Long(val); 1243 }; 1244 1245 /** 1246 * Make a Long. 1247 * 1248 * @param {Number} num the Long for the Simple. 1249 * @returns {baja.Long} the Long. 1250 */ 1251 baja.Long.prototype.make = function (val) { 1252 return baja.Long.make(val); 1253 }; 1254 1255 /** 1256 * Decode a Long from a String. 1257 * 1258 * @param {String} str 1259 * @returns {baja.Long} 1260 */ 1261 baja.Long.prototype.decodeFromString = function (str) { 1262 if (str === "0") { 1263 return baja.Long.DEFAULT; 1264 } 1265 // Min and max limits 1266 if (str === "max") { 1267 return baja.Long.MAX_VALUE; 1268 } 1269 else if (str === "min") { 1270 return baja.Long.MIN_VALUE; 1271 } 1272 1273 return baja.Long.make(Number(str)); 1274 }; 1275 1276 /** 1277 * Encode the Long to a String. 1278 * 1279 * @returns {String} 1280 */ 1281 baja.Long.prototype.encodeToString = function () { 1282 1283 // Check range limits and NAN 1284 if (isNaN(this.$val)) { 1285 return "0"; 1286 } 1287 else if (this.$val >= baja.Long.MAX_VALUE.valueOf()) { 1288 return "max"; 1289 } 1290 else if (this.$val <= baja.Long.MIN_VALUE.valueOf()) { 1291 return "min"; 1292 } 1293 1294 return Math.floor(this.$val).toFixed(0); 1295 }; 1296 1297 /** 1298 * Default Simple instance. 1299 */ 1300 baja.Long.DEFAULT = uncacheConstantEncodeDecode(new baja.Long(0)); 1301 1302 /** 1303 * Long Max Value. 1304 */ 1305 baja.Long.MAX_VALUE = uncacheConstantEncodeDecode(new baja.Long(9223372036854775807)); 1306 1307 /** 1308 * Long Min Value. 1309 */ 1310 baja.Long.MIN_VALUE = uncacheConstantEncodeDecode(new baja.Long(-9223372036854775808)); 1311 1312 baja.Long.prototype.decodeFromString = cacheDecode(baja.Long.prototype.decodeFromString); 1313 baja.Long.prototype.encodeToString = cacheEncode(baja.Long.prototype.encodeToString); 1314 1315 // Register Type 1316 baja.Long.registerType("baja:Long"); 1317 1318 /** 1319 * Equality test. 1320 * 1321 * @function 1322 * 1323 * @param obj 1324 * @returns {Boolean} 1325 */ 1326 baja.Long.prototype.equals = valueOfEquals; 1327 1328 /** 1329 * Return the Number encapsulated in the Long. 1330 * 1331 * @returns {Number} Number for the Long. 1332 */ 1333 baja.Long.prototype.valueOf = function () { 1334 return this.$val; 1335 }; 1336 1337 /** 1338 * Return the Symbol used for encoding this data type (primarily used for facets). 1339 * 1340 * @returns {String} the Symbol used for encoding this data type (primarily used for facets). 1341 */ 1342 baja.Long.prototype.getDataTypeSymbol = function () { 1343 return "l"; 1344 }; 1345 1346 /** 1347 * Returns a string representation of the integer. 1348 * 1349 * @returns {String} 1350 */ 1351 baja.Long.prototype.toString = function () { 1352 return String(this.valueOf()); 1353 }; 1354 1355 //////////////////////////////////////////////////////////////// 1356 // Facets 1357 //////////////////////////////////////////////////////////////// 1358 1359 /** 1360 * @class Represents a BFacets in BajaScript. 1361 * <p> 1362 * BFacets is a map of name/value pairs used to annotate a 1363 * BComplex's Slot or to just provide additional metadata 1364 * about something. The values of facets may only be 1365 * BIDataValues which are a predefined subset of simples. 1366 * <p> 1367 * When creating a Simple, always use the 'make' method instead of creating a new Object. 1368 * 1369 * @name baja.Facets 1370 * @extends baja.Simple 1371 */ 1372 baja.Facets = function (keys, vals) { 1373 baja.Facets.$super.apply(this, arguments); 1374 this.$map = new baja.OrderedMap(); 1375 strictArg(keys, Array, "Keys array required"); 1376 strictArg(vals, Array, "Values array required"); 1377 1378 if (keys.length !== vals.length) { 1379 throw new Error("baja.Facets Constructor must have an equal number of keys and values"); 1380 } 1381 if (keys.length === 0) { 1382 return; 1383 } 1384 1385 // Iterate through key, value pair arguments and add to the internal Map 1386 var dvt = baja.lt("baja:IDataValue"); 1387 var key, i; 1388 for (i = 0; i < keys.length; ++i) { 1389 if (typeof keys[i] !== 'string') { 1390 throw new Error("Facets keys must be a String"); 1391 } 1392 1393 baja.SlotPath.verifyValidName(keys[i]); 1394 1395 // Check everything being added here is a DataValue 1396 if (!vals[i].getType) { 1397 throw new Error("Facet value has no Baja Type associated with it"); 1398 } 1399 if (!vals[i].getType().is(dvt)) { 1400 throw new Error("Can only add baja:IDataValue Types to BFacets: " + vals[i].getType().getTypeSpec()); 1401 } 1402 1403 this.$map.put(keys[i], vals[i]); 1404 } 1405 }.$extend(baja.Simple); 1406 1407 /** 1408 * Default Facets instance. 1409 */ 1410 var facetsDefault = baja.Facets.DEFAULT = new baja.Facets([], []); 1411 1412 /** 1413 * NULL Facets instance. 1414 */ 1415 baja.Facets.NULL = facetsDefault; 1416 1417 function facetsExtend(orig, toAdd) { 1418 var obj = {}, newKeys = [], newValues = []; 1419 1420 baja.iterate(orig.getKeys(), function (key) { 1421 obj[key] = orig.get(key); 1422 }); 1423 1424 //overwrite any existing values with ones from toAdd 1425 baja.iterate(toAdd.getKeys(), function (key) { 1426 obj[key] = toAdd.get(key); 1427 }); 1428 1429 baja.iterate(obj, function (value, key) { 1430 newKeys.push(key); 1431 newValues.push(value); 1432 }); 1433 1434 return baja.Facets.make(newKeys, newValues); 1435 } 1436 1437 function facetsFromObj(obj) { 1438 var keys = [], 1439 values = []; 1440 1441 baja.iterate(obj, function (v, k) { 1442 keys.push(k); 1443 values.push(v); 1444 }); 1445 1446 return baja.Facets.make(keys, values); 1447 } 1448 1449 /** 1450 * Make a Facets object. This function can either take two Array objects 1451 * for keys and values, two Facets or two Object Literals. In the latter two cases, a new 1452 * Facets object will be returned containing a combination of keys and values 1453 * from both input Facets objects. If a key exists on both Facets objects, 1454 * the value from the second Facets will take precedence. 1455 * 1456 * @param {Array|baja.Facets|Object} keys an array of keys for the facets. The keys 1457 * must be Strings. (This may also be a Facets (or Object Literal) object whose values will be 1458 * combined with the second parameter. Values in this object will be 1459 * overwritten by corresponding values from the other). 1460 * @param {Array|baja.Facets|Object} values an array of values for the facets. The 1461 * values must be BajaScript Objects whose Type implements BIDataValue. 1462 * (This may also be a Facets (or Object Literal) object whose values will be combined with the 1463 * first parameter. Values in this object will overwrite corresponding values 1464 * on the other.) 1465 * @returns {baja.Facets} the Facets 1466 */ 1467 baja.Facets.make = function (keys, values) { 1468 // If there are no arguments are defined then return the default 1469 if (arguments.length === 0) { 1470 return facetsDefault; 1471 } 1472 1473 // Throw an error if no keys 1474 if (!keys) { 1475 throw new Error("Keys required"); 1476 } 1477 1478 // If the keys are an Object then convert to Facets 1479 if (keys.constructor === Object) { 1480 keys = facetsFromObj(keys); 1481 } 1482 1483 // If the values are an Object then convert to Facets 1484 if (values && values.constructor === Object) { 1485 values = facetsFromObj(values); 1486 } 1487 1488 if (keys instanceof baja.Facets) { 1489 // If keys and values are facets then merge 1490 if (values && values instanceof baja.Facets) { 1491 return facetsExtend(keys, values); 1492 } 1493 1494 // If just the keys are facets then just return them 1495 return keys; 1496 } 1497 else { 1498 // If we've got here then we assume the keys and values are arrays... 1499 if (keys.length === 0) { 1500 return facetsDefault; 1501 } 1502 1503 // Note: I could do more argument checking here but I don't want to slow this down 1504 // more than necessary. 1505 1506 return new baja.Facets(keys, values); 1507 } 1508 }; 1509 1510 /** 1511 * Make a Facets. 1512 * 1513 * @param {Array} keys an array of keys for the facets. The keys must be Strings. 1514 * @param {Array} values an array of values for the facets. The values must be BajaScript Objects whose 1515 * Type implements BIDataValue. 1516 * @returns {baja.Facets} the Facets. 1517 */ 1518 baja.Facets.prototype.make = function (args) { 1519 return baja.Facets.make.apply(baja.Facets, arguments); 1520 }; 1521 1522 /** 1523 * Decode a String to a Facets. 1524 * 1525 * @param {String} str 1526 * @returns {baja.Facets} 1527 */ 1528 baja.Facets.prototype.decodeFromString = function (str) { 1529 if (str.length === 0) { 1530 return facetsDefault; 1531 } 1532 1533 var a = str.split("|"), // array 1534 a2, // inner array 1535 keys = [], 1536 vals = [], 1537 dv, // decoded values 1538 v; // decoded value 1539 1540 // TODO: Since we haven't yet implemented all of the datatypes for Facets 1541 // we are hacking around the issue by creating the getDataTypeSymbol methods on a 1542 // DefaultSimple on the fly. This will have to do until the other Simples needed 1543 // for the other datatypes have been created 1544 function addDataType(v, dt) { 1545 v.getDataTypeSymbol = function () { 1546 return dt; 1547 }; 1548 1549 if (dt === "u") { 1550 /** 1551 * @ignore - get JsDoc to ignore this symbols so it's not picked up accidently. 1552 */ 1553 v.getType = function () { 1554 return baja.lt("baja:Unit"); 1555 }; 1556 } 1557 else if (dt === "z") { 1558 /** 1559 * @ignore - get JsDoc to ignore this symbols so it's not picked up accidently. 1560 */ 1561 v.getType = function () { 1562 return baja.lt("baja:TimeZone"); 1563 }; 1564 } 1565 } 1566 1567 // Find the first separator and then split the String 1568 function splitFirst(str, sep) { 1569 var ind = str.indexOf(sep); 1570 var a = []; 1571 if (ind === -1) { 1572 a.push(str); 1573 } 1574 else { 1575 a.push(str.substring(0, ind)); 1576 if (ind < (str.length - 1)) { 1577 a.push(str.substring(ind + 1, str.length)); 1578 } 1579 } 1580 return a; 1581 } 1582 1583 var i; 1584 for (i = 0; i < a.length; ++i) { 1585 1586 a2 = splitFirst(a[i], "="); 1587 1588 baja.SlotPath.verifyValidName(a2[0]); 1589 1590 keys.push(a2[0]); 1591 1592 // Unmarshal data value from String 1593 dv = splitFirst(a2[1], ":"); 1594 1595 if (dv.length === 1) { 1596 dv.push(""); 1597 } 1598 1599 if (dv[0] === "s") { 1600 v = baja.SlotPath.unescape(dv[1]); 1601 } 1602 else if (dv[0] === "b") { 1603 v = Boolean.DEFAULT.decodeFromString(dv[1]); 1604 } 1605 else if (dv[0] === "i") { 1606 v = baja.Integer.DEFAULT.decodeFromString(dv[1]); 1607 } 1608 else if (dv[0] === "l") { 1609 v = baja.Long.DEFAULT.decodeFromString(dv[1]); 1610 } 1611 else if (dv[0] === "f") { 1612 v = baja.Float.DEFAULT.decodeFromString(dv[1]); 1613 } 1614 else if (dv[0] === "d") { 1615 v = Number.DEFAULT.decodeFromString(dv[1]); 1616 } 1617 else if (dv[0] === "a") { 1618 v = baja.AbsTime.DEFAULT.decodeFromString(dv[1]); 1619 } 1620 else if (dv[0] === "E") { 1621 v = baja.EnumRange.DEFAULT.decodeFromString(dv[1]); 1622 } 1623 else if (dv[0] === "e") { 1624 v = baja.DynamicEnum.DEFAULT.decodeFromString(dv[1]); 1625 } 1626 else if (dv[0] === "r") { 1627 v = baja.RelTime.DEFAULT.decodeFromString(dv[1]); 1628 } 1629 else { 1630 // TODO: Need to get around to decoding BUnits and other essential data types. 1631 // Currently this is one big hack 1632 1633 v = new baja.DefaultSimple(dv[1]); 1634 addDataType(v, dv[0]); 1635 } 1636 1637 vals.push(v); 1638 } 1639 1640 return baja.Facets.make(keys, vals); 1641 }; 1642 baja.Facets.prototype.decodeFromString = cacheDecode(baja.Facets.prototype.decodeFromString); 1643 1644 /** 1645 * Encode Facets to a String. 1646 * 1647 * @returns {String} 1648 */ 1649 baja.Facets.prototype.encodeToString = function () { 1650 var s = "", // TODO: This needs more work for data encoding 1651 k = this.$map.getKeys(), 1652 v, i; 1653 for (i = 0; i < k.length; ++i) { 1654 if (i > 0) { 1655 s += "|"; 1656 } 1657 1658 v = this.$map.get(k[i]); 1659 1660 if (v.getDataTypeSymbol === undefined) { 1661 throw new Error("Cannot encode data type as 'getDataTypeSymbol' is not defined: " + v.getType()); 1662 } 1663 1664 // If a String then escape it 1665 if (v.getDataTypeSymbol() === "s") { 1666 v = baja.SlotPath.escape(v); 1667 } 1668 1669 s += k[i] + "=" + v.getDataTypeSymbol() + ":" + v.encodeToString(); 1670 } 1671 return s; 1672 }; 1673 baja.Facets.prototype.encodeToString = cacheEncode(baja.Facets.prototype.encodeToString); 1674 1675 // Register Type 1676 baja.Facets.registerType("baja:Facets"); 1677 1678 /** 1679 * Return a value from the map for the given key 1680 * 1681 * @param {String} key the key used to look up the data value 1682 * @param [def] if defined, this value is returned if the key can't be found. 1683 * @returns the data value for the key (null if not found) 1684 */ 1685 baja.Facets.prototype.get = function (key, def) { 1686 strictArg(key, String); 1687 def = bajaDef(def, null); 1688 var v = this.$map.get(key); 1689 return v === null ? def : v; 1690 }; 1691 1692 /** 1693 * Return a copy of the Facets keys. 1694 * 1695 * @returns {Array} all of the keys (String) used in the Facets 1696 */ 1697 baja.Facets.prototype.getKeys = function () { 1698 return this.$map.getKeys(); 1699 }; 1700 1701 /** 1702 * Return a String representation of the Facets. 1703 * 1704 * @returns {String} 1705 */ 1706 baja.Facets.prototype.toString = function () { 1707 var str = "", 1708 keys = this.$map.getKeys(), 1709 i; 1710 1711 for (i = 0; i < keys.length; ++i) { 1712 if (i > 0) { 1713 str += ","; 1714 } 1715 str += keys[i] + "=" + this.$map.get(keys[i]).toString(); 1716 } 1717 return str; 1718 }; 1719 1720 /** 1721 * Return the value of the Facets. 1722 * @function 1723 * 1724 * @returns {String} 1725 */ 1726 baja.Facets.prototype.valueOf = baja.Facets.prototype.toString; 1727 1728 /** 1729 * Return the facets for the given object. 1730 * <p> 1731 * If the facets can't be found then baja.Facets.DEFAULT is returned. 1732 * 1733 * @param obj 1734 * @returns {baja.Facets} 1735 */ 1736 baja.Facets.getFacetsFromObject = function (obj) { 1737 var val = facetsDefault; 1738 1739 if (obj.getType().is("baja:Facets")) { 1740 val = obj; 1741 } 1742 else if (obj.getType().isComplex()) { 1743 if (obj.has("facets")) { 1744 val = obj.getFacets(); 1745 } 1746 else if (obj.getParent()) { 1747 val = obj.getParent().getFacets(obj.getPropertyInParent()); 1748 } 1749 } 1750 1751 return val; 1752 }; 1753 1754 //////////////////////////////////////////////////////////////// 1755 // Enums 1756 //////////////////////////////////////////////////////////////// 1757 1758 /** 1759 * @class Represents a BEnumRange in BajaScript. 1760 * <p> 1761 * An EnumRange stores a range of ordinal/name pairs for Enumerations. 1762 * <p> 1763 * When creating a Simple, always use the 'make' method instead of creating a new Object. 1764 * 1765 * @name baja.EnumRange 1766 * @extends baja.Simple 1767 */ 1768 baja.EnumRange = function (frozen, dynamic, byOrdinal, byTag, options) { 1769 baja.EnumRange.$super.apply(this, arguments); 1770 this.$frozen = frozen; 1771 this.$dynamic = strictArg(dynamic, Array); 1772 this.$byOrdinal = strictArg(byOrdinal, Object); 1773 this.$byTag = strictArg(byTag, Object); 1774 this.$options = strictArg(options, baja.Facets); 1775 }.$extend(baja.Simple); 1776 1777 /** 1778 * Make an EnumRange. 1779 * <p> 1780 * The TypeSpec for a FrozenEnum can be used as the first argument. If other arguments 1781 * are required then an Object Literal is used to to specify the method's arguments. 1782 * <pre> 1783 * // For example... 1784 * var er = baja.EnumRange.make({ 1785 * ordinals: [0, 1, 2], 1786 * tags: ["A", "B", "C"] 1787 * }); 1788 * </pre> 1789 * <p> 1790 * 1791 * @param {Object} [obj] the Object Literal that holds the method's arguments. 1792 * @param {String|Type} [frozen] the Type or TypeSpec for the FrozenEnum. 1793 * @param {Array} [ordinals] an array of numbers that specify the dynamic enum ordinals. 1794 * @param {Array} [tags] an array of strings that specify the dynamic enum tags. 1795 * @param {baja.Facets} [facets] optional facets. 1796 * @returns {baja.EnumRange} the EnumRange. 1797 */ 1798 baja.EnumRange.make = function (obj) { 1799 obj = objectify(obj, "frozen"); 1800 1801 var frozen = bajaDef(obj.frozen, null), 1802 ordinals = obj.ordinals, 1803 tags = obj.tags, 1804 count = obj.count, 1805 options = obj.options; 1806 1807 // Support String typespec as well as type 1808 if (typeof frozen === "string") { 1809 frozen = baja.lt(frozen); 1810 } 1811 1812 if (ordinals === undefined) { 1813 ordinals = []; 1814 } 1815 if (tags === undefined) { 1816 tags = []; 1817 } 1818 if (count === undefined && ordinals instanceof Array) { 1819 count = ordinals.length; 1820 } 1821 if (options === undefined) { 1822 options = facetsDefault; 1823 } 1824 1825 strictAllArgs([ordinals, tags, count, options], [Array, Array, Number, baja.Facets]); 1826 1827 if (ordinals.length !== tags.length) { 1828 throw new Error("Ordinals and tags arrays must match in length"); 1829 } 1830 1831 // optimization 1832 if (count === 0 && options === facetsDefault) { 1833 if (frozen === null) { 1834 return baja.EnumRange.DEFAULT; 1835 } 1836 else { 1837 return new baja.EnumRange(frozen, [], {}, {}, options); 1838 } 1839 } 1840 1841 var byOrdinal = {}, 1842 byTag = {}, 1843 dynaOrdinals = [], 1844 o, t, i; 1845 1846 for (i = 0; i < count; ++i) { 1847 o = ordinals[i]; 1848 t = tags[i]; 1849 1850 // Check for undefined due to JavaScript loading sequence 1851 if (baja.SlotPath !== undefined) { 1852 baja.SlotPath.verifyValidName(t); 1853 } 1854 1855 // check against frozen 1856 if (frozen !== null && frozen.isOrdinal(o)) { 1857 continue; 1858 } 1859 1860 // check duplicate ordinal 1861 if (byOrdinal.hasOwnProperty(o)) { 1862 throw new Error("Duplicate ordinal: " + t + "=" + o); 1863 } 1864 1865 // check duplicate tag 1866 if (byTag.hasOwnProperty(t) || 1867 (frozen && frozen.isTag(t))) { 1868 throw new Error("Duplicate tag: " + t + "=" + o); 1869 } 1870 1871 // put into map 1872 byOrdinal[o] = t; 1873 byTag[t] = o; 1874 dynaOrdinals.push(o); 1875 } 1876 1877 return new baja.EnumRange(frozen, dynaOrdinals, byOrdinal, byTag, options); 1878 1879 }; 1880 1881 /** 1882 * Make an EnumRange. 1883 * <p> 1884 * The TypeSpec for a FrozenEnum can be used as the first argument. If other arguments 1885 * are required then an Object Literal is used to to specify the method's arguments. 1886 * <pre> 1887 * // For example... 1888 * var er = baja.$("baja:EnumRange").make({ 1889 * ordinals: [0, 1, 2], 1890 * tags: ["A", "B", "C"] 1891 * }); 1892 * </pre> 1893 * <p> 1894 * 1895 * @param {Object} [obj] the Object Literal that holds the method's arguments. 1896 * @param {String|Type} [frozen] the Type or TypeSpec for the FrozenEnum. 1897 * @param {Array} [ordinals] an array of numbers that specify the dynamic enum ordinals. 1898 * @param {Array} [tags] an array of strings that specify the dynamic enum tags. 1899 * @param {baja.Facets} [facets] optional facets. 1900 * @returns {baja.EnumRange} the EnumRange . 1901 */ 1902 baja.EnumRange.prototype.make = function (obj) { 1903 return baja.EnumRange.make.apply(baja.EnumRange, arguments); 1904 }; 1905 1906 function splitFrozenDynamic(s) { 1907 var frozen = null; 1908 var dynamic = null; 1909 var plus = s.indexOf('+'); 1910 if (plus < 0) { 1911 if (s.indexOf("{") === 0) { 1912 dynamic = s; 1913 } 1914 else { 1915 frozen = s; 1916 } 1917 } 1918 else { 1919 if (s.indexOf("{") === 0) { 1920 dynamic = s.substring(0, plus); 1921 frozen = s.substring(plus + 1); 1922 } 1923 else { 1924 frozen = s.substring(0, plus); 1925 dynamic = s.substring(plus + 1); 1926 } 1927 } 1928 return [frozen, dynamic]; 1929 } 1930 1931 function parseDynamic(s, ordinals, tags) { 1932 var count = 0, 1933 ordinal, 1934 st = s.split(/[=,]/), 1935 i; 1936 1937 for (i = 0; i < st.length; ++i) { 1938 tags[count] = st[i]; 1939 ordinal = parseInt(st[++i], 10); 1940 if (isNaN(ordinal)) { 1941 throw new Error("Invalid ordinal: " + st[i]); 1942 } 1943 ordinals[count] = ordinal; 1944 count++; 1945 } 1946 return count; 1947 } 1948 1949 /** 1950 * Decode an EnumRange from a String. 1951 * 1952 * @param {String} str 1953 * @returns {baja.EnumRange} 1954 */ 1955 baja.EnumRange.prototype.decodeFromString = function (str) { 1956 if (str === "{}") { 1957 return baja.EnumRange.DEFAULT; 1958 } 1959 1960 // split body from options (there can't be a question mark 1961 // anywhere until after the frozen type or dynamic {} 1962 var options = facetsDefault, 1963 question = str.indexOf('?'); 1964 if (question > 0) { 1965 options = facetsDefault.decodeFromString(str.substring(question + 1)); 1966 str = str.substring(0, question); 1967 } 1968 1969 // split dynamic and frozen tokens 1970 var split = splitFrozenDynamic(str), 1971 frozenStr = split[0], 1972 dynamicStr = split[1], 1973 frozen = null; 1974 1975 // get frozen 1976 if (frozenStr !== null) { 1977 frozen = baja.lt(frozenStr); 1978 if (frozen === null) { 1979 throw new Error("Invalid frozen EnumRange spec: " + frozenStr); 1980 } 1981 } 1982 1983 if (dynamicStr === null) { 1984 return this.make({ 1985 "frozen": frozen, 1986 "options": options 1987 }); 1988 } 1989 1990 // check for required braces on dynamic 1991 if (dynamicStr.charAt(0) !== "{") { 1992 throw new Error("Missing {"); 1993 } 1994 if (dynamicStr.charAt(dynamicStr.length - 1) !== "}") { 1995 throw new Error("Missing }"); 1996 } 1997 dynamicStr = dynamicStr.substring(1, dynamicStr.length - 1); 1998 1999 // get dynamic 2000 var ordinals = [], 2001 tags = [], 2002 count = parseDynamic(dynamicStr, ordinals, tags); 2003 2004 return this.make({ 2005 "frozen": frozen, 2006 "ordinals": ordinals, 2007 "tags": tags, 2008 "count": count, 2009 "options": options 2010 }); 2011 }; 2012 2013 /** 2014 * Encode an EnumRange to a String. 2015 * 2016 * @returns {String} 2017 */ 2018 baja.EnumRange.prototype.encodeToString = function () { 2019 // Optimization for encoding 2020 if (this === baja.EnumRange.DEFAULT) { 2021 return "{}"; 2022 } 2023 2024 var s = "", 2025 key, 2026 tag, i; 2027 2028 if (this.$frozen !== null) { 2029 s += this.$frozen.getTypeSpec(); 2030 } 2031 2032 if (this.$dynamic.length > 0 || this.$frozen === null) { 2033 if (s.length > 0) { 2034 s += "+"; 2035 } 2036 2037 s += "{"; 2038 for (i = 0; i < this.$dynamic.length; ++i) { 2039 key = this.$dynamic[i]; 2040 tag = this.$byOrdinal[key]; 2041 if (i > 0) { 2042 s += ","; 2043 } 2044 s += tag + "=" + key; 2045 } 2046 s += "}"; 2047 } 2048 2049 if (this.$options !== facetsDefault) { 2050 s += "?" + this.$options.encodeToString(); 2051 } 2052 2053 return s; 2054 }; 2055 2056 /** 2057 * Default EnumRange instance. 2058 */ 2059 baja.EnumRange.DEFAULT = uncacheConstantEncodeDecode(new baja.EnumRange(null, [], {}, {}, facetsDefault)); 2060 2061 baja.EnumRange.prototype.decodeFromString = cacheDecode(baja.EnumRange.prototype.decodeFromString); 2062 baja.EnumRange.prototype.encodeToString = cacheEncode(baja.EnumRange.prototype.encodeToString); 2063 2064 // Register Type 2065 baja.EnumRange.registerType("baja:EnumRange"); 2066 2067 /** 2068 * Return the data type symbol. 2069 * 2070 * @returns {String} data type symbol. 2071 */ 2072 baja.EnumRange.prototype.getDataTypeSymbol = function () { 2073 return "E"; 2074 }; 2075 2076 /** 2077 * Return all of the ordinals for the EnumRange. 2078 * <p> 2079 * The returned array contains both frozen and enum ordinals. 2080 * 2081 * @returns {Array} an array of numbers that represents the ordinals for this EnumRange. 2082 */ 2083 baja.EnumRange.prototype.getOrdinals = function () { 2084 var ordinals, i; 2085 if (this.$frozen !== null) { 2086 ordinals = this.$frozen.getOrdinals(); 2087 } 2088 else { 2089 ordinals = []; 2090 } 2091 for (i = 0; i < this.$dynamic.length; ++i) { 2092 ordinals.push(this.$dynamic[i]); 2093 } 2094 return ordinals; 2095 }; 2096 2097 /** 2098 * Return true if the ordinal is valid in this EnumRange. 2099 * 2100 * @param {Number} ordinal 2101 * @returns {Boolean} true if valid 2102 */ 2103 baja.EnumRange.prototype.isOrdinal = function (ordinal) { 2104 strictArg(ordinal, Number); 2105 if (this.$frozen !== null && this.$frozen.isOrdinal(ordinal)) { 2106 return true; 2107 } 2108 return this.$byOrdinal.hasOwnProperty(ordinal); 2109 }; 2110 2111 /** 2112 * Return the tag for the specified ordinal. 2113 * <p> 2114 * If the ordinal isn't valid then the ordinal is returned 2115 * as a String. 2116 * 2117 * @param {Number} ordinal 2118 * @returns {String} tag 2119 */ 2120 baja.EnumRange.prototype.getTag = function (ordinal) { 2121 strictArg(ordinal, Number); 2122 if (this.$byOrdinal.hasOwnProperty(ordinal)) { 2123 return this.$byOrdinal[ordinal]; 2124 } 2125 2126 if (this.$frozen !== null && this.$frozen.isOrdinal(ordinal)) { 2127 return this.$frozen.getTag(ordinal); 2128 } 2129 2130 return String(ordinal); 2131 }; 2132 2133 /** 2134 * Return true if the tag is used within the EnumRange. 2135 * 2136 * @param {String} tag 2137 * @returns {Boolean} true if valid. 2138 */ 2139 baja.EnumRange.prototype.isTag = function (tag) { 2140 strictArg(tag, String); 2141 if (this.$frozen !== null && this.$frozen.isTag(tag)) { 2142 return true; 2143 } 2144 2145 return this.$byTag.hasOwnProperty(tag); 2146 }; 2147 2148 /** 2149 * Convert the tag to its ordinal within the EnumRange. 2150 * 2151 * @param {String} tag 2152 * @returns {Number} ordinal for the tag. 2153 * @throws error if the tag is invalid. 2154 */ 2155 baja.EnumRange.prototype.tagToOrdinal = function (tag) { 2156 strictArg(tag, String); 2157 if (this.$frozen !== null && this.$frozen.isTag(tag)) { 2158 return this.$frozen.tagToOrdinal(tag); 2159 } 2160 2161 if (this.$byTag.hasOwnProperty(tag)) { 2162 return this.$byTag[tag]; 2163 } 2164 else { 2165 throw new Error("Invalid tag: " + tag); 2166 } 2167 }; 2168 2169 /** 2170 * Get the enum for the specified tag or ordinal. 2171 * <p> 2172 * This method is used to access an enum based upon a tag or ordinal. 2173 * 2174 * @param {String|Number} arg a tag or ordinal. 2175 * @returns {baja.DynamicEnum|baja.FrozenEnum|Boolean} the enum for the tag or ordinal. 2176 * @throws error if the tag or ordinal is invalid. 2177 */ 2178 baja.EnumRange.prototype.get = function (arg) { 2179 2180 if (typeof arg === "string") { 2181 if (this === baja.EnumRange.BOOLEAN_RANGE) { 2182 return arg === "false" ? false : true; 2183 } 2184 2185 // Look up via tag name 2186 if (this.$frozen !== null && this.$frozen.isTag(arg)) { 2187 return this.$frozen.getFrozenEnum(arg); 2188 } 2189 2190 if (this.$byTag.hasOwnProperty(arg)) { 2191 return baja.DynamicEnum.make({"ordinal": this.$byTag[arg], "range": this}); 2192 } 2193 } 2194 else if (typeof arg === "number") { 2195 if (this === baja.EnumRange.BOOLEAN_RANGE) { 2196 return arg === 0 ? false : true; 2197 } 2198 2199 // Look up via ordinal 2200 if (this.$frozen !== null && this.$frozen.isOrdinal(arg)) { 2201 return this.$frozen.getFrozenEnum(arg); 2202 } 2203 2204 if (this.isOrdinal(arg)) { 2205 return baja.DynamicEnum.make({"ordinal": arg, "range": this}); 2206 } 2207 } 2208 2209 throw new Error("Unable to access enum"); 2210 }; 2211 2212 /** 2213 * Return true if the ordinal is a valid ordinal in the frozen range. 2214 * 2215 * @param {Number} ordinal 2216 * @returns {Boolean} true if valid. 2217 */ 2218 baja.EnumRange.prototype.isFrozenOrdinal = function (ordinal) { 2219 strictArg(ordinal, Number); 2220 return this.$frozen !== null && this.$frozen.isOrdinal(ordinal); 2221 }; 2222 2223 /** 2224 * Return true if the ordinal is a valid ordinal in the dynamic range. 2225 * 2226 * @param {Number} ordinal 2227 * @returns {Boolean} true if valid 2228 */ 2229 baja.EnumRange.prototype.isDynamicOrdinal = function (ordinal) { 2230 strictArg(ordinal, Number); 2231 return this.$byOrdinal.hasOwnProperty(ordinal); 2232 }; 2233 2234 /** 2235 * Return the Type used for the frozen enum range or null if this range 2236 * has no frozen ordinal/tag pairs. 2237 * 2238 * @returns {Type} the Type for the FrozenEnum or null. 2239 */ 2240 baja.EnumRange.prototype.getFrozenType = function () { 2241 return this.$frozen; 2242 }; 2243 2244 /** 2245 * Get the options for this range stored as a Facets instance. 2246 * 2247 * @returns {baja.Facets} facets 2248 */ 2249 baja.EnumRange.prototype.getOptions = function () { 2250 return this.$options; 2251 }; 2252 2253 /** 2254 * Boolean EnumRange. 2255 */ 2256 baja.EnumRange.BOOLEAN_RANGE = baja.EnumRange.make({ 2257 "ordinals": [0, 1], 2258 "tags": ["false", "true"] 2259 }); 2260 2261 //////////////////////////////////////////////////////////////// 2262 // EnumSet 2263 //////////////////////////////////////////////////////////////// 2264 2265 /** 2266 * @class Represents a baja:EnumSet in BajaScript. 2267 * <p> 2268 * An EnumSet contains an EnumRange and an array of ordinals. 2269 * <p> 2270 * When creating a Simple, always use the 'make' method instead of creating a new Object. 2271 * 2272 * @name baja.EnumSet 2273 * @extends baja.Simple 2274 */ 2275 baja.EnumSet = function (ordinals, range) { 2276 baja.EnumSet.$super.apply(this, arguments); 2277 this.$ordinals = strictArg(ordinals); 2278 this.$range = strictArg(range, Object); 2279 }.$extend(baja.Simple); 2280 2281 /** 2282 * Make an EnumSet. An EnumSet can be created using either an array of 2283 * ordinals (in which case the range will be set to 2284 * <code>baja.EnumRange.DEFAULT</code>), or, to specify a range as well, an 2285 * object literal with <code>ordinals</code> and <code>range</code> 2286 * properties. 2287 * <pre> 2288 * // For example... 2289 * var defaultRange = baja.EnumSet.make([0, 1, 2]); 2290 * var customRange = baja.EnumSet.make({ 2291 * ordinals: [0, 2, 4], 2292 * range: baja.EnumRange.make({ 2293 * ordinals: [0, 1, 2, 3, 4], 2294 * tags: ['a', 'b', 'c', 'd', 'e'] 2295 * }) 2296 * }); 2297 * </pre> 2298 * <p> 2299 * 2300 * @param {Object|Array} obj the Object Literal that holds the method's arguments 2301 * (or an array of ordinals). 2302 * @param {Array} [obj.ordinals] an array of ordinals. 2303 * @param {baja.EnumRange} [obj.range] the EnumRange to assign the EnumSet. 2304 * @returns {baja.EnumSet} the EnumSet. 2305 */ 2306 baja.EnumSet.make = function (obj) { 2307 obj = objectify(obj, "ordinals"); 2308 2309 var ordinals = obj.ordinals, 2310 range = obj.range, 2311 count; 2312 2313 if (ordinals === undefined) { 2314 ordinals = []; 2315 } 2316 2317 if (range === undefined) { 2318 range = baja.EnumRange.DEFAULT; 2319 } 2320 2321 strictArg(ordinals, Array); 2322 strictArg(range, baja.EnumRange); 2323 2324 // optimization 2325 if (ordinals.length === 0 && range === baja.EnumRange.DEFAULT) { 2326 return baja.EnumSet.NULL; 2327 } 2328 2329 return new baja.EnumSet(ordinals, range); 2330 }; 2331 2332 /** 2333 * Make an EnumSet. Same as static method <code>baja.EnumSet.make</code>. 2334 * 2335 * @see baja.EnumSet.make 2336 */ 2337 baja.EnumSet.prototype.make = function (obj) { 2338 return baja.EnumSet.make.apply(baja.EnumSet, arguments); 2339 }; 2340 2341 /** 2342 * Decode an EnumSet from a String. 2343 * 2344 * @param {String} str 2345 * @returns {baja.EnumSet} 2346 */ 2347 baja.EnumSet.prototype.decodeFromString = cacheDecode(function decodeFromString(str) { 2348 // parse range if specified 2349 var ordinals = [], 2350 ordinal, 2351 range, 2352 at = str.indexOf('@'), 2353 split, 2354 count, 2355 i; 2356 2357 if (at >= 0) { 2358 range = baja.EnumRange.DEFAULT.decodeFromString(str.substring(at + 1)); 2359 str = str.substring(0, at); 2360 } 2361 2362 if (str.length) { 2363 split = str.split(','); 2364 count = split.length; 2365 for (i = 0; i < count; i++) { 2366 ordinal = parseInt(split[i], 10); 2367 if (isNaN(ordinal)) { 2368 throw new Error("Invalid ordinal: " + split[i]); 2369 } 2370 ordinals.push(ordinal); 2371 } 2372 } 2373 2374 return this.make({ 2375 ordinals: ordinals, 2376 range: range 2377 }); 2378 }); 2379 2380 /** 2381 * Encode an EnumSet to a String. 2382 * 2383 * @returns {String} 2384 */ 2385 baja.EnumSet.prototype.encodeToString = cacheEncode(function encodeToString() { 2386 var ordinals = this.$ordinals, 2387 range = this.$range, 2388 s = ordinals.join(','), 2389 i; 2390 2391 if (range && (range !== baja.EnumRange.DEFAULT)) { 2392 s += '@' + range.encodeToString(); 2393 } 2394 return s; 2395 }); 2396 2397 /** 2398 * Return the data type symbol (E). 2399 * 2400 * @returns {String} data type symbol 2401 */ 2402 baja.EnumSet.prototype.getDataTypeSymbol = function () { 2403 return "E"; 2404 }; 2405 2406 /** 2407 * Return all of the ordinals for the EnumSet 2408 * 2409 * @returns {Array} an array of numbers that represents the ordinals for this EnumSet. 2410 */ 2411 baja.EnumSet.prototype.getOrdinals = function () { 2412 return this.$ordinals; 2413 }; 2414 2415 baja.EnumSet.prototype.getRange = function () { 2416 return this.$range; 2417 }; 2418 2419 /** 2420 * Default EnumSet instance. 2421 */ 2422 baja.EnumSet.DEFAULT = new baja.EnumSet([], baja.EnumRange.DEFAULT); 2423 baja.EnumSet.NULL = baja.EnumSet.DEFAULT; 2424 2425 // Register Type 2426 baja.EnumSet.registerType("baja:EnumSet"); 2427 2428 /** 2429 * @class Represents a baja:Enum in BajaScript. 2430 * 2431 * @name baja.Enum 2432 * @extends baja.Simple 2433 */ 2434 baja.Enum = function () { 2435 baja.Enum.$super.apply(this, arguments); 2436 }.$extend(baja.Simple); 2437 2438 /** 2439 * Return the enum from a BIEnum. 2440 * 2441 * @return resolved enum value 2442 */ 2443 baja.Enum.getEnumFromIEnum = function (enumVal) { 2444 var val = enumVal; 2445 2446 if (enumVal.getType().isComplex() && enumVal.has("out") && enumVal.getOut().getType().is("baja:StatusValue")) { 2447 val = enumVal.getOut().getValue(); 2448 } 2449 else if (enumVal.getType().is("baja:StatusValue")) { 2450 val = enumVal.getValue(); 2451 } 2452 2453 return val; 2454 }; 2455 2456 /** 2457 * @class Represents a baja:DynamicEnum in BajaScript. 2458 * <p> 2459 * BDynamicEnum stores an ordinal state variable as 2460 * a Number. An instance of BEnumRange may be used to specify the range. 2461 * <p> 2462 * When creating a Simple, always use the 'make' method instead of creating a new Object. 2463 * 2464 * @name baja.DynamicEnum 2465 * @extends baja.Enum 2466 */ 2467 baja.DynamicEnum = function (ordinal, range) { 2468 baja.DynamicEnum.$super.apply(this, arguments); 2469 this.$ordinal = strictArg(ordinal, Number); 2470 this.$range = strictArg(range || baja.EnumRange.DEFAULT, baja.EnumRange); 2471 }.$extend(baja.Enum); 2472 2473 /** 2474 * Make a DynamicEnum 2475 * <p> 2476 * An ordinal or an Object Literal can be used for the method's arguments... 2477 * <pre> 2478 * var de1 = baja.DynamicEnum.make(0); // Just with an ordinal 2479 * 2480 * //... or with an Object Literal... 2481 * 2482 * var de2 = baja.DynamicEnum.make({ordinal: 0, range: enumRange}); 2483 * 2484 * //...or create from another enumeration... 2485 * 2486 * var de3 = baja.DynamicEnum.make({en: anEnum}); 2487 * </pre> 2488 * 2489 * @param {Object|Number} [obj] the Object Literal for the method's arguments or an ordinal. 2490 * @param {Number} [obj.ordinal] the ordinal for the enum. 2491 * @param {baja.EnumRange} [obj.range] the range for the enum. 2492 * @param {baja.DynamicEnum|baja.FrozenEnum|Boolean|String} [obj.en] if defined, this enum will be used for the ordinal and range. 2493 * As well as an enum, this can also be a TypeSpec 2494 * String (moduleName:typeName) for a FrozenEnum. 2495 * @returns {baja.DynamicEnum} the DynamicEnum. 2496 */ 2497 baja.DynamicEnum.make = function (obj) { 2498 obj = objectify(obj, "ordinal"); 2499 2500 var ordinal = bajaDef(obj.ordinal, 0); 2501 var range = bajaDef(obj.range, baja.EnumRange.DEFAULT); 2502 2503 // Create from another enumeration... 2504 var en; 2505 if (obj.en !== undefined) { 2506 en = obj.en; 2507 2508 if (!bajaHasType(en)) { 2509 throw new Error("Invalid Enum Argument"); 2510 } 2511 2512 if (en.getType().is("baja:DynamicEnum")) { 2513 return en; 2514 } 2515 2516 // If a type spec is passed in then resolve it to the enum instance 2517 if (typeof en === "string") { 2518 en = baja.$(en); 2519 } 2520 2521 if (en.getType().isFrozenEnum() || en.getType().is("baja:Boolean")) { 2522 ordinal = en.getOrdinal(); 2523 range = en.getRange(); 2524 } 2525 else { 2526 throw new Error("Argument must be an Enum"); 2527 } 2528 } 2529 2530 // Check for default 2531 if (ordinal === 0 && range === baja.EnumRange.DEFAULT) { 2532 return baja.DynamicEnum.DEFAULT; 2533 } 2534 2535 return new baja.DynamicEnum(ordinal, range); 2536 }; 2537 2538 /** 2539 * Make a DynamicEnum. 2540 * <p> 2541 * An ordinal or an Object Literal can be used for the method's arguments... 2542 * <pre> 2543 * var de1 = baja.$("baja:DynamicEnum").make(0); // Just with an ordinal 2544 * 2545 * //... or with an Object Literal... 2546 * 2547 * var de2 = baja.$("baja:DynamicEnum").make({ordinal: 0, range: enumRange}); 2548 * 2549 * //...or create from another enumeration... 2550 * 2551 * var de3 = baja.$("baja:DynamicEnum").make({en: anEnum}); 2552 * </pre> 2553 * 2554 * @param {Object|Number} [obj] the Object Literal for the method's arguments or an ordinal. 2555 * @param {Number} [obj.ordinal] the ordinal for the enum. 2556 * @param {baja.EnumRange} [obj.range] the range for the enum. 2557 * @param {baja.DynamicEnum|baja.FrozenEnum|Boolean|String} [obj.en] if defined, this enum will be used for the ordinal and range. 2558 * As well as an enum, this can also be a TypeSpec 2559 * String (moduleName:typeName) for a FrozenEnum. 2560 * @returns {baja.DynamicEnum} the DynamicEnum. 2561 */ 2562 baja.DynamicEnum.prototype.make = function (obj) { 2563 return baja.DynamicEnum.make.apply(baja.DynamicEnum, arguments); 2564 }; 2565 2566 /** 2567 * Decode a DynamicEnum from a String. 2568 * 2569 * @param {String} str 2570 * @returns {baja.DynamicEnum} 2571 */ 2572 baja.DynamicEnum.prototype.decodeFromString = function (str) { 2573 var o = str; 2574 var r = null; 2575 2576 var at = str.indexOf("@"); 2577 if (at > 0) { 2578 o = str.substring(0, at); 2579 r = str.substring(at + 1); 2580 } 2581 2582 var ordinal = parseInt(o, 10); 2583 var range = baja.EnumRange.DEFAULT; 2584 if (r !== null) { 2585 range = range.decodeFromString(r); 2586 } 2587 2588 return this.make({"ordinal": ordinal, "range": range}); 2589 }; 2590 baja.DynamicEnum.prototype.decodeFromString = cacheDecode(baja.DynamicEnum.prototype.decodeFromString); 2591 2592 /** 2593 * Encode a DynamicEnum to a String. 2594 * 2595 * @name baja.DynamicEnum#encodeToString 2596 * @function 2597 * 2598 * @returns {String} 2599 */ 2600 baja.DynamicEnum.prototype.encodeToString = function () { 2601 var s = ""; 2602 s += this.$ordinal; 2603 if (this.$range !== baja.EnumRange.DEFAULT) { 2604 s += "@" + this.$range.encodeToString(); 2605 } 2606 2607 return s; 2608 }; 2609 baja.DynamicEnum.prototype.encodeToString = cacheEncode(baja.DynamicEnum.prototype.encodeToString); 2610 2611 /** 2612 * Default DynamicEnum instance. 2613 */ 2614 baja.DynamicEnum.DEFAULT = new baja.DynamicEnum(0, baja.EnumRange.DEFAULT); 2615 2616 // Register Type 2617 baja.DynamicEnum.registerType("baja:DynamicEnum"); 2618 2619 /** 2620 * Return the data type symbol. 2621 * 2622 * @returns {String} the data type symbol. 2623 */ 2624 baja.DynamicEnum.prototype.getDataTypeSymbol = function () { 2625 return "e"; 2626 }; 2627 2628 /** 2629 * Return whether the enum is action or not. 2630 * 2631 * @returns {Boolean} true if active. 2632 */ 2633 baja.DynamicEnum.prototype.isActive = function () { 2634 return this.$ordinal !== 0; 2635 }; 2636 2637 /** 2638 * Return the ordinal. 2639 * 2640 * @returns {Number} the ordinal. 2641 */ 2642 baja.DynamicEnum.prototype.getOrdinal = function () { 2643 return this.$ordinal; 2644 }; 2645 2646 /** 2647 * Return the range. 2648 * 2649 * @returns {baja.EnumRange} the enum range. 2650 */ 2651 baja.DynamicEnum.prototype.getRange = function () { 2652 return this.$range; 2653 }; 2654 2655 /** 2656 * Return the tag for the ordinal. 2657 * 2658 * @returns {String} the tag. 2659 */ 2660 baja.DynamicEnum.prototype.getTag = function () { 2661 return this.$range.getTag(this.$ordinal); 2662 }; 2663 2664 /** 2665 * Return the String representation of the DynamicEnum. 2666 * 2667 * @returns {String} 2668 */ 2669 baja.DynamicEnum.prototype.toString = function () { 2670 return this.getTag(); 2671 }; 2672 2673 /** 2674 * Equals comparison via tag or ordinal. 2675 * 2676 * @param {String|Number|baja.DynamicEnum} arg the enum, tag or ordinal used for comparison. 2677 * @returns {Boolean} true if equal. 2678 */ 2679 baja.DynamicEnum.prototype.is = function (arg) { 2680 var tof = typeof arg; 2681 if (tof === "number") { 2682 return arg === this.$ordinal; 2683 } 2684 else if (tof === "string") { 2685 return arg === this.getTag(); 2686 } 2687 else { 2688 return this.equals(arg); 2689 } 2690 }; 2691 2692 /** 2693 * @class Represents a baja:FrozenEnum in BajaScript. 2694 * <p> 2695 * In Niagara, a BFrozenEnum is a hard coded set of enumerations and is immutable. 2696 * <p> 2697 * FrozenEnum objects are treated in a special way in BajaScript. A FrozenEnum's range (also 2698 * known as a Contract in BajaScript) is lazily requested and stored within the BajaScript 2699 * registry. Therefore, this object shouldn't be used directly. 2700 * Here's the preferred way of access an FrozenEnum... 2701 * <pre> 2702 * var en = baja.$("baja:Weekday").get("monday"); 2703 * </pre> 2704 * 2705 * @name baja.FrozenEnum 2706 * @extends baja.Enum 2707 * 2708 * @see baja.FrozenEnum#get 2709 */ 2710 var FrozenEnum = function () { 2711 // This Constructor should be considered private 2712 FrozenEnum.$super.apply(this, arguments); 2713 this.$ordinal = 0; 2714 }.$extend(baja.Enum); 2715 2716 /** 2717 * Called once a Contract has been loaded onto a FrozenEnum. 2718 * <p> 2719 * This method is designed to be overriden. 2720 * 2721 * @name baja.FrozenEnum#contractCommitted 2722 * @function 2723 * @private 2724 */ 2725 FrozenEnum.prototype.contractCommitted = function () { 2726 }; 2727 2728 /** 2729 * Make a FrozenEnum. 2730 * 2731 * @name baja.FrozenEnum#make 2732 * @function 2733 * @private 2734 * 2735 * @param {String|Number} arg the tag or ordinal of the frozen enum we want to access. 2736 * 2737 * @see baja.FrozenEnum#make 2738 * 2739 * @returns {baja.FrozenEnum} the frozen enum. 2740 */ 2741 FrozenEnum.prototype.make = function (arg) { 2742 return this.get(arg); 2743 }; 2744 2745 /** 2746 * Decode a FrozenEnum from a String. 2747 * 2748 * @name baja.FrozenEnum#decodeFromString 2749 * @function 2750 * 2751 * @param {String} str 2752 * @returns {baja.FrozenEnum} 2753 */ 2754 FrozenEnum.prototype.decodeFromString = function (str) { 2755 return this.get(str); 2756 }; 2757 2758 /** 2759 * Encode the FrozenEnum to a String. 2760 * 2761 * @name baja.FrozenEnum#encodeToString 2762 * @function 2763 * 2764 * @returns {String} 2765 */ 2766 FrozenEnum.prototype.encodeToString = function () { 2767 return this.getTag(); 2768 }; 2769 2770 /** 2771 * Default FrozenEnum instance. 2772 * 2773 * @name baja.FrozenEnum.DEFAULT 2774 * @private 2775 * @inner 2776 */ 2777 FrozenEnum.DEFAULT = new FrozenEnum(); 2778 2779 // Register Type 2780 FrozenEnum.registerType("baja:FrozenEnum"); 2781 2782 /** 2783 * Return true if the enum is active. 2784 * 2785 * @name baja.FrozenEnum#isActive 2786 * @function 2787 * 2788 * @returns {Boolean} true if active. 2789 */ 2790 FrozenEnum.prototype.isActive = function () { 2791 return this.$ordinal !== 0; 2792 }; 2793 2794 /** 2795 * Return the ordinal for the frozen enum. 2796 * 2797 * @name baja.FrozenEnum#getOrdinal 2798 * @function 2799 * 2800 * @returns {Number} ordinal. 2801 */ 2802 FrozenEnum.prototype.getOrdinal = function () { 2803 return this.$ordinal; 2804 }; 2805 2806 /** 2807 * Return the range for the frozen enum. 2808 * 2809 * @name baja.FrozenEnum#getRange 2810 * @function 2811 * 2812 * @returns {baja.EnumRange} range. 2813 */ 2814 FrozenEnum.prototype.getRange = function () { 2815 return this.getType().getRange(); 2816 }; 2817 2818 /** 2819 * Return the tag for the FrozenEnum. 2820 * 2821 * @name baja.FrozenEnum#getTag 2822 * @function 2823 * 2824 * @returns {String} tag 2825 */ 2826 FrozenEnum.prototype.getTag = function () { 2827 return this.getType().getTag(this.$ordinal); 2828 }; 2829 2830 /** 2831 * Return the display tag for the FrozenEnum. 2832 * <p> 2833 * A display tag is a translated human friendly readable version of a tag. 2834 * 2835 * @name baja.FrozenEnum#getDisplayTag 2836 * @function 2837 * 2838 * @returns {String} display tag. 2839 */ 2840 FrozenEnum.prototype.getDisplayTag = function () { 2841 return this.getType().getDisplayTag(this.$ordinal); 2842 }; 2843 2844 /** 2845 * Return the String representation of a FrozenEnum. 2846 * 2847 * @name baja.FrozenEnum#toString 2848 * @function 2849 * 2850 * @returns {String} string 2851 */ 2852 FrozenEnum.prototype.toString = function () { 2853 return this.getDisplayTag(); 2854 }; 2855 2856 /** 2857 * Get a FrozenEnum. 2858 * <p> 2859 * This is the primary way to access an enum... 2860 * <pre> 2861 * var en = baja.$("baja:Weekday").get("monday"); 2862 * </pre> 2863 * 2864 * @name baja.FrozenEnum#get 2865 * @function 2866 * 2867 * @param {String|Number} arg the tag of ordinal of the enum to get. 2868 * @returns {baja.FrozenEnum} the frozen enum. 2869 */ 2870 FrozenEnum.prototype.get = function (arg) { 2871 return this.getRange().get(arg); 2872 }; 2873 2874 /** 2875 * Equals comparison via tag or ordinal. 2876 * <p> 2877 * This is a convenient way to compare enums... 2878 * <pre> 2879 * var monday = baja.$("baja:Weekday").get("monday"); 2880 * baja.outln("Is the day Tuesday?: " + monday.is("tuesday")); 2881 * </pre> 2882 * 2883 * @name baja.FrozenEnum#is 2884 * @function 2885 * 2886 * @param {String|Number|baja.FrozenEnum} arg the enum, tag or ordinal used for comparison. 2887 * @returns {Boolean} true if equal. 2888 */ 2889 FrozenEnum.prototype.is = function (arg) { 2890 var tof = typeof arg; 2891 if (tof === "string" || tof === "number") { 2892 return this.equals(this.getRange().get(arg)); 2893 } 2894 else { 2895 return this.equals(arg); 2896 } 2897 }; 2898 2899 /** 2900 * Return the Frozen Enum as a Dynamic Enum. 2901 * 2902 * @name baja.FrozenEnum#asDynamic 2903 * @function 2904 * @see baja.DynamicEnum#make 2905 * 2906 * @returns {baja.DynamicEnum} 2907 */ 2908 FrozenEnum.prototype.asDynamic = function () { 2909 return baja.DynamicEnum.make({en: this}); 2910 }; 2911 2912 //////////////////////////////////////////////////////////////// 2913 // Status 2914 //////////////////////////////////////////////////////////////// 2915 2916 /** 2917 * @class Represents a baja:Status in BajaScript. 2918 * <p> 2919 * Status provides a bit mask for various standardized 2920 * status flags in the Baja control architecture. Plus 2921 * it provides for arbitrary extensions using BFacets. 2922 * <p> 2923 * When creating a Simple, always use the 'make' method instead of creating a new Object. 2924 * 2925 * @name baja.Status 2926 * @extends baja.Simple 2927 */ 2928 baja.Status = function (bits, facets) { 2929 // Constructor should be considered private 2930 baja.Status.$super.apply(this, arguments); 2931 this.$bits = bits; 2932 this.$facets = facets; 2933 }.$extend(baja.Simple); 2934 2935 /** 2936 * Make a Status 2937 * <p> 2938 * The bits (Number) or (for more arguments) an Object Literal can be used to specify the method's arguments. 2939 * <pre> 2940 * var st1 = baja.Status.make(baja.Status.DOWN | baja.Status.FAULT); 2941 * 2942 * // ... or for more arguments... 2943 * 2944 * var st2 = baja.Status.make({ 2945 * bits: baja.Status.DOWN, 2946 * facets: facets 2947 * }); 2948 * </pre> 2949 * The make method can also be used to create a new status with its state changed... 2950 * <pre> 2951 * var newStatus = baja.Status.make({ 2952 * orig: oldStatus, 2953 * bits: baja.Status.OVERRIDDEN, 2954 * state: true 2955 * }}; 2956 * </pre> 2957 * 2958 * @param {Object|Number} obj the Object Literal that specifies the method's arguments or Status bits. 2959 * @param {Number} obj.bits the Status bits. 2960 * @param {baja.Facets} [obj.facets] the facets for the Status. 2961 * @param {baja.Status} [obj.orig] if defined, obj.state must also be defined. This is used to create 2962 * a new Status with one of it's bit states changed (see example above). 2963 * @param {Boolean} [obj.state] the state of the bit to change (used in conjunction with obj.orig). 2964 * @returns {baja.Status} the status. 2965 */ 2966 baja.Status.make = function (obj) { 2967 obj = objectify(obj, "bits"); 2968 2969 var orig = obj.orig, 2970 bits = obj.bits, 2971 state = obj.state, 2972 facets = obj.facets, 2973 nullLookup = baja.Status.$nullLookup; 2974 2975 // If the bits are a baja.Status then get the bits 2976 if (bajaHasType(bits) && bits.getType().equals(baja.Status.DEFAULT.getType())) { 2977 bits = bits.getBits(); 2978 } 2979 2980 strictArg(bits, Number); 2981 2982 // Make with original bits... 2983 if (orig !== undefined) { 2984 strictAllArgs([orig, state], [baja.Status, Boolean]); 2985 2986 bits = state ? (orig.$bits | bits) : (orig.$bits & ~bits); 2987 if (bits === 0 && orig.$facets === facetsDefault) { 2988 return baja.Status.ok; 2989 } 2990 if (orig.$bits === bits) { 2991 return orig; 2992 } 2993 facets = orig.$facets; 2994 } 2995 2996 // Standard make... 2997 facets = bajaDef(facets, facetsDefault); 2998 strictArg(facets, baja.Facets); 2999 3000 if (facets === facetsDefault) { 3001 if (bits === 0) { 3002 return baja.Status.DEFAULT; 3003 } 3004 3005 if (bits <= 256) { 3006 if (!(nullLookup.hasOwnProperty(bits))) { 3007 nullLookup[bits] = new baja.Status(bits, facets); 3008 } 3009 return nullLookup[bits]; 3010 } 3011 } 3012 3013 return new baja.Status(bits, facets); 3014 }; 3015 3016 /** 3017 * Make a Status. 3018 * <p> 3019 * The bits (Number) or (for more arguments) an Object Literal can be used to specify the method's arguments. 3020 * <pre> 3021 * var st1 = baja.Status.make(baja.Status.DOWN | baja.Status.FAULT); 3022 * 3023 * // ... or for more arguments... 3024 * 3025 * var st2 = baja.$("baja:Status").make({ 3026 * bits: baja.Status.DOWN, 3027 * facets: facets 3028 * }); 3029 * </pre> 3030 * The make method can also be used to create a new status with its state changed... 3031 * <pre> 3032 * var newStatus = baja.$("baja:Status").make({ 3033 * orig: oldStatus, 3034 * bits: baja.Status.OVERRIDDEN, 3035 * state: true 3036 * }}; 3037 * </pre> 3038 * 3039 * @param {Object|Number} obj the Object Literal that specifies the method's arguments or Status bits. 3040 * @param {Number} obj.bits the Status bits. 3041 * @param {baja.Facets} [obj.facets] the facets for the Status. 3042 * @param {baja.Status} [obj.orig] if defined, obj.state must also be defined. This is used to create 3043 * a new Status with one of it's bit states changed (see example above). 3044 * @param {Boolean} [obj.state] the state of the bit to change (used in conjunction with obj.orig). 3045 * @returns {baja.Status} the status. 3046 */ 3047 baja.Status.prototype.make = function (obj) { 3048 return baja.Status.make.apply(baja.Status, arguments); 3049 }; 3050 3051 /** 3052 * Decode a Status from a String. 3053 * 3054 * @param {String} str 3055 * @returns {baja.Status} 3056 */ 3057 baja.Status.prototype.decodeFromString = function (str) { 3058 var x, 3059 semi = str.indexOf(';'); 3060 if (semi < 0) { 3061 x = baja.Status.make(parseInt(str, 16)); 3062 } 3063 else { 3064 var bits = parseInt(str.substring(0, semi), 16); 3065 x = baja.Status.make({"bits": bits, "facets": facetsDefault.decodeFromString(str.substring(semi + 1))}); 3066 } 3067 return x; 3068 }; 3069 baja.Status.prototype.decodeFromString = cacheDecode(baja.Status.prototype.decodeFromString); 3070 3071 /** 3072 * Encode the Status to a String. 3073 * 3074 * @returns {String} 3075 */ 3076 baja.Status.prototype.encodeToString = function () { 3077 var s = this.$bits.toString(16); 3078 if (this.$facets !== facetsDefault) { 3079 s += ";" + this.$facets.encodeToString(); 3080 } 3081 return s; 3082 }; 3083 baja.Status.prototype.encodeToString = cacheEncode(baja.Status.prototype.encodeToString); 3084 3085 /** 3086 * Equality test. 3087 * 3088 * @param obj 3089 * @returns {Boolean} 3090 */ 3091 baja.Status.prototype.equals = function (obj) { 3092 if (bajaHasType(obj) && obj.getType().equals(this.getType())) { 3093 if (this.$facets === facetsDefault) { 3094 return obj.$bits === this.$bits; 3095 } 3096 else { 3097 return obj.$bits === this.$bits && this.$facets.equals(obj.$facets); 3098 } 3099 } 3100 else { 3101 return false; 3102 } 3103 }; 3104 3105 /** 3106 * Default Status instance. 3107 */ 3108 baja.Status.DEFAULT = new baja.Status(0, facetsDefault); 3109 3110 // Register Type 3111 baja.Status.registerType("baja:Status"); 3112 3113 // If the facets are null then the Status is interned into this Object map 3114 var statusNullLookup = baja.Status.$nullLookup = {}; 3115 3116 /** 3117 * Bit for disabled. 3118 */ 3119 baja.Status.DISABLED = 0x0001; 3120 3121 /** 3122 * Bit for fault. 3123 */ 3124 baja.Status.FAULT = 0x0002; 3125 3126 /** 3127 * Bit for down. 3128 */ 3129 baja.Status.DOWN = 0x0004; 3130 3131 /** 3132 * Bit for alarm. 3133 */ 3134 baja.Status.ALARM = 0x0008; 3135 3136 /** 3137 * Bit for stale. 3138 */ 3139 baja.Status.STALE = 0x0010; 3140 3141 /** 3142 * Bit for overriden. 3143 */ 3144 baja.Status.OVERRIDDEN = 0x0020; 3145 3146 /** 3147 * Bit for null. 3148 */ 3149 baja.Status.NULL = 0x0040; 3150 3151 /** 3152 * Bit for unacked alarm. 3153 */ 3154 baja.Status.UNACKED_ALARM = 0x0080; 3155 3156 /** 3157 * String used in a Facets for identifying the active priority level of a writable point. 3158 */ 3159 baja.Status.ACTIVE_LEVEL = "activeLevel"; 3160 3161 /** 3162 * Status for ok (null facets). 3163 */ 3164 baja.Status.ok = baja.Status.DEFAULT; 3165 3166 /** 3167 * Status for disabled (null facets). 3168 */ 3169 statusNullLookup[baja.Status.DISABLED] = baja.Status.disabled = new baja.Status(baja.Status.DISABLED, facetsDefault); 3170 3171 /** 3172 * Status for fault (null facets). 3173 */ 3174 statusNullLookup[baja.Status.FAULT] = baja.Status.fault = new baja.Status(baja.Status.FAULT, facetsDefault); 3175 3176 /** 3177 * Status for down (null facets). 3178 */ 3179 statusNullLookup[baja.Status.DOWN] = baja.Status.down = new baja.Status(baja.Status.DOWN, facetsDefault); 3180 3181 /** 3182 * Status for alarm (null facets). 3183 */ 3184 statusNullLookup[baja.Status.ALARM] = baja.Status.alarm = new baja.Status(baja.Status.ALARM, facetsDefault); 3185 3186 /** 3187 * Status for stale (null facets). 3188 */ 3189 statusNullLookup[baja.Status.STALE] = baja.Status.stale = new baja.Status(baja.Status.STALE, facetsDefault); 3190 3191 /** 3192 * Status for overriden (null facets). 3193 */ 3194 statusNullLookup[baja.Status.OVERRIDDEN] = baja.Status.overridden = new baja.Status(baja.Status.OVERRIDDEN, facetsDefault); 3195 3196 /** 3197 * Status for null status (null facets). 3198 */ 3199 statusNullLookup[baja.Status.NULL] = baja.Status.nullStatus = new baja.Status(baja.Status.NULL, facetsDefault); 3200 3201 /** 3202 * Status for unacked alarm (null facets). 3203 */ 3204 statusNullLookup[baja.Status.UNACKED_ALARM] = baja.Status.unackedAlarm = new baja.Status(baja.Status.UNACKED_ALARM, facetsDefault); 3205 3206 /** 3207 * Return the facets for the Status. 3208 * 3209 * @returns {baja.Facets} status facets 3210 */ 3211 baja.Status.prototype.getFacets = function () { 3212 return this.$facets; 3213 }; 3214 3215 /** 3216 * Return a value from the status facets. 3217 * 3218 * @param {String} name the name of the value to get from the status facets. 3219 * @param [def] if defined, this value is returned if the name can't be found in 3220 * the status facets. 3221 * @returns the value from the status facets (null if def is undefined and name can't be found). 3222 */ 3223 baja.Status.prototype.get = function (name, def) { 3224 return this.$facets.get(name, def); 3225 }; 3226 3227 /** 3228 * Return the Status bits. 3229 * 3230 * @returns {Number} status bits. 3231 */ 3232 baja.Status.prototype.getBits = function () { 3233 return this.$bits; 3234 }; 3235 3236 /** 3237 * Return true if the Status is not disabled, fault, down 3238 * stale or null. 3239 * 3240 * @returns {Boolean} true if valid. 3241 */ 3242 baja.Status.prototype.isValid = function () { 3243 return (((this.$bits & baja.Status.DISABLED) === 0) && 3244 ((this.$bits & baja.Status.FAULT) === 0) && 3245 ((this.$bits & baja.Status.DOWN) === 0) && 3246 ((this.$bits & baja.Status.STALE) === 0) && 3247 ((this.$bits & baja.Status.NULL) === 0)); 3248 }; 3249 3250 /** 3251 * Return true if the Status is ok. 3252 * 3253 * @returns {Boolean} 3254 */ 3255 baja.Status.prototype.isOk = function () { 3256 return this.$bits === 0; 3257 }; 3258 3259 /** 3260 * Return true if the Status is disabled. 3261 * 3262 * @returns {Boolean} 3263 */ 3264 baja.Status.prototype.isDisabled = function () { 3265 return (this.$bits & baja.Status.DISABLED) !== 0; 3266 }; 3267 3268 /** 3269 * Return true if the Status is in fault. 3270 * 3271 * @returns {Boolean} 3272 */ 3273 baja.Status.prototype.isFault = function () { 3274 return (this.$bits & baja.Status.FAULT) !== 0; 3275 }; 3276 3277 /** 3278 * Return true if the Status is down. 3279 * 3280 * @returns {Boolean} 3281 */ 3282 baja.Status.prototype.isDown = function () { 3283 return (this.$bits & baja.Status.DOWN) !== 0; 3284 }; 3285 3286 /** 3287 * Return true if the Status is in alarm. 3288 * 3289 * @name baja.Status#isAlarm 3290 * @function 3291 * 3292 * @returns {Boolean} 3293 */ 3294 baja.Status.prototype.isAlarm = function () { 3295 return (this.$bits & baja.Status.ALARM) !== 0; 3296 }; 3297 3298 /** 3299 * Return true if the Status is stale. 3300 * 3301 * @returns {Boolean} 3302 */ 3303 baja.Status.prototype.isStale = function () { 3304 return (this.$bits & baja.Status.STALE) !== 0; 3305 }; 3306 3307 /** 3308 * Return true if the Status is overriden. 3309 * 3310 * @returns {Boolean} 3311 */ 3312 baja.Status.prototype.isOverridden = function () { 3313 return (this.$bits & baja.Status.OVERRIDDEN) !== 0; 3314 }; 3315 3316 /** 3317 * Return true if the Status is null. 3318 * 3319 * @returns {Boolean} 3320 */ 3321 baja.Status.prototype.isNull = function () { 3322 return (this.$bits & baja.Status.NULL) !== 0; 3323 }; 3324 3325 /** 3326 * Return true if the Status is unacked alarm. 3327 * 3328 * @returns {Boolean} 3329 */ 3330 baja.Status.prototype.isUnackedAlarm = function () { 3331 return (this.$bits & baja.Status.UNACKED_ALARM) !== 0; 3332 }; 3333 3334 /** 3335 * Return the status from a BIStatus. 3336 * 3337 * @return resolved status value 3338 */ 3339 baja.Status.getStatusFromIStatus = function (statusVal) { 3340 var status = statusVal; 3341 3342 if (statusVal.getType().isComplex() && statusVal.has("out") && statusVal.getOut().getType().is("baja:StatusValue")) { 3343 status = statusVal.getOut().getStatus(); 3344 } 3345 else if (statusVal.getType().is("baja:StatusValue")) { 3346 status = statusVal.getStatus(); 3347 } 3348 3349 return status; 3350 }; 3351 3352 //////////////////////////////////////////////////////////////// 3353 // Date and Time 3354 //////////////////////////////////////////////////////////////// 3355 3356 (function dateAndTime() { 3357 3358 //////////////////////////////////////////////////////////////// 3359 // Date and Time Text Formatting 3360 //////////////////////////////////////////////////////////////// 3361 3362 /** 3363 * @namespace Date and Time formatting. 3364 */ 3365 baja.TimeFormat = {}; 3366 var SHOW_DATE = baja.TimeFormat.SHOW_DATE = 0x01, 3367 SHOW_TIME = baja.TimeFormat.SHOW_TIME = 0x02, 3368 SHOW_SECONDS = baja.TimeFormat.SHOW_SECONDS = 0x04, 3369 SHOW_MILLIS = baja.TimeFormat.SHOW_MILLIS = 0x08, 3370 SHOW_ZONE = 0x10; // TODO: Implement timezones. Currently this is unsupported. 3371 3372 // In the first version, this implementation is currently limited 3373 var patternCache = {}, 3374 SHOW = [ 3375 0, // 0 blank 3376 SHOW_DATE, // 1 YEAR_2 3377 SHOW_DATE, // 2 YEAR_4 3378 SHOW_DATE, // 3 MON_1 3379 SHOW_DATE, // 4 MON_2 3380 SHOW_DATE, // 5 MON_TAG 3381 SHOW_DATE, // 6 DAY_1 3382 SHOW_DATE, // 7 DAY_2 3383 SHOW_TIME, // 8 HOUR_12_1 3384 SHOW_TIME, // 9 HOUR_12_2 3385 SHOW_TIME, // 10 HOUR_24_1 3386 SHOW_TIME, // 11 HOUR_24_2 3387 SHOW_TIME, // 12 MIN 3388 SHOW_TIME, // 13 AM_PM 3389 SHOW_SECONDS | SHOW_MILLIS, // 14 SEC 3390 SHOW_ZONE, // 15 ZONE_TAG 3391 SHOW_DATE, // 16 WEEK_1 3392 SHOW_DATE, // 17 WEEK_2 3393 SHOW_DATE, // 18 MON 3394 SHOW_ZONE, // 19 ZONE_OFFSET 3395 SHOW_DATE // 20 WEEK_YEAR 3396 ], 3397 YEAR_2 = 1, // YY two digit year 3398 YEAR_4 = 2, // YYYY four digit year 3399 MON_1 = 3, // M one digit month 3400 MON_2 = 4, // MM two digit month 3401 MON_TAG = 5, // MMM short tag month 3402 MON = 18, // MMMM long tag month 3403 DAY_1 = 6, // D one digit day of month 3404 DAY_2 = 7, // DD two digit day of month 3405 HOUR_12_1 = 8, // h one digit 12 hour 3406 HOUR_12_2 = 9, // hh two digit 12 hour 3407 HOUR_24_1 = 10, // H one digit 24 hour 3408 HOUR_24_2 = 11, // HH two digit 24 hour 3409 MIN = 12, // mm two digit minutes 3410 AM_PM = 13, // a AM PM marker 3411 SEC = 14, // ss two digit seconds and millis 3412 ZONE_TAG = 15, // z timezone 3413 WEEK_1 = 16, // W short tag day of week 3414 WEEK_2 = 17, // WW day of week 3415 ZONE_OFFSET = 19,// Z timezone offest (RFC 822) 3416 WEEK_YEAR = 20; // w week of year 3417 3418 function toCode(c, count) { 3419 switch(c) { 3420 case "Y": return count <= 2 ? YEAR_2 : YEAR_4; 3421 case "M": 3422 switch(count) { 3423 case 1: return MON_1; 3424 case 2: return MON_2; 3425 case 3: return MON_TAG; 3426 default: return MON; 3427 } 3428 case "D": return count === 1 ? DAY_1 : DAY_2; 3429 case "h": return count === 1 ? HOUR_12_1 : HOUR_12_2; 3430 case "H": return count === 1 ? HOUR_24_1 : HOUR_24_2; 3431 case "m": return MIN; 3432 case "s": return SEC; 3433 case "a": return AM_PM; 3434 case "z": return ZONE_TAG; 3435 case "Z": return ZONE_OFFSET; 3436 case "W": return count === 1 ? WEEK_1 : WEEK_2; 3437 case "w": return WEEK_YEAR; 3438 default: return c.charCodeAt(0); 3439 } 3440 } 3441 3442 function buildPattern(textPattern) { 3443 // Allocate a pattern array 3444 var len = textPattern.length, 3445 pattern = [], 3446 last = textPattern.charAt(0), 3447 count = 1, 3448 i, 3449 c; 3450 3451 // Parse text pattern into pattern codes 3452 for (i = 1; i < len; ++i) { 3453 c = textPattern.charAt(i); 3454 if (last === c) { 3455 count++; 3456 continue; 3457 } 3458 pattern.push(toCode(last, count)); 3459 last = c; 3460 count = 1; 3461 } 3462 pattern.push(toCode(last, count)); 3463 return pattern; 3464 } 3465 3466 function pad(s, num) { 3467 if (num < 10) { 3468 s += "0"; 3469 } 3470 s += num; 3471 return s; 3472 } 3473 3474 /** 3475 * Create a formatting date/time String. 3476 * 3477 * @private 3478 * @ignore 3479 * 3480 * @param {Object} obj the Object Literal for the method's arguments. 3481 * @param {String} [obj.textPattern] 3482 * @param {baja.AbsTime} [obj.absTime] 3483 * @param {Number} [obj.year] 3484 * @param {baja.FrozenEnum} [obj.month] 3485 * @param {Number} [obj.day] days 1 to 31 3486 * @param {Number} [obj.hour] 3487 * @param {Number} [obj.min] 3488 * @param {Number} [obj.sec] 3489 * @param {Number} [obj.ms] 3490 * @param [obj.timeZone] 3491 * @param {Number} [obj.show] 3492 */ 3493 function toDateTimeString(obj) { 3494 // Get the pattern code 3495 var pattern; 3496 if (patternCache.hasOwnProperty(obj.textPattern)) { 3497 pattern = patternCache[obj.textPattern]; 3498 } 3499 else { 3500 pattern = patternCache[obj.textPattern] = buildPattern(obj.textPattern); 3501 } 3502 3503 var s = "", 3504 sep1 = -1, 3505 sep2 = -1, 3506 shownCount = 0, 3507 c, 3508 i; 3509 3510 // walk thru the pattern 3511 for (i = 0; i < pattern.length; ++i) { 3512 // get the code 3513 c = pattern[i]; 3514 3515 // if the code is a separator, save it away and move on 3516 if (c >= SHOW.length) { 3517 if (sep1 === -1) { 3518 sep1 = c; 3519 } 3520 else if (sep2 === -1) { 3521 sep2 = c; 3522 } 3523 continue; 3524 } 3525 3526 // if we shouldn't show this field, then clear 3527 // the pending separator and move on 3528 if ((SHOW[c] & obj.show) === 0) { 3529 sep1 = sep2 = -1; 3530 continue; 3531 } 3532 3533 // we are now going to show this field, so update our show count 3534 shownCount++; 3535 3536 // if we have a pending separator then write the separator; 3537 // note we don't show the separator if this is the first field 3538 if (shownCount > 1 && sep1 !== -1) { 3539 s += String.fromCharCode(sep1); 3540 if (sep2 !== -1) { 3541 s += String.fromCharCode(sep2); 3542 } 3543 sep1 = sep2 = -1; 3544 } 3545 3546 // output the field according to the pattern code 3547 shownCount++; 3548 switch(c) { 3549 case YEAR_2: 3550 //issue 12377 3551 //old code -> pad(s, year >= 2000 ? year-2000 : year-1900); 3552 //fix below 3553 s = pad(s, obj.year % 100); 3554 break; 3555 case YEAR_4: 3556 s += obj.year; 3557 break; 3558 case MON_1: 3559 s += obj.month.getOrdinal() + 1; 3560 break; 3561 case MON_2: 3562 s = pad(s, obj.month.getOrdinal() + 1); 3563 break; 3564 case MON_TAG: 3565 s += baja.lex("baja").get(obj.month.getTag() + ".short"); 3566 break; 3567 case MON: 3568 s.append(obj.month.getDisplayTag()); 3569 break; 3570 case DAY_1: 3571 s += obj.day; 3572 break; 3573 case DAY_2: 3574 s = pad(s, obj.day); 3575 break; 3576 case HOUR_12_1: 3577 if (obj.hour === 0) { 3578 s += "12"; 3579 } 3580 else { 3581 s += obj.hour > 12 ? obj.hour - 12 : obj.hour; 3582 } 3583 break; 3584 case HOUR_12_2: 3585 if (obj.hour === 0) { 3586 s += "12"; 3587 } 3588 else { 3589 s = pad(s, obj.hour > 12 ? obj.hour - 12 : obj.hour); 3590 } 3591 break; 3592 case HOUR_24_1: 3593 s += obj.hour; 3594 break; 3595 case HOUR_24_2: 3596 s = pad(s, obj.hour); 3597 break; 3598 case MIN: 3599 s = pad(s, obj.min); 3600 break; 3601 case AM_PM: 3602 s += obj.hour < 12 ? "AM" : "PM"; 3603 break; 3604 case SEC: 3605 s = pad(s, obj.sec); 3606 if ((obj.show & SHOW_MILLIS) === 0) { 3607 break; 3608 } 3609 s += "."; 3610 if (obj.ms < 10) { 3611 s += "0"; 3612 } 3613 if (obj.ms < 100) { 3614 s += "0"; 3615 } 3616 s += obj.ms; 3617 break; 3618 case ZONE_TAG: 3619 // TODO: Timezones 3620 s += "*** Zone tag not supported ***"; 3621 break; 3622 case ZONE_OFFSET: 3623 // TODO: Timezones to get UTC offset 3624 var offset = obj.absTime.getOffset(); 3625 3626 if (offset === 0) { 3627 s += "Z"; 3628 } 3629 else { 3630 var hrOff = Math.abs(offset / (1000 * 60 * 60)), 3631 minOff = Math.abs((offset % (1000 * 60 * 60)) / (1000 * 60)); 3632 3633 if (offset < 0) { 3634 s += "-"; 3635 } 3636 else { 3637 s += "+"; 3638 } 3639 3640 if (hrOff < 10) { 3641 s += "0"; 3642 } 3643 s += hrOff; 3644 3645 s += ":"; 3646 if (minOff < 10) { 3647 s += "0"; 3648 } 3649 s += minOff; 3650 } 3651 break; 3652 case WEEK_1: 3653 s += baja.lex("baja").get(obj.absTime.getDate().getWeekday().getTag() + ".short"); 3654 break; 3655 case WEEK_2: 3656 s += obj.absTime.getDate().getWeekday().getDisplayTag(); 3657 break; 3658 case WEEK_YEAR: 3659 // TODO: Week year 3660 s += "*** Week year not supported ***"; 3661 break; 3662 } 3663 3664 // clear separators 3665 sep1 = sep2 = -1; 3666 } 3667 return s; 3668 } 3669 3670 3671 // These will get set as properties on RelTime, left free floating for 3672 // better minification. 3673 var MILLIS_IN_SECOND = 1000, 3674 MILLIS_IN_MINUTE = MILLIS_IN_SECOND * 60, 3675 MILLIS_IN_HOUR = MILLIS_IN_MINUTE * 60, 3676 MILLIS_IN_DAY = MILLIS_IN_HOUR * 24; 3677 3678 /** 3679 * @class Represents a baja:Time in BajaScript. 3680 * <p> 3681 * Time stores a time of day which is independent 3682 * of any date in the past or future. 3683 * <p> 3684 * When creating a Simple, always use the 'make' method instead of creating a new Object. 3685 * 3686 * @name baja.Time 3687 * @extends baja.Simple 3688 */ 3689 baja.Time = function (hour, min, sec, ms) { 3690 // Constructor should be considered private 3691 baja.Time.$super.apply(this, arguments); 3692 this.$hour = hour; 3693 this.$min = min; 3694 this.$sec = sec; 3695 this.$ms = ms; 3696 }.$extend(baja.Simple); 3697 3698 /** 3699 * Make a Time. 3700 * <p> 3701 * An Object Liternal is used for the method's arguments... 3702 * <pre> 3703 * var t1 = baja.Time.make({ 3704 * hour: 23, 3705 * min: 12, 3706 * sec: 15, 3707 * ms: 789 3708 * }); 3709 * // ...or use a baja.RelTime to specify hour, min, sec and ms... 3710 * var t2 = baja.Time.make({ 3711 * relTime: myRelTime 3712 * }); 3713 * // ...or pass in milliseconds past midnight... 3714 * var t3 = baja.Time.make(12345); 3715 * </pre> 3716 * 3717 * @param {Object} obj the Object Liternal used for the method's arguments. 3718 * @param {Number} [obj.hour] hours (0-23). 3719 * @param {Number} [obj.min] minutes (0-59). 3720 * @param {Number} [obj.sec] seconds (0-59). 3721 * @param {Number} [obj.ms] milliseconds (0-999). 3722 * @param {baja.RelTime} [obj.relTime] if defined, this is the milliseconds 3723 * since the start of the day. This overrides the 3724 * other hour, min, sec and ms arguments. 3725 * @returns {baja.Time} the time. 3726 */ 3727 baja.Time.make = function (obj) { 3728 var hour, min, sec, ms; 3729 3730 function processMillis(millis) { 3731 baja.strictArg(millis, Number); 3732 millis = millis % MILLIS_IN_DAY; 3733 hour = Math.floor(millis / MILLIS_IN_HOUR); 3734 millis = millis % MILLIS_IN_HOUR; 3735 min = Math.floor(millis / MILLIS_IN_MINUTE); 3736 millis = millis % MILLIS_IN_MINUTE; 3737 sec = Math.floor(millis / MILLIS_IN_SECOND); 3738 ms = Math.floor(millis % MILLIS_IN_SECOND); 3739 } 3740 3741 obj = objectify(obj, "milliseconds"); 3742 3743 if (typeof obj.milliseconds === "number") { 3744 // Create from a number of milliseconds... 3745 processMillis(obj.milliseconds); 3746 } 3747 else if (bajaHasType(obj.relTime) && obj.relTime.getType().is("baja:RelTime")) { 3748 // Build from rel time (overrides other hour, min, sec and ms on object)... 3749 processMillis(obj.relTime.getMillis()); 3750 } 3751 else { 3752 // Attempt to get time from Object Literal... 3753 hour = bajaDef(obj.hour, 0); 3754 min = bajaDef(obj.min, 0); 3755 sec = bajaDef(obj.sec, 0); 3756 ms = bajaDef(obj.ms, 0); 3757 } 3758 3759 // Ensure we're dealing with numbers 3760 strictAllArgs([hour, min, sec, ms], [Number, Number, Number, Number]); 3761 3762 if (hour < 0 || hour > 23 || 3763 min < 0 || min > 59 || 3764 sec < 0 || sec > 59 || 3765 ms < 0 || ms > 999) { 3766 throw new Error("Invalid time: " + hour + ":" + min + ":" + sec + "." + ms); 3767 } 3768 3769 if (hour === 0 && min === 0 && sec === 0 && ms === 0) { 3770 return baja.Time.DEFAULT; 3771 } 3772 else { 3773 return new baja.Time(hour, min, sec, ms); 3774 } 3775 }; 3776 3777 /** 3778 * Make a Time. 3779 * <p> 3780 * An Object Liternal is used for the method's arguments... 3781 * <pre> 3782 * var t1 = baja.$("baja:Time").make({ 3783 * hour: 23, 3784 * min: 12, 3785 * sec: 15, 3786 * ms: 789 3787 * }); 3788 * // ...or use a baja.RelTime to specify hour, min, sec and ms... 3789 * var t2 = baja.$("baja:Time").make({ 3790 * relTime: timeOfDayMillis 3791 * }); 3792 * // ...or pass in milliseconds past midnight... 3793 * var t3 = baja.Time.make(12345); 3794 * </pre> 3795 * 3796 * @param {Object} obj the Object Liternal used for the method's arguments. 3797 * @param {Number} [obj.hour] hours (0-23). 3798 * @param {Number} [obj.min] minutes (0-59). 3799 * @param {Number} [obj.sec] seconds (0-59). 3800 * @param {Number} [obj.ms] milliseconds (0-999). 3801 * @param {baja.RelTime} [obj.relTime] if defined, this is the milliseconds 3802 * since the start of the day. This overrides the 3803 * other hour, min, sec and ms arguments. 3804 * @returns {baja.Time} the time. 3805 */ 3806 baja.Time.prototype.make = function (obj) { 3807 return baja.Time.make.apply(baja.Time, arguments); 3808 }; 3809 3810 /** 3811 * Decode a Time from a String. 3812 * 3813 * @param {String} str 3814 * @returns {baja.Time} 3815 */ 3816 baja.Time.prototype.decodeFromString = function (str) { 3817 // Time ISO 8601 format hh:mm:ss.mmm 3818 var res = /^([0-1][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])\.([0-9]{3})$/.exec(str); 3819 3820 if (res === null) { 3821 throw new Error("Failed to decode time: " + str); 3822 } 3823 3824 var t = baja.Time.make({ 3825 hour: parseInt(res[1], 10), 3826 min: parseInt(res[2], 10), 3827 sec: parseInt(res[3], 10), 3828 ms: parseInt(res[4], 10) 3829 }); 3830 3831 return t; 3832 }; 3833 baja.Time.prototype.decodeFromString = cacheDecode(baja.Time.prototype.decodeFromString); 3834 3835 // Util for padding out zeros into Strings 3836 function zeroPad(num, n) { 3837 var str = num.toString(10), 3838 zeros = n - str.length, i; 3839 for (i = 0; i < zeros; ++i) { 3840 str = "0" + str; 3841 } 3842 return str; 3843 } 3844 3845 /** 3846 * Encode a Time to a String. 3847 * 3848 * @returns {String} 3849 */ 3850 baja.Time.prototype.encodeToString = function () { 3851 return zeroPad(this.$hour, 2) + ":" + zeroPad(this.$min, 2) + ":" + zeroPad(this.$sec, 2) + "." + zeroPad(this.$ms, 3); 3852 }; 3853 baja.Time.prototype.encodeToString = cacheEncode(baja.Time.prototype.encodeToString); 3854 3855 /** 3856 * Equality test. 3857 * 3858 * @param obj 3859 * @returns {Boolean} 3860 */ 3861 baja.Time.prototype.equals = function (obj) { 3862 if (bajaHasType(obj) && obj.getType().equals(this.getType())) { 3863 return this.getTimeOfDayMillis() === obj.getTimeOfDayMillis(); 3864 } 3865 else { 3866 return false; 3867 } 3868 }; 3869 3870 /** 3871 * Default Time instance. 3872 */ 3873 baja.Time.DEFAULT = new baja.Time(0, 0, 0, 0); 3874 3875 /** 3876 * Midnight Time. 3877 */ 3878 baja.Time.MIDNIGHT = baja.Time.DEFAULT; 3879 3880 // Register Type 3881 baja.Time.registerType("baja:Time"); 3882 3883 /** 3884 * Return hours (0-23). 3885 * 3886 * @returns {Number} 3887 */ 3888 baja.Time.prototype.getHour = function () { 3889 return this.$hour; 3890 }; 3891 3892 /** 3893 * Return minutes (0-59). 3894 * 3895 * @returns {Number} 3896 */ 3897 baja.Time.prototype.getMinute = function () { 3898 return this.$min; 3899 }; 3900 3901 /** 3902 * Return seconds (0-59). 3903 * 3904 * @returns {Number} 3905 */ 3906 baja.Time.prototype.getSecond = function () { 3907 return this.$sec; 3908 }; 3909 3910 /** 3911 * Return milliseconds (0-999). 3912 * 3913 * @returns {Number} 3914 */ 3915 baja.Time.prototype.getMillisecond = function () { 3916 return this.$ms; 3917 }; 3918 3919 /** 3920 * Return the milliseconds since the start of the day. 3921 * 3922 * @returns {Number} 3923 */ 3924 baja.Time.prototype.getTimeOfDayMillis = function () { 3925 if (this.$timeOfDayMs === undefined) { 3926 var ret = this.$hour * MILLIS_IN_HOUR; 3927 ret += this.$min * MILLIS_IN_MINUTE; 3928 ret += this.$sec * MILLIS_IN_SECOND; 3929 ret += this.$ms; 3930 this.$timeOfDayMs = ret; 3931 } 3932 return this.$timeOfDayMs; 3933 }; 3934 3935 /** 3936 * Return a new time of day by adding the specified 3937 * duration. If the result goes past midnight, then roll 3938 * into the next day. 3939 * 3940 * @param {baja.RelTime|baja.Time|Number} duration - RelTime or number of millis 3941 * @returns {baja.Time} the new time with the duration added on. 3942 */ 3943 baja.Time.prototype.add = function (duration) { 3944 strictArg(duration); 3945 3946 if (typeof duration.getMillis === 'function') { 3947 duration = duration.getMillis(); 3948 } else if (typeof duration.getTimeOfDayMillis === 'function') { 3949 duration = duration.getTimeOfDayMillis(); 3950 } 3951 3952 strictArg(duration, Number); 3953 3954 return baja.Time.make(MILLIS_IN_DAY 3955 + this.getTimeOfDayMillis() 3956 + duration); 3957 }; 3958 3959 /** 3960 * Return true if the specified time is before this time. 3961 * 3962 * @param {baja.Time} time 3963 * @returns {Boolean} 3964 */ 3965 baja.Time.prototype.isBefore = function (time) { 3966 strictArg(time, baja.Time); 3967 return this.getTimeOfDayMillis() < time.getTimeOfDayMillis(); 3968 }; 3969 3970 /** 3971 * Return true if the specified time is after this time. 3972 * 3973 * @param {baja.Time} time 3974 * @returns {Boolean} 3975 */ 3976 baja.Time.prototype.isAfter = function (time) { 3977 strictArg(time, baja.Time); 3978 return this.getTimeOfDayMillis() > time.getTimeOfDayMillis(); 3979 }; 3980 3981 /** 3982 * Return a String representation of the time. 3983 * 3984 * @param {Object} [obj] the Object Literal for the method's arguments. 3985 * @params {String} [obj.textPattern] the text pattern to use for formatting. 3986 * If not specified, then the user's default 3987 * time format text pattern will be used. 3988 * @param {Number} [obj.show] flags used to format the time. For more information, 3989 * please see {@link baja.TimeFormat}. 3990 * @returns {String} 3991 */ 3992 baja.Time.prototype.toString = function (obj) { 3993 var textPattern = (obj && obj.textPattern) || baja.getTimeFormatPattern(), 3994 show = ((obj && obj.show) || 0) | SHOW_TIME; 3995 3996 // Filter out invalid flags 3997 show &= ~SHOW_DATE; 3998 3999 return toDateTimeString({ 4000 show: show, 4001 textPattern: textPattern, 4002 hour: this.$hour, 4003 min: this.$min, 4004 sec: this.$sec, 4005 ms: this.$ms 4006 }); 4007 }; 4008 4009 /** 4010 * @class Represents a baja:Date in BajaScript. 4011 * <p> 4012 * Date represents a specific day, month, and year. 4013 * <p> 4014 * When creating a Simple, always use the 'make' method instead of creating a new Object. 4015 * 4016 * @name baja.Date 4017 * @extends baja.Simple 4018 */ 4019 baja.Date = function (year, month, day) { 4020 // Constructor should be considered private 4021 baja.Date.$super.apply(this, arguments); 4022 this.$year = year; 4023 this.$month = month; // Zero indexed 4024 this.$day = day; 4025 }.$extend(baja.Simple); 4026 4027 /** 4028 * Make a Date. 4029 * <p> 4030 * An Object Literal is used to for the method's arguments... 4031 * <pre> 4032 * var d1 = baja.Date.make({ 4033 * year: 2008, 4034 * month: baja.$("baja:Month").get("december"), 4035 * day: 24 4036 * }); 4037 * // ...or from a JavaScript Date... 4038 * var d2 = baja.Date.make({ 4039 * jsDate: date 4040 * }); 4041 * </pre> 4042 * 4043 * @param {Object} obj the Object Literal. 4044 * @param {Number} obj.year 4045 * @param {Number|baja.FrozenEnum} obj.month Number (0-11) or a baja:Month FrozenEnum for the month of the year. 4046 * @param {Number} obj.day (1-31). 4047 * @param {Date} [obj.jsDate] A JavaScript Date used to specify the year, month and day. 4048 * If defined, this will override the year, month and day arguments. 4049 * @returns {baja.Date} 4050 */ 4051 baja.Date.make = function (obj) { 4052 obj = objectify(obj); 4053 4054 var year, 4055 month, 4056 day; 4057 4058 // Create baja.Date from a JavaScript date 4059 if (obj.jsDate && obj.jsDate instanceof Date) { 4060 year = obj.jsDate.getFullYear(); 4061 month = obj.jsDate.getMonth(); // zero index based 4062 day = obj.jsDate.getDate(); 4063 } 4064 else { 4065 year = obj.year; 4066 month = obj.month; // If a number, this should be zero index based 4067 day = obj.day; 4068 4069 // If the month is a baja:Month then get its ordinal as zero index based 4070 if (bajaHasType(month) && month.getType().is("baja:Month")) { 4071 month = month.getOrdinal(); 4072 } 4073 } 4074 4075 // Validate we have these specified 4076 strictAllArgs([year, month, day], [Number, Number, Number]); 4077 4078 if (year < 0 || month < 0 || month > 11 || day < 1 || day > 31) { 4079 throw new Error("Invalid date range"); 4080 } 4081 4082 // Check to see if we should return the default instance 4083 var d = baja.Date.DEFAULT; 4084 if (year === d.$year && month === d.$month && day === d.$day) { 4085 return d; 4086 } 4087 4088 return new baja.Date(year, month, day); 4089 }; 4090 4091 /** 4092 * Make a Date. 4093 * <p> 4094 * An Object Literal is used to for the method's arguments... 4095 * <pre> 4096 * var d1 = baja.$("baja:Date").make({ 4097 * year: 2008, 4098 * month: baja.$("baja:Month").get("december"), 4099 * day: 24 4100 * }); 4101 * // ...or from a JavaScript Date... 4102 * var d2 = baja.$("baja:Date").make({ 4103 * jsDate: date 4104 * }); 4105 * </pre> 4106 * 4107 * @param {Object} obj the Object Literal. 4108 * @param {Number} obj.year 4109 * @param {Number|baja.FrozenEnum} obj.month Number (0-11) or a baja:Month FrozenEnum for the month of the year. 4110 * @param {Number} obj.day (1-31). 4111 * @param {Date} [obj.jsDate] A JavaScript Date used to specify the year, month and day. 4112 * If defined, this will override the year, month and day arguments. 4113 * @returns {baja.Date} 4114 */ 4115 baja.Date.prototype.make = function (obj) { 4116 return baja.Date.make.apply(baja.Date, arguments); 4117 }; 4118 4119 /** 4120 * Decode a Date from a String. 4121 * 4122 * @name baja.Date#decodeFromString 4123 * @function 4124 * 4125 * @param {String} str 4126 * @returns {baja.Date} 4127 */ 4128 baja.Date.prototype.decodeFromString = function (str) { 4129 // Decode ISO 8601 encoding yyyy-mm-dd 4130 var res = /^([0-9]{4,})\-([0-9]{2})\-([0-9]{2})$/.exec(str); 4131 4132 if (res === null) { 4133 throw new Error("Could not decode baja.Date: " + str); 4134 } 4135 4136 function parse(s) { 4137 return parseInt(s, 10); 4138 } 4139 4140 var d = baja.Date.make({ 4141 year: parse(res[1]), 4142 month: parse(res[2]) - 1, 4143 day: parse(res[3]) 4144 }); 4145 4146 return d; 4147 }; 4148 baja.Date.prototype.decodeFromString = cacheDecode(baja.Date.prototype.decodeFromString); 4149 4150 /** 4151 * Encode the Date to a String. 4152 * 4153 * @name baja.Date#encodeToString 4154 * @function 4155 * 4156 * @returns {String} 4157 */ 4158 baja.Date.prototype.encodeToString = function () { 4159 var s = zeroPad(this.$year, 4) + "-" + zeroPad((this.$month + 1), 2) + "-" + zeroPad(this.$day, 2); 4160 return s; 4161 }; 4162 baja.Date.prototype.encodeToString = cacheEncode(baja.Date.prototype.encodeToString); 4163 4164 var dateCompareTo = function (date) { 4165 strictArg(date, baja.Date); 4166 if (this.$year !== date.$year) { 4167 return this.$year - date.$year; 4168 } 4169 if (this.$month !== date.$month) { 4170 return this.$month - date.$month; 4171 } 4172 if (this.$day !== date.$day) { 4173 return this.$day - date.$day; 4174 } 4175 return 0; 4176 }; 4177 4178 /** 4179 * Equality test. 4180 * 4181 * @param obj 4182 * @returns {Boolean} 4183 */ 4184 baja.Date.prototype.equals = function (obj) { 4185 if (bajaHasType(obj) && obj.getType().equals(this.getType())) { 4186 return dateCompareTo.call(this, obj) === 0; 4187 } 4188 else { 4189 return false; 4190 } 4191 }; 4192 4193 /** 4194 * Default Date instance. 4195 */ 4196 baja.Date.DEFAULT = new baja.Date(1970, 0, 1); 4197 4198 // Register Type 4199 baja.Date.registerType("baja:Date"); 4200 4201 /** 4202 * Return a Date that maps to the current day. 4203 * 4204 * @name baja.Date#today 4205 * @function 4206 * 4207 * @returns {baja.Date} 4208 */ 4209 baja.Date.today = function () { 4210 return baja.Date.make({jsDate: new Date()}); 4211 }; 4212 4213 /** 4214 * Return the year. 4215 * 4216 * @returns {Number} 4217 */ 4218 baja.Date.prototype.getYear = function () { 4219 return this.$year; 4220 }; 4221 4222 /** 4223 * Return the month. 4224 * 4225 * @returns {baja.FrozenEnum} a baja:Month FrozenEnum 4226 */ 4227 baja.Date.prototype.getMonth = function () { 4228 return baja.$("baja:Month").get(this.$month); 4229 }; 4230 4231 /** 4232 * Return the day (1-31). 4233 * 4234 * @returns {Number} 4235 */ 4236 baja.Date.prototype.getDay = function () { 4237 return this.$day; 4238 }; 4239 4240 /** 4241 * Return a new JavaScript Date using this date's year, month and day. 4242 * 4243 * @returns {Date} 4244 */ 4245 baja.Date.prototype.getJsDate = function () { 4246 // JavaScript Date is mutable therefore we have to return a new instance of Date each time 4247 return new Date(this.$year, 4248 this.$month, 4249 this.$day, 4250 /*hours*/0, 4251 /*minutes*/0, 4252 /*seconds*/0, 4253 /*ms*/0); 4254 }; 4255 4256 var getCachedJsDate = function () { 4257 // Lazily create and return an immutable cached version of the JavaScript Date 4258 if (this.$jsDate === undefined) { 4259 this.$jsDate = this.getJsDate(); 4260 } 4261 return this.$jsDate; 4262 }; 4263 4264 /** 4265 * Return the weekday as a baja:Weekday FrozenEnum. 4266 * 4267 * @returns {baja.FrozenEnum} a baja:Weekday FrozenEnum. 4268 */ 4269 baja.Date.prototype.getWeekday = function () { 4270 return baja.$("baja:Weekday").get(getCachedJsDate.call(this).getDay()); 4271 }; 4272 4273 /** 4274 * Return true if the specified date is before this date. 4275 * 4276 * @returns {Boolean} 4277 */ 4278 baja.Date.prototype.isBefore = function (date) { 4279 return dateCompareTo.call(this, date) < 0; 4280 }; 4281 4282 /** 4283 * Return true if the specified date is after this date. 4284 * 4285 * @returns {Boolean} 4286 */ 4287 baja.Date.prototype.isAfter = function (date) { 4288 return dateCompareTo.call(this, date) > 0; 4289 }; 4290 4291 /** 4292 * Return a String representation of the Date. 4293 * 4294 * @param {Object} [obj] the Object Literal for the method's arguments. 4295 * @params {String} [obj.textPattern] the text pattern to use for formatting. 4296 * If not specified, then the user's default 4297 * time format text pattern will be used. 4298 * @param {Number} [obj.show] flags used to format the time. For more information, 4299 * please see {@link baja.TimeFormat}. 4300 * @returns {String} 4301 */ 4302 baja.Date.prototype.toString = function (obj) { 4303 var textPattern = (obj && obj.textPattern) || baja.getTimeFormatPattern(), 4304 show = ((obj && obj.show) || 0) | SHOW_DATE; 4305 4306 // Filter out invalid flags 4307 show &= ~SHOW_TIME; 4308 show &= ~SHOW_SECONDS; 4309 show &= ~SHOW_MILLIS; 4310 4311 return toDateTimeString({ 4312 show: show, 4313 textPattern: textPattern, 4314 year: this.getYear(), 4315 month: this.getMonth(), 4316 day: this.getDay() 4317 }); 4318 }; 4319 4320 // TODO: getDayOfYear, isLeapYear, add, subtract, delta, nextDay, prevDay, nextMonth, prevMonth, nextYear, prevYear, next, prev 4321 4322 /** 4323 * @class Represents a baja:AbsTime in BajaScript. 4324 * <p> 4325 * AbsTime encapsulates an absolute point in time 4326 * relative to a given time zone. 4327 * <p> 4328 * When creating a Simple, always use the 'make' method instead of creating a new Object. 4329 * 4330 * @name baja.AbsTime 4331 * @extends baja.Simple 4332 */ 4333 baja.AbsTime = function (date, time, offset) { 4334 // Constructor should be considered private 4335 baja.AbsTime.$super.apply(this, arguments); 4336 this.$date = date; 4337 this.$time = time; 4338 this.$offset = offset; 4339 }.$extend(baja.Simple); 4340 4341 /** 4342 * Make an AbsTime. 4343 * <p> 4344 * An Object Literal is used for the method's arguments... 4345 * <pre> 4346 * var at1 = baja.AbsTime.make({ 4347 * date: baja.Date.make({year: 1981, month: 5, day: 17}), 4348 * time: baja.Time.make({hour: 15, min: 30}), 4349 * offset: 0 4350 * }); 4351 * // ...or from a JavaScript Date (UTC offset is ignored)... 4352 * var at2 = baja.AbsTime.make({jsDate: new Date()}); 4353 * </pre> 4354 * 4355 * @param {Object} [obj] the Object Literal used for the method's arguments. 4356 * @param {baja.Date} [obj.date] 4357 * @param {baja.Time} [obj.time] 4358 * @param {Number} [obj.offset] UTC offset in milliseconds. 4359 * @param {Date} [jsDate] if defined, this date is used for the date and time. 4360 * The UTC offset from this date is currently ignored 4361 * (the offset argument is used) This will override the 4362 * date and time arguments. 4363 * @returns baja.AbsTime 4364 */ 4365 baja.AbsTime.make = function (obj) { 4366 obj = objectify(obj); 4367 4368 var date = bajaDef(obj.date, baja.Date.DEFAULT), 4369 time = bajaDef(obj.time, baja.Time.DEFAULT), 4370 offset = bajaDef(obj.offset, 0), // TODO: Should we use the browser's timezone offset by default? 4371 jsDate; 4372 4373 if (obj.jsDate !== undefined) { 4374 // Get information from JavaScript Date 4375 jsDate = obj.jsDate; 4376 4377 if (!(jsDate instanceof Date)) { 4378 throw new Error("jsDate must be a JavaScript Date"); 4379 } 4380 4381 date = baja.Date.make({ 4382 "year": jsDate.getFullYear(), 4383 "month": jsDate.getMonth(), 4384 "day": jsDate.getDate() 4385 }); 4386 4387 time = baja.Time.make({ 4388 "hour": jsDate.getHours(), 4389 "min": jsDate.getMinutes(), 4390 "sec": jsDate.getSeconds(), 4391 "ms": jsDate.getMilliseconds() 4392 }); 4393 } 4394 4395 // The year, month and day must always be specified for this to be valid 4396 strictAllArgs([date, time, offset], [baja.Date, baja.Time, Number]); 4397 4398 if (date === baja.Date.DEFAULT && time === baja.Time.DEFAULT && offset === 0) { 4399 return baja.AbsTime.DEFAULT; 4400 } 4401 4402 return new baja.AbsTime(date, time, offset); 4403 }; 4404 4405 /** 4406 * Make an AbsTime. 4407 * <p> 4408 * An Object Literal is used for the method's arguments... 4409 * <pre> 4410 * var at1 = baja.$("baja:AbsTime").make({ 4411 * date: baja.$("baja:Date").make({year: 1981, month: 5, day: 17}), 4412 * time: baja.$("baja:Time").make({hour: 15, min: 30}), 4413 * offset: 0 4414 * }); 4415 * // ...or from a JavaScript Date... 4416 * var at2 = baja.$("baja:AbsTime").make({jsDate: new Date(), offset: 5}); 4417 * </pre> 4418 * 4419 * @param {Object} [obj] the Object Literal used for the method's arguments. 4420 * @param {baja.Date} [obj.date] 4421 * @param {baja.Time} [obj.time] 4422 * @param {Number} [offset] 4423 * @param {Date} [jsDate] if defined, this date is used for the date and time. 4424 * The UTC offset from this date is currently ignored 4425 * (the offset argument is used) This will override the 4426 * date and time arguments. 4427 * @returns baja.AbsTime 4428 */ 4429 baja.AbsTime.prototype.make = function (obj) { 4430 return baja.AbsTime.make.apply(baja.AbsTime, arguments); 4431 }; 4432 4433 /** 4434 * Decode an AbsTime from a String. 4435 * 4436 * @param {String} str 4437 * @returns {baja.AbsTime} 4438 */ 4439 baja.AbsTime.prototype.decodeFromString = function (str) { 4440 // Decode ISO 8601 encoding that BAbsTime creates 4441 var res = /^([0-9]{4,})\-([0-9]{2})\-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})\.([0-9]{3})(?:(Z)|(?:(\-|\+)([0-9]{2}):([0-9]{2})))$/.exec(str); 4442 4443 if (res === null) { 4444 throw new Error("Could not decode AbsTime: " + str); 4445 } 4446 4447 function parse(s) { 4448 return parseInt(s, 10); 4449 } 4450 4451 var date = baja.Date.make({ 4452 "year": parse(res[1]), 4453 "month": parse(res[2]) - 1, // Zero indexed based 4454 "day": parse(res[3]) 4455 }); 4456 4457 var time = baja.Time.make({ 4458 "hour": parse(res[4]), 4459 "min": parse(res[5]), 4460 "sec": parse(res[6]), 4461 "ms": parse(res[7]) 4462 }); 4463 4464 var offset = 0; 4465 4466 if (res[8] !== "Z") { 4467 // Parse hour and minutes and convert to millis 4468 offset = parse(res[10]) * (60 * 60 * 1000) + parse(res[11]) * (60 * 1000); 4469 4470 // Apply sign 4471 if (res[9] === "-") { 4472 offset *= -1; 4473 } 4474 } 4475 4476 return baja.AbsTime.make({ 4477 "date": date, 4478 "time": time, 4479 "offset": offset 4480 }); 4481 }; 4482 baja.AbsTime.prototype.decodeFromString = cacheDecode(baja.AbsTime.prototype.decodeFromString); 4483 4484 /** 4485 * Encode the AbsTime to a String. 4486 * 4487 * @returns {String} 4488 */ 4489 baja.AbsTime.prototype.encodeToString = function () { 4490 var s = this.$date.encodeToString() + "T" + this.$time.encodeToString(); 4491 4492 if (this.$offset === 0) { 4493 s += "Z"; 4494 } 4495 else { 4496 var hrOff = Math.floor(Math.abs(this.$offset / (1000 * 60 * 60))); 4497 var minOff = Math.floor(Math.abs((this.$offset % (1000 * 60 * 60)) / (1000 * 60))); 4498 4499 if (this.$offset < 0) { 4500 s += "-"; 4501 } 4502 else { 4503 s += "+"; 4504 } 4505 4506 s += zeroPad(hrOff, 2) + ":"; 4507 s += zeroPad(minOff, 2); 4508 } 4509 4510 return s; 4511 }; 4512 baja.AbsTime.prototype.encodeToString = cacheEncode(baja.AbsTime.prototype.encodeToString); 4513 4514 /** 4515 * Equality test. 4516 * 4517 * @param obj 4518 * @returns {Boolean} 4519 */ 4520 baja.AbsTime.prototype.equals = function (obj) { 4521 if (bajaHasType(obj) && obj.getType().equals(this.getType())) { 4522 return obj.$date.equals(this.$date) && 4523 obj.$time.equals(this.$time) && 4524 obj.$offset === this.$offset; 4525 } 4526 else { 4527 return false; 4528 } 4529 }; 4530 4531 /** 4532 * Default AbsTime instance - maps to Java Epoch. 4533 */ 4534 baja.AbsTime.DEFAULT = new baja.AbsTime(baja.Date.DEFAULT, baja.Time.DEFAULT, 0); 4535 4536 // Register Type 4537 baja.AbsTime.registerType("baja:AbsTime"); 4538 4539 /** 4540 * Return the data type symbol. 4541 * 4542 * @returns {String} 4543 */ 4544 baja.AbsTime.prototype.getDataTypeSymbol = function () { 4545 return "a"; 4546 }; 4547 4548 /** 4549 * Return the time. 4550 * 4551 * @returns {baja.Time} 4552 */ 4553 baja.AbsTime.prototype.getTime = function () { 4554 return this.$time; 4555 }; 4556 4557 /** 4558 * Return the date. 4559 * 4560 * @returns {baja.Date} 4561 */ 4562 baja.AbsTime.prototype.getDate = function () { 4563 return this.$date; 4564 }; 4565 4566 /** 4567 * Return the UTC offset. 4568 * 4569 * @returns {Number} 4570 */ 4571 baja.AbsTime.prototype.getOffset = function () { 4572 return this.$offset; 4573 }; 4574 4575 /** 4576 * Make an AbsTime with the current date and time. 4577 * 4578 * @returns {baja.AbsTime} 4579 */ 4580 baja.AbsTime.now = function () { 4581 // TODO: Should we use the browser's timezone? 4582 return baja.AbsTime.make({ jsDate: new Date() }); 4583 }; 4584 4585 /** 4586 * Return a new JavaScript Date based on this AbsTime. 4587 * <p> 4588 * Please, note the AbsTime UTC offset is ignored. 4589 * 4590 * @name baja.AbsTime#getJsDate 4591 * @function 4592 * 4593 * @returns {Date} 4594 */ 4595 baja.AbsTime.prototype.getJsDate = function () { 4596 // Create a JavaScript Date and return it (warning has no timezone offset) 4597 return new Date(this.$date.getYear(), 4598 this.$date.getMonth().getOrdinal(), 4599 this.$date.getDay(), 4600 this.$time.getHour(), 4601 this.$time.getMinute(), 4602 this.$time.getSecond(), 4603 this.$time.getMillisecond()); 4604 }; 4605 4606 /** 4607 * Return a String representation of the AbsTime. 4608 * 4609 * @param {Object} [obj] the Object Literal for the method's arguments. 4610 * @params {String} [obj.textPattern] the text pattern to use for formatting. 4611 * If not specified, then the user's default 4612 * time format text pattern will be used. 4613 * @param {Number} [obj.show] flags used to format the time. For more information, 4614 * please see {@link baja.TimeFormat}. 4615 * @returns {String} 4616 */ 4617 baja.AbsTime.prototype.toString = function (obj) { 4618 var textPattern = (obj && obj.textPattern) || baja.getTimeFormatPattern(), 4619 show = (obj && obj.show) || (SHOW_DATE | SHOW_TIME | SHOW_SECONDS | SHOW_MILLIS), 4620 time = this.$time, 4621 date = this.$date; 4622 4623 return toDateTimeString({ 4624 show: show, 4625 textPattern: textPattern, 4626 year: date.getYear(), 4627 month: date.getMonth(), 4628 day: date.getDay(), 4629 hour: time.getHour(), 4630 min: time.getMinute(), 4631 sec: time.getSecond(), 4632 ms: time.getMillisecond() 4633 }); 4634 }; 4635 4636 /** 4637 * @class Represents a baja:RelTime in BajaScript. 4638 * <p> 4639 * RelTime is a Simple type for managing 4640 * a relative amount of time. 4641 * <p> 4642 * When creating a Simple, always use the 'make' method instead of creating a new Object. 4643 * 4644 * @name baja.RelTime 4645 * @extends baja.Simple 4646 */ 4647 baja.RelTime = function (ms) { 4648 baja.RelTime.$super.apply(this, arguments); 4649 this.$ms = parseInt(ms, 10); 4650 }.$extend(baja.Simple); 4651 4652 /** 4653 * Make a RelTime. 4654 * <p> 4655 * This method can take a number of milliseconds of an Object Literal with the method's argument... 4656 * <pre> 4657 * var rt1 = baja.RelTime.make(1000); // One second 4658 * 4659 * // ...or we can specify an Object Literal for more arguments... 4660 * 4661 * // Create a RelTime with 2 days + 2 hours + 2 minutes + 2 seconds + 2 milliseconds... 4662 * var rt2 = baja.RelTime.make({ 4663 * days: 2, 4664 * hours: 2, 4665 * minutes: 2, 4666 * seconds: 2, 4667 * ms: 2 4668 * }); 4669 * </pre> 4670 * 4671 * @param {Object|Number} [obj] the Object Literal or the number of milliseconds. 4672 * @param {Object} [obj.days] the number of days. 4673 * @param {Object} [obj.hours] the number of hours. 4674 * @param {Object} [obj.minutes] the number of minutes. 4675 * @param {Object} [obj.seconds] the number of seconds. 4676 * @param {Object} [obj.ms] the number of milliseconds. 4677 * @returns {baja.RelTime} 4678 */ 4679 baja.RelTime.make = function (obj) { 4680 var ms = 0; 4681 4682 if (typeof obj === "number") { 4683 ms = obj; 4684 } 4685 else { 4686 obj = objectify(obj, "ms"); 4687 ms = bajaDef(obj.ms, 0); 4688 4689 strictArg(ms, Number); 4690 4691 if (typeof obj.days === "number") { 4692 ms += MILLIS_IN_DAY * obj.days; 4693 } 4694 if (typeof obj.hours === "number") { 4695 ms += MILLIS_IN_HOUR * obj.hours; 4696 } 4697 if (typeof obj.minutes === "number") { 4698 ms += MILLIS_IN_MINUTE * obj.minutes; 4699 } 4700 if (typeof obj.seconds === "number") { 4701 ms += MILLIS_IN_SECOND * obj.seconds; 4702 } 4703 } 4704 4705 if (ms === 0) { 4706 return baja.RelTime.DEFAULT; 4707 } 4708 return new baja.RelTime(ms); 4709 }; 4710 4711 /** 4712 * Make a RelTime. 4713 * <p> 4714 * This method can take a number of milliseconds of an Object Literal with the method's argument... 4715 * <pre> 4716 * var rt1 = baja.$("baja:RelTime").make(1000); // One second 4717 * 4718 * // ...or we can specify an Object Literal for more arguments... 4719 * 4720 * // Create a RelTime with 2 days + 2 hours + 2 minutes + 2 seconds + 2 milliseconds... 4721 * var rt2 = baja.$("baja:RelTime").make({ 4722 * days: 2, 4723 * hours: 2, 4724 * minutes: 2, 4725 * seconds: 2, 4726 * ms: 2 4727 * }); 4728 * </pre> 4729 * 4730 * @param {Object|Number} [obj] the Object Literal or the number of milliseconds. 4731 * @param {Object} [obj.days] the number of days. 4732 * @param {Object} [obj.hours] the number of hours. 4733 * @param {Object} [obj.minutes] the number of minutes. 4734 * @param {Object} [obj.seconds] the number of seconds. 4735 * @param {Object} [obj.ms] the number of milliseconds. 4736 * @returns {baja.RelTime} 4737 */ 4738 baja.RelTime.prototype.make = function (ms) { 4739 return baja.RelTime.make.apply(baja.RelTime, arguments); 4740 }; 4741 4742 /** 4743 * Decode a RelTime from a String. 4744 * 4745 * @name baja.RelTime#decodeFromString 4746 * @function 4747 * 4748 * @param {String} str 4749 * @returns {baja.RelTime} 4750 */ 4751 baja.RelTime.prototype.decodeFromString = function (str) { 4752 // Parse number 4753 var n = Number(str); 4754 4755 // If still not a number then throw an error 4756 if (isNaN(n)) { 4757 throw new Error("Unable to create RelTime: " + str); 4758 } 4759 4760 return baja.RelTime.make(n); 4761 }; 4762 baja.RelTime.prototype.decodeFromString = cacheDecode(baja.RelTime.prototype.decodeFromString); 4763 4764 /** 4765 * Encode the RelTime to a String. 4766 * 4767 * @name baja.RelTime#encodeToString 4768 * @function 4769 * 4770 * @returns {String} 4771 */ 4772 baja.RelTime.prototype.encodeToString = function () { 4773 return this.$ms.toString(); 4774 }; 4775 baja.RelTime.prototype.encodeToString = cacheEncode(baja.RelTime.prototype.encodeToString); 4776 4777 /** 4778 * Default RelTime instance. 4779 */ 4780 baja.RelTime.DEFAULT = new baja.RelTime(0); 4781 4782 // Register Type 4783 baja.RelTime.registerType("baja:RelTime"); 4784 4785 /** 4786 * Milliseconds in a second (Number). 4787 */ 4788 baja.RelTime.MILLIS_IN_SECOND = MILLIS_IN_SECOND; 4789 4790 /** 4791 * Milliseconds in a minute (Number). 4792 */ 4793 baja.RelTime.MILLIS_IN_MINUTE = MILLIS_IN_MINUTE; 4794 4795 /** 4796 * Milliseconds in an hour (Number). 4797 */ 4798 baja.RelTime.MILLIS_IN_HOUR = MILLIS_IN_HOUR; 4799 4800 /** 4801 * Milliseconds in a day (Number). 4802 */ 4803 baja.RelTime.MILLIS_IN_DAY = MILLIS_IN_DAY; 4804 4805 /** 4806 * RelTime instance for a second. 4807 */ 4808 baja.RelTime.SECOND = baja.RelTime.make(MILLIS_IN_SECOND); 4809 4810 /** 4811 * RelTime instance for a minute. 4812 */ 4813 baja.RelTime.MINUTE = baja.RelTime.make(MILLIS_IN_MINUTE); 4814 4815 /** 4816 * RelTime instance for an hour. 4817 */ 4818 baja.RelTime.HOUR = baja.RelTime.make(MILLIS_IN_HOUR); 4819 4820 /** 4821 * RelTime instance for a day. 4822 */ 4823 baja.RelTime.DAY = baja.RelTime.make(MILLIS_IN_DAY); 4824 4825 /** 4826 * Equality test. 4827 * 4828 * @function 4829 * 4830 * @param obj 4831 * @returns {Boolean} 4832 */ 4833 baja.RelTime.prototype.equals = valueOfEquals; 4834 4835 /** 4836 * Return the data type symbol. 4837 * 4838 * @name baja.RelTime#getDataTypeSymbol 4839 * @function 4840 * 4841 * @returns {String} 4842 */ 4843 baja.RelTime.prototype.getDataTypeSymbol = function () { 4844 return "r"; 4845 }; 4846 4847 /** 4848 * Return number of milliseconds. 4849 * 4850 * @returns {Number} 4851 */ 4852 baja.RelTime.prototype.valueOf = function () { 4853 return this.$ms; 4854 }; 4855 4856 /** 4857 * Return a String representation of a RelTime. 4858 * 4859 * @returns {String} 4860 */ 4861 baja.RelTime.prototype.toString = function () { 4862 return this.$ms.toString(); 4863 }; 4864 4865 /** 4866 * Return number of milliseconds. 4867 * 4868 * @returns {Number} 4869 */ 4870 baja.RelTime.prototype.getMillis = function () { 4871 return this.$ms; 4872 }; 4873 4874 /** 4875 * Return the milliseconds part of this duration. 4876 * 4877 * @returns {Number} 4878 */ 4879 baja.RelTime.prototype.getMillisPart = function () { 4880 return this.$ms % 1000; 4881 }; 4882 4883 function truncateToInteger(num) { 4884 return Math[num < 0 ? 'ceil' : 'floor'](num); 4885 } 4886 /** 4887 * Return number of seconds. 4888 * 4889 * @returns {Number} 4890 */ 4891 baja.RelTime.prototype.getSeconds = function () { 4892 return truncateToInteger(this.$ms / MILLIS_IN_SECOND); 4893 }; 4894 4895 /** 4896 * Return the seconds part of this duration. 4897 * 4898 * @returns {Number} 4899 */ 4900 baja.RelTime.prototype.getSecondsPart = function () { 4901 return this.getSeconds() % 60; 4902 }; 4903 4904 /** 4905 * Return number of minutes. 4906 * 4907 * @returns {Number} 4908 */ 4909 baja.RelTime.prototype.getMinutes = function () { 4910 return truncateToInteger(this.$ms / MILLIS_IN_MINUTE); 4911 }; 4912 4913 /** 4914 * Return the minutes part of this duration. 4915 * 4916 * @returns {Number} 4917 */ 4918 baja.RelTime.prototype.getMinutesPart = function () { 4919 return this.getMinutes() % 60; 4920 }; 4921 4922 /** 4923 * Return number of hours. 4924 * 4925 * @returns {Number} 4926 */ 4927 baja.RelTime.prototype.getHours = function () { 4928 return truncateToInteger(this.$ms / MILLIS_IN_HOUR); 4929 }; 4930 4931 /** 4932 * Return the hours part of this duration. 4933 * 4934 * @returns {Number} 4935 */ 4936 baja.RelTime.prototype.getHoursPart = function () { 4937 return this.getHours() % 24; 4938 }; 4939 4940 /** 4941 * Return number of days. 4942 * 4943 * @returns {Number} 4944 */ 4945 baja.RelTime.prototype.getDays = function () { 4946 return truncateToInteger(this.$ms / MILLIS_IN_DAY); 4947 }; 4948 4949 /** 4950 * Return the days part of this duration. 4951 * 4952 * @returns {Number} 4953 */ 4954 baja.RelTime.prototype.getDaysPart = function () { 4955 return this.getDays(); 4956 }; 4957 4958 }()); // dateAndTime 4959 4960 //////////////////////////////////////////////////////////////// 4961 // Icon 4962 //////////////////////////////////////////////////////////////// 4963 4964 /** 4965 * @class A BajaScript Icon. 4966 * <p> 4967 * A BajaScript Icon may contain multiple Image references. 4968 * <p> 4969 * For maximum efficiency, all icons in BajaScript are completely interned. 4970 * <p> 4971 * This Constructor shouldn't be invoked directly. Please use the 'make' methods to create 4972 * an instance of an Icon. 4973 * 4974 * @name baja.Icon 4975 * @extends baja.Simple 4976 */ 4977 4978 var iconsCache = {}; 4979 4980 baja.Icon = function (ords) { 4981 baja.Icon.$super.apply(this, arguments); 4982 this.$ords = strictArg(ords, Array); 4983 }.$extend(baja.Simple); 4984 4985 /** 4986 * Default Icon instance. 4987 */ 4988 baja.Icon.DEFAULT = new baja.Icon([]); 4989 4990 function encodeIcon(ords) { 4991 return ords.join("\n"); 4992 } 4993 4994 /** 4995 * Make an Icon. 4996 * 4997 * @param {Array|String} [ords] an array of Image ORDs or Strings or just a single ORD or String 4998 * @returns {baja.Icon} 4999 */ 5000 baja.Icon.make = function (ords) { 5001 if (arguments.length === 0) { 5002 return baja.Icon.DEFAULT; 5003 } 5004 5005 // If a String is passed in then convert to an Array 5006 if (typeof ords === "string") { 5007 ords = [ords]; 5008 } 5009 // If an array is passed in then treat this as an array of ORDs 5010 if (ords && ords.constructor === Array) { 5011 var encodedStr = encodeIcon(ords); 5012 // If available from the cache then use it... 5013 if (iconsCache.hasOwnProperty(encodedStr)) { 5014 return iconsCache[encodedStr]; 5015 } 5016 5017 if (ords.length === 0) { 5018 return baja.Icon.DEFAULT; 5019 } 5020 if (ords.length === 1 && ords[0].toString() === "") { 5021 return baja.Icon.DEFAULT; 5022 } 5023 5024 var newOrds = [], i; 5025 for (i = 0; i < ords.length; ++i) { 5026 newOrds.push(baja.Ord.make(ords[i].toString())); 5027 } 5028 5029 var icon = new baja.Icon(newOrds); 5030 // Store icon in cache 5031 iconsCache[encodedStr] = icon; 5032 icon.$cEncStr = encodedStr; 5033 5034 return icon; 5035 } 5036 else { 5037 throw new Error("Invalid argument for baja.Icon.make"); 5038 } 5039 }; 5040 5041 /** 5042 * Make an Icon. 5043 * 5044 * @param {Array} [images] an array of Image ORDs or Strings 5045 * @returns {baja.Icon} 5046 */ 5047 baja.Icon.prototype.make = function (args) { 5048 return baja.Icon.make.apply(baja.Icon, arguments); 5049 }; 5050 5051 /** 5052 * Decode a String to an Icon. 5053 * 5054 * @param {String} str 5055 * @returns {baja.Icon} 5056 */ 5057 baja.Icon.prototype.decodeFromString = function (str) { 5058 // If available from the cache then use it... 5059 if (iconsCache.hasOwnProperty(str)) { 5060 return iconsCache[str]; 5061 } 5062 5063 return baja.Icon.make(str.split("\n")); 5064 }; 5065 5066 /** 5067 * Encode Icon to a String. 5068 * 5069 * @returns {String} 5070 */ 5071 baja.Icon.prototype.encodeToString = function () { 5072 if (!this.$cEncStr) { 5073 this.$cEncStr = encodeIcon(this.$ords); 5074 } 5075 return this.$cEncStr; 5076 }; 5077 5078 // Register Type 5079 baja.Icon.registerType("baja:Icon"); 5080 5081 /** 5082 * Return a String representation of the Icon. 5083 * 5084 * @returns {String} 5085 */ 5086 baja.Icon.prototype.toString = function () { 5087 return this.encodeToString(); 5088 }; 5089 5090 /** 5091 * Return the inner value of the Icon. 5092 * 5093 * @returns {String} 5094 */ 5095 baja.Icon.prototype.valueOf = function () { 5096 return this.toString(); 5097 }; 5098 5099 /** 5100 * Return a copy of the image ORDs Array. 5101 * <p> 5102 * Please note, this is a copy of the Image ORDs and not the URIs to access the images. 5103 * 5104 * @see baja.Icon#getImageUris 5105 * 5106 * @returns {Array} an array of ORDs to the images. 5107 */ 5108 baja.Icon.prototype.getImageOrds = function () { 5109 // Return a copy of the images array 5110 return Array.prototype.slice.call(this.$ords); 5111 }; 5112 5113 /** 5114 * Return an array of URIs to the images. 5115 * 5116 * @returns {Array} an array of URI Strings to the images. 5117 */ 5118 baja.Icon.prototype.getImageUris = function () { 5119 var regex = /^module\:\/\//, i; 5120 if (!this.$uris) { 5121 this.$uris = []; 5122 for (i = 0; i < this.$ords.length; ++i) { 5123 this.$uris.push(this.$ords[i].toString().replace(regex, "/module/")); 5124 } 5125 } 5126 return Array.prototype.slice.call(this.$uris); 5127 }; 5128 5129 /** 5130 * Return the standard Object Icon. 5131 * 5132 * @returns {baja.Icon} 5133 */ 5134 var iconStdObj; 5135 baja.Icon.getStdObjectIcon = function () { 5136 if (!iconStdObj) { 5137 iconStdObj = baja.Icon.make(["module://icons/x16/object.png"]); 5138 } 5139 return iconStdObj; 5140 }; 5141 5142 //////////////////////////////////////////////////////////////// 5143 // Format 5144 //////////////////////////////////////////////////////////////// 5145 5146 /** 5147 * @class Format is used to format Objects into Strings using 5148 * a standardized formatting pattern language. 5149 * <p> 5150 * Currently this implementation of Format is limited to processing Lexicon patterns. 5151 * <p> 5152 * This Constructor shouldn't be invoked directly. Please use the 'make' methods to create 5153 * an instance of a Format. 5154 * 5155 * @name baja.Format 5156 * @extends baja.Simple 5157 */ 5158 baja.Format = function (pattern) { 5159 baja.Format.$super.apply(this, arguments); 5160 this.$pattern = strictArg(pattern, String); 5161 }.$extend(baja.Simple); 5162 5163 /** 5164 * Default Format instance. 5165 */ 5166 baja.Format.DEFAULT = new baja.Format(""); 5167 5168 /** 5169 * Make a Format. 5170 * 5171 * @param {String} [pattern] the Format Pattern String. 5172 * @returns {baja.Format} 5173 */ 5174 baja.Format.make = function (pattern) { 5175 pattern = pattern || ""; 5176 5177 if (pattern === "") { 5178 return baja.Format.DEFAULT; 5179 } 5180 5181 baja.strictArg(pattern, String); 5182 5183 return new baja.Format(pattern); 5184 }; 5185 5186 /** 5187 * Make a Format. 5188 * 5189 * @param {String} [pattern] the Format Pattern String. 5190 * @returns {baja.Format} 5191 */ 5192 baja.Format.prototype.make = function (args) { 5193 return baja.Format.make.apply(baja.Format, arguments); 5194 }; 5195 5196 /** 5197 * Decode a String to a Format. 5198 * 5199 * @param {String} str 5200 * @returns {baja.Format} 5201 */ 5202 baja.Format.prototype.decodeFromString = function (str) { 5203 return baja.Format.make(str); 5204 }; 5205 5206 /** 5207 * Encode Format to a String. 5208 * 5209 * @name baja.Format#encodeToString 5210 * @function 5211 * 5212 * @returns {String} 5213 */ 5214 baja.Format.prototype.encodeToString = function () { 5215 return this.$pattern; 5216 }; 5217 5218 // Register Type 5219 baja.Format.registerType("baja:Format"); 5220 5221 /** 5222 * Return a String representation of the Format. 5223 * 5224 * @returns {String} 5225 */ 5226 baja.Format.prototype.toString = function () { 5227 return this.$pattern; 5228 }; 5229 5230 /** 5231 * Return the inner value of the Format. 5232 * 5233 * @function 5234 * 5235 * @returns {String} 5236 */ 5237 baja.Format.prototype.valueOf = baja.Format.prototype.toString; 5238 5239 // Scope some of the variables here... 5240 (function () { 5241 var formatMatches = [], 5242 formatErr = "error: ", 5243 lexiconRegex = /^lexicon\(([a-zA-Z0-9]+)\:([a-zA-Z0-9.\-_]+)\)$/; 5244 5245 // Replace %% with % 5246 formatMatches.push({ 5247 isMatch: function (content) { 5248 return content === ""; 5249 }, 5250 replace: function () { 5251 return "%"; 5252 } 5253 }); 5254 5255 // Identity %.% 5256 formatMatches.push({ 5257 isMatch: function (content) { 5258 return content === "."; 5259 }, 5260 replace: function (content, obj) { 5261 var val, parent, slot = null, i, o; 5262 if (obj.target) { 5263 if (obj.target.container) { 5264 if (obj.target.propertyPath && obj.target.propertyPath.length > 0) { 5265 val = parent = obj.target.container; 5266 5267 for (i = 0; i < obj.target.propertyPath.length; ++i) { 5268 slot = obj.target.propertyPath[i]; 5269 parent = val; 5270 val = val.get(slot); 5271 } 5272 5273 return obj.display ? parent.getDisplay(slot) : parent.get(slot); 5274 } 5275 else { 5276 return obj.display ? obj.target.container.getDisplay(obj.target.slot) : obj.target.container.get(obj.target.slot); 5277 } 5278 } 5279 else { 5280 o = obj.target.getObject(); 5281 return obj.display && bajaHasType(o) && o.getType().isComplex() ? o.getDisplay() : o; 5282 } 5283 } 5284 else { 5285 o = obj.object; 5286 return obj.display && bajaHasType(o) && o.getType().isComplex() ? o.getDisplay() : o; 5287 } 5288 } 5289 }); 5290 5291 // Lexicon %.% 5292 formatMatches.push({ 5293 isMatch: function (content) { 5294 return content.match(lexiconRegex); 5295 }, 5296 replace: function (content, obj) { 5297 var result = lexiconRegex.exec(content), 5298 res = null; 5299 lexiconRegex.lastIndex = 0; 5300 try { 5301 res = baja.lex(result[1]).get(result[2]); 5302 } 5303 catch (ignore) {} 5304 return res; 5305 } 5306 }); 5307 5308 // Reflect Call 5309 formatMatches.push({ 5310 isMatch: function (content) { 5311 // Always last in the list so this should always match 5312 return true; 5313 }, 5314 replace: function (content, obj) { 5315 var val, parent, slot = null, x, 5316 split = content.split(/\./g); 5317 5318 // Process the format text 5319 val = parent = obj.object; 5320 5321 for (x = 0; x < split.length; ++x) { 5322 if (val === null || val === undefined) { 5323 return formatErr + content; 5324 } 5325 5326 // First try looking for the Slot 5327 if (bajaHasType(val, "baja:Complex") && val.has(split[x])) { 5328 slot = val.getSlot(split[x]); 5329 parent = val; 5330 val = val.get(slot); 5331 } 5332 // If there's not Slot then see if a function exists 5333 else { 5334 // Nullify this since at this point we're no longer looking up a Slot chain 5335 slot = null; 5336 parent = null; 5337 5338 if (typeof val["get" + split[x].capitalizeFirstLetter()] === "function") { 5339 val = val["get" + split[x].capitalizeFirstLetter()](); 5340 } 5341 else if (typeof val[split[x]] === "function") { 5342 val = val[split[x]](); 5343 } 5344 else { 5345 val = null; 5346 } 5347 } 5348 } 5349 5350 if (slot && parent) { 5351 val = obj.display ? parent.getDisplay(slot) : parent.get(slot); 5352 return val; 5353 } 5354 else if (bajaHasType(val, "baja:Complex")) { 5355 parent = val.getParent(); 5356 if (parent) { 5357 val = obj.display ? parent.getDisplay(val.getPropertyInParent()) : parent.get(val.getPropertyInParent()); 5358 return val; 5359 } 5360 } 5361 // As a last resort, just call toString 5362 if (val !== null) { 5363 return val.toString(); 5364 } 5365 return val; 5366 } 5367 }); 5368 5369 5370 /** 5371 * Format the specified object using the format pattern. 5372 * <p> 5373 * This method can take an Object Literal or a single pattern String 5374 * argument. 5375 * 5376 * @param {Object} obj the Object Literal. 5377 * @param {String} obj.pattern the format pattern to process. 5378 * @param {Boolean} [obj.display] if true, the display string of a Property value is used. 5379 * If false, the toString version of a Property value is used. 5380 * By default, this value is true (in BajaScript, most of the time 5381 * we're dealing with mounted Components in a Proxy Component Space). 5382 * 5383 * @returns {String} 5384 */ 5385 baja.Format.format = function (obj) { 5386 5387 // TODO: Currently format processing doesn't work in exactly the same way as Niagara. 5388 // Certainly it can never be 100% accurate. However, we can try to cover most common use cases 5389 // that we want to support. 5390 5391 obj = objectify(obj, "pattern"); 5392 var pattern = obj.pattern; 5393 obj.display = bajaDef(obj.display, true); 5394 5395 // Format replace 5396 var result = pattern.replace(/%[^%]*%/g, function (match) { 5397 // Remove '%' at start and end 5398 var content = match.substring(1, match.length - 1), i; 5399 5400 // Match up with a format handler and process it 5401 for (i = 0; i < formatMatches.length; ++i) { 5402 if (formatMatches[i].isMatch(content)) { 5403 var res = formatMatches[i].replace(content, obj); 5404 return res === null ? formatErr + content : res; 5405 } 5406 } 5407 return formatErr + content; 5408 }); 5409 5410 return result; 5411 }; 5412 }()); 5413 5414 /** 5415 * Format the specified object using the format pattern. 5416 * 5417 * @returns {String} 5418 */ 5419 baja.Format.prototype.format = function (obj) { 5420 obj = objectify(obj); 5421 obj.pattern = this.$pattern; 5422 return baja.Format.format(obj); 5423 }; 5424 5425 //////////////////////////////////////////////////////////////// 5426 // Permissions 5427 //////////////////////////////////////////////////////////////// 5428 5429 /** 5430 * @class Permissions for a given security domain. 5431 * <p> 5432 * This Constructor shouldn't be invoked directly. Please use the 'make' methods to create 5433 * an instance of a Permissions Object. 5434 * 5435 * @name baja.Permissions 5436 * @extends baja.Simple 5437 */ 5438 baja.Permissions = function (mask) { 5439 baja.Permissions.$super.apply(this, arguments); 5440 this.$mask = mask; 5441 }.$extend(baja.Simple); 5442 5443 var OPERATOR_READ = baja.Permissions.OPERATOR_READ = 0x0001, 5444 OPERATOR_WRITE = baja.Permissions.OPERATOR_WRITE = 0x0002, 5445 OPERATOR_INVOKE = baja.Permissions.OPERATOR_INVOKE = 0x0004, 5446 ADMIN_READ = baja.Permissions.ADMIN_READ = 0x0010, 5447 ADMIN_WRITE = baja.Permissions.ADMIN_WRITE = 0x0020, 5448 ADMIN_INVOKE = baja.Permissions.ADMIN_INVOKE = 0x0040, 5449 permissionsAll = OPERATOR_READ | OPERATOR_WRITE | OPERATOR_INVOKE | ADMIN_READ | ADMIN_WRITE | ADMIN_INVOKE, 5450 permissionsCache = {}; 5451 5452 permissionsCache[0] = baja.Permissions.none = baja.Permissions.DEFAULT = new baja.Permissions(0); 5453 permissionsCache[permissionsAll] = baja.Permissions.all = new baja.Permissions(permissionsAll); 5454 5455 /** 5456 * Make a permissions object. 5457 * 5458 * @params {String|Number} perm the permissions to decode. 5459 * @returns {baja.Permissions} 5460 */ 5461 baja.Permissions.prototype.make = function (perm) { 5462 var mask = 0, 5463 i, 5464 p; 5465 5466 if (typeof perm === "string") { 5467 for (i = 0; i < perm.length; ++i) { 5468 switch(perm.charAt(i)) { 5469 case "i": mask |= OPERATOR_INVOKE; break; 5470 case "r": mask |= OPERATOR_READ; break; 5471 case "w": mask |= OPERATOR_WRITE; break; 5472 case "I": mask |= ADMIN_INVOKE; break; 5473 case "R": mask |= ADMIN_READ; break; 5474 case "W": mask |= ADMIN_WRITE; break; 5475 } 5476 } 5477 } 5478 else { 5479 mask = perm; 5480 } 5481 5482 // Get permissions from cache 5483 if (permissionsCache[mask]) { 5484 p = permissionsCache[mask]; 5485 } 5486 else { 5487 p = permissionsCache[mask] = new baja.Permissions(mask); 5488 } 5489 return p; 5490 }; 5491 5492 /** 5493 * Make a permissions object. 5494 * 5495 * @params {String|Number} perm the permissions to decode. 5496 * @returns {baja.Permissions} 5497 */ 5498 baja.Permissions.make = function (perm) { 5499 return baja.Permissions.DEFAULT.make.apply(baja.Permissions, arguments); 5500 }; 5501 5502 /** 5503 * Decode Permissions from a String. 5504 * 5505 * @name baja.Permissions#decodeFromString 5506 * @function 5507 * 5508 * @returns {baja.Permissions} 5509 */ 5510 baja.Permissions.prototype.decodeFromString = cacheDecode(function (s) { 5511 return this.make(s); 5512 }); 5513 5514 /** 5515 * Encode Permissions to a String. 5516 * 5517 * @name baja.Permissions#encodeToString 5518 * @function 5519 * 5520 * @returns {String} 5521 */ 5522 baja.Permissions.prototype.encodeToString = cacheEncode(function () { 5523 var s = ""; 5524 if (this.hasOperatorInvoke()) { 5525 s += "i"; 5526 } 5527 if (this.hasOperatorRead()) { 5528 s += "r"; 5529 } 5530 if (this.hasOperatorWrite()) { 5531 s += "w"; 5532 } 5533 if (this.hasAdminInvoke()) { 5534 s += "I"; 5535 } 5536 if (this.hasAdminRead()) { 5537 s += "R"; 5538 } 5539 if (this.hasAdminWrite()) { 5540 s += "W"; 5541 } 5542 return s; 5543 }); 5544 5545 /** 5546 * Return a String representation of the Permissions. 5547 * 5548 * @returns {String} 5549 */ 5550 baja.Permissions.prototype.toString = function () { 5551 return this.encodeToString(); 5552 }; 5553 5554 // Register Type 5555 baja.Permissions.registerType("baja:Permissions"); 5556 5557 /** 5558 * Is the operator read permission enabled? 5559 * 5560 * @returns {Boolean} 5561 */ 5562 baja.Permissions.prototype.hasOperatorRead = function () { 5563 return (this.$mask & OPERATOR_READ) !== 0; 5564 }; 5565 5566 /** 5567 * Is the operator write permission enabled? 5568 * 5569 * @returns {Boolean} 5570 */ 5571 baja.Permissions.prototype.hasOperatorWrite = function () { 5572 return (this.$mask & OPERATOR_WRITE) !== 0; 5573 }; 5574 5575 /** 5576 * Is the operator invoke permission enabled? 5577 * 5578 * @returns {Boolean} 5579 */ 5580 baja.Permissions.prototype.hasOperatorInvoke = function () { 5581 return (this.$mask & OPERATOR_INVOKE) !== 0; 5582 }; 5583 5584 /** 5585 * Is the admin read permission enabled? 5586 * 5587 * @returns {Boolean} 5588 */ 5589 baja.Permissions.prototype.hasAdminRead = function () { 5590 return (this.$mask & ADMIN_READ) !== 0; 5591 }; 5592 5593 /** 5594 * Is the admin write permission enabled? 5595 * 5596 * @returns {Boolean} 5597 */ 5598 baja.Permissions.prototype.hasAdminWrite = function () { 5599 return (this.$mask & ADMIN_WRITE) !== 0; 5600 }; 5601 5602 /** 5603 * Is the admin invoke permission enabled? 5604 * 5605 * @returns {Boolean} 5606 */ 5607 baja.Permissions.prototype.hasAdminInvoke = function () { 5608 return (this.$mask & ADMIN_INVOKE) !== 0; 5609 }; 5610 5611 /** 5612 * Return true if the specified permissions are enabled. 5613 * 5614 * @params {Number|baja.Permissions} 5615 * @returns {Boolean} 5616 */ 5617 baja.Permissions.prototype.has = function (mask) { 5618 if (mask && mask instanceof baja.Permissions) { 5619 mask = mask.getMask(); 5620 } 5621 return (this.$mask & mask) === mask; 5622 }; 5623 5624 /** 5625 * Return the mask for the permissions. 5626 * 5627 * @returns {Boolean} 5628 */ 5629 baja.Permissions.prototype.getMask = function () { 5630 return this.$mask; 5631 }; 5632 5633 }(baja));