1 /** 2 * Cisco Finesse - JavaScript Library 3 * Version 10.5(1) 4 * Cisco Systems, Inc. 5 * http://www.cisco.com/ 6 * 7 * Portions created or assigned to Cisco Systems, Inc. are 8 * Copyright (c) 2014 Cisco Systems, Inc. or its affiliated entities. All Rights Reserved. 9 */ 10 /** 11 * This javascript library is made available to Cisco partners and customers as 12 * a convenience to help minimize the cost of Cisco Finesse customizations. 13 * This library can be used in Cisco Finesse deployments. Cisco does not 14 * permit the use of this library in customer deployments that do not include 15 * Cisco Finesse. Support for the javascript library is provided on a 16 * "best effort" basis via CDN. Like any custom deployment, it is the 17 * responsibility of the partner and/or customer to ensure that the 18 * customization works correctly and this includes ensuring that the Cisco 19 * Finesse JavaScript is properly integrated into 3rd party applications. 20 * Cisco reserves the right to make changes to the javascript code and 21 * corresponding API as part of the normal Cisco Finesse release cycle. The 22 * implication of this is that new versions of the javascript might be 23 * incompatible with applications built on older Finesse integrations. That 24 * said, it is Cisco's intention to ensure javascript compatibility across 25 * versions as much as possible and Cisco will make every effort to clearly 26 * document any differences in the javascript across versions in the event 27 * that a backwards compatibility impacting change is made. 28 */ 29 30 /* Simple JavaScript Inheritance 31 * By John Resig http://ejohn.org/ 32 * MIT Licensed. 33 */ 34 // Inspired by base2 and Prototype 35 (function (factory) { 36 37 38 // Define as an AMD module if possible 39 if ( typeof define === 'function' && define.amd ) 40 { 41 define('../thirdparty/Class',[], factory ); 42 } 43 44 /* Define using browser globals otherwise 45 * Prevent multiple instantiations if the script is loaded twice 46 */ 47 else 48 { 49 factory(); 50 } 51 }(function () { 52 (function(){ 53 var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; 54 // The base Class implementation (does nothing) 55 /** @private */ 56 this.Class = function(){}; 57 58 // Create a new Class that inherits from this class 59 /** @private */ 60 Class.extend = function(prop) { 61 var _super = this.prototype; 62 63 // Instantiate a base class (but only create the instance, 64 // don't run the init constructor) 65 initializing = true; 66 var prototype = new this(); 67 initializing = false; 68 69 // Copy the properties over onto the new prototype 70 for (var name in prop) { 71 // Check if we're overwriting an existing function 72 prototype[name] = typeof prop[name] == "function" && 73 typeof _super[name] == "function" && fnTest.test(prop[name]) ? 74 (function(name, fn){ 75 return function() { 76 var tmp = this._super; 77 78 // Add a new ._super() method that is the same method 79 // but on the super-class 80 this._super = _super[name]; 81 82 // The method only need to be bound temporarily, so we 83 // remove it when we're done executing 84 var ret = fn.apply(this, arguments); 85 this._super = tmp; 86 87 return ret; 88 }; 89 })(name, prop[name]) : 90 prop[name]; 91 } 92 93 // The dummy class constructor 94 /** @private */ 95 function Class() { 96 // All construction is actually done in the init method 97 if ( !initializing && this.init ) 98 this.init.apply(this, arguments); 99 } 100 101 // Populate our constructed prototype object 102 Class.prototype = prototype; 103 104 // Enforce the constructor to be what we expect 105 Class.prototype.constructor = Class; 106 107 // And make this class extendable 108 Class.extend = arguments.callee; 109 110 return Class; 111 }; 112 })(); 113 })); 114 115 /** 116 * JavaScript base object that all finesse objects should inherit 117 * from because it encapsulates and provides the common functionality. 118 * 119 * Note: This javascript class requires the "inhert.js" to be included 120 * (which simplifies the class inheritance). 121 * 122 * 123 * @requires finesse.utilities.Logger 124 */ 125 126 /** The following comment is to prevent jslint errors about 127 * using variables before they are defined. 128 */ 129 /*global Class */ 130 (function (factory) { 131 132 133 // Define as an AMD module if possible 134 if ( typeof define === 'function' && define.amd ) 135 { 136 define('FinesseBase', ["../thirdparty/Class"], factory ); 137 } 138 /* Define using browser globals otherwise 139 * Prevent multiple instantiations if the script is loaded twice 140 */ 141 else 142 { 143 factory(Class); 144 } 145 }(function (Class) { 146 var FinesseBase = Class.extend({ 147 init: function () { 148 } 149 }); 150 151 window.finesse = window.finesse || {}; 152 window.finesse.FinesseBase = FinesseBase; 153 154 return FinesseBase; 155 })); 156 157 /** 158 * A collection of conversion utilities. 159 * Last modified 07-06-2011, Cisco Systems 160 * 161 */ 162 /** @private */ 163 (function (factory) { 164 165 166 // Define as an AMD module if possible 167 if ( typeof define === 'function' && define.amd ) 168 { 169 define('utilities/../../thirdparty/util/converter',[], factory); 170 } 171 /* Define using browser globals otherwise 172 * Prevent multiple instantiations if the script is loaded twice 173 */ 174 else 175 { 176 factory(); 177 } 178 }(function () { 179 window.finesse = window.finesse || {}; 180 181 /** 182 * @class 183 * Contains a collection of utility functions. 184 * @private 185 */ 186 window.finesse.Converter = (function () { 187 return { 188 /* This work is licensed under Creative Commons GNU LGPL License. 189 190 License: http://creativecommons.org/licenses/LGPL/2.1/ 191 Version: 0.9 192 Author: Stefan Goessner/2006 193 Web: http://goessner.net/ 194 195 2013-09-16 Modified to remove use of XmlNode.innerHTML in the innerXml function by Cisco Systems, Inc. 196 */ 197 xml2json: function (xml, tab) { 198 var X = { 199 toObj: function (xml) { 200 var o = {}; 201 if (xml.nodeType === 1) { 202 // element node .. 203 if (xml.attributes.length) 204 // element with attributes .. 205 for (var i = 0; i < xml.attributes.length; i++) 206 o["@" + xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue || "").toString(); 207 if (xml.firstChild) { 208 // element has child nodes .. 209 var textChild = 0, 210 cdataChild = 0, 211 hasElementChild = false; 212 for (var n = xml.firstChild; n; n = n.nextSibling) { 213 if (n.nodeType == 1) hasElementChild = true; 214 else if (n.nodeType == 3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++; 215 // non-whitespace text 216 else if (n.nodeType == 4) cdataChild++; 217 // cdata section node 218 } 219 if (hasElementChild) { 220 if (textChild < 2 && cdataChild < 2) { 221 // structured element with evtl. a single text or/and cdata node .. 222 X.removeWhite(xml); 223 for (var n = xml.firstChild; n; n = n.nextSibling) { 224 if (n.nodeType == 3) 225 // text node 226 o["#text"] = X.escape(n.nodeValue); 227 else if (n.nodeType == 4) 228 // cdata node 229 o["#cdata"] = X.escape(n.nodeValue); 230 else if (o[n.nodeName]) { 231 // multiple occurence of element .. 232 if (o[n.nodeName] instanceof Array) 233 o[n.nodeName][o[n.nodeName].length] = X.toObj(n); 234 else 235 o[n.nodeName] = [o[n.nodeName], X.toObj(n)]; 236 } 237 else 238 // first occurence of element.. 239 o[n.nodeName] = X.toObj(n); 240 } 241 } 242 else { 243 // mixed content 244 if (!xml.attributes.length) 245 o = X.escape(X.innerXml(xml)); 246 else 247 o["#text"] = X.escape(X.innerXml(xml)); 248 } 249 } 250 else if (textChild) { 251 // pure text 252 if (!xml.attributes.length) 253 o = X.escape(X.innerXml(xml)); 254 else 255 o["#text"] = X.escape(X.innerXml(xml)); 256 } 257 else if (cdataChild) { 258 // cdata 259 if (cdataChild > 1) 260 o = X.escape(X.innerXml(xml)); 261 else 262 for (var n = xml.firstChild; n; n = n.nextSibling) 263 o["#cdata"] = X.escape(n.nodeValue); 264 } 265 } 266 if (!xml.attributes.length && !xml.firstChild) o = null; 267 } 268 else if (xml.nodeType == 9) { 269 // document.node 270 o = X.toObj(xml.documentElement); 271 } 272 else 273 throw ("unhandled node type: " + xml.nodeType); 274 return o; 275 }, 276 toJson: function(o, name, ind) { 277 var json = name ? ("\"" + name + "\"") : ""; 278 if (o instanceof Array) { 279 for (var i = 0, n = o.length; i < n; i++) 280 o[i] = X.toJson(o[i], "", ind + "\t"); 281 json += (name ? ":[": "[") + (o.length > 1 ? ("\n" + ind + "\t" + o.join(",\n" + ind + "\t") + "\n" + ind) : o.join("")) + "]"; 282 } 283 else if (o == null) 284 json += (name && ":") + "null"; 285 else if (typeof(o) == "object") { 286 var arr = []; 287 for (var m in o) 288 arr[arr.length] = X.toJson(o[m], m, ind + "\t"); 289 json += (name ? ":{": "{") + (arr.length > 1 ? ("\n" + ind + "\t" + arr.join(",\n" + ind + "\t") + "\n" + ind) : arr.join("")) + "}"; 290 } 291 else if (typeof(o) == "string") 292 json += (name && ":") + "\"" + o.toString() + "\""; 293 else 294 json += (name && ":") + o.toString(); 295 return json; 296 }, 297 innerXml: function(node) { 298 var s = ""; 299 var asXml = function(n) { 300 var s = ""; 301 if (n.nodeType == 1) { 302 s += "<" + n.nodeName; 303 for (var i = 0; i < n.attributes.length; i++) 304 s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\""; 305 if (n.firstChild) { 306 s += ">"; 307 for (var c = n.firstChild; c; c = c.nextSibling) 308 s += asXml(c); 309 s += "</" + n.nodeName + ">"; 310 } 311 else 312 s += "/>"; 313 } 314 else if (n.nodeType == 3) 315 s += n.nodeValue; 316 else if (n.nodeType == 4) 317 s += "<![CDATA[" + n.nodeValue + "]]>"; 318 return s; 319 }; 320 for (var c = node.firstChild; c; c = c.nextSibling) 321 s += asXml(c); 322 return s; 323 }, 324 escape: function(txt) { 325 return txt.replace(/[\\]/g, "\\\\") 326 .replace(/[\"]/g, '\\"') 327 .replace(/[\n]/g, '\\n') 328 .replace(/[\r]/g, '\\r'); 329 }, 330 removeWhite: function(e) { 331 e.normalize(); 332 for (var n = e.firstChild; n;) { 333 if (n.nodeType == 3) { 334 // text node 335 if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) { 336 // pure whitespace text node 337 var nxt = n.nextSibling; 338 e.removeChild(n); 339 n = nxt; 340 } 341 else 342 n = n.nextSibling; 343 } 344 else if (n.nodeType == 1) { 345 // element node 346 X.removeWhite(n); 347 n = n.nextSibling; 348 } 349 else 350 // any other node 351 n = n.nextSibling; 352 } 353 return e; 354 } 355 }; 356 if (xml.nodeType == 9) 357 // document node 358 xml = xml.documentElement; 359 var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t"); 360 return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}"; 361 }, 362 363 /* This work is licensed under Creative Commons GNU LGPL License. 364 365 License: http://creativecommons.org/licenses/LGPL/2.1/ 366 Version: 0.9 367 Author: Stefan Goessner/2006 368 Web: http://goessner.net/ 369 */ 370 json2xml: function(o, tab) { 371 var toXml = function(v, name, ind) { 372 var xml = ""; 373 if (v instanceof Array) { 374 for (var i = 0, n = v.length; i < n; i++) 375 xml += ind + toXml(v[i], name, ind + "\t") + "\n"; 376 } 377 else if (typeof(v) == "object") { 378 var hasChild = false; 379 xml += ind + "<" + name; 380 for (var m in v) { 381 if (m.charAt(0) == "@") 382 xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\""; 383 else 384 hasChild = true; 385 } 386 xml += hasChild ? ">": "/>"; 387 if (hasChild) { 388 for (var m in v) { 389 if (m == "#text") 390 xml += v[m]; 391 else if (m == "#cdata") 392 xml += "<![CDATA[" + v[m] + "]]>"; 393 else if (m.charAt(0) != "@") 394 xml += toXml(v[m], m, ind + "\t"); 395 } 396 xml += (xml.charAt(xml.length - 1) == "\n" ? ind: "") + "</" + name + ">"; 397 } 398 } 399 else { 400 xml += ind + "<" + name + ">" + v.toString() + "</" + name + ">"; 401 } 402 return xml; 403 }, 404 xml = ""; 405 for (var m in o) 406 xml += toXml(o[m], m, ""); 407 return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, ""); 408 } 409 }; 410 })(); 411 })); 412 413 /** 414 * SaxParser.js: provides a simple SAX parser 415 * 416 * NONVALIDATING - this will not validate whether you have valid XML or not. It will simply report what it finds. 417 * Only supports elements, attributes, and text. No comments, cdata, processing instructions, etc. 418 */ 419 420 /** 421 * @requires 422 * @ignore 423 */ 424 // Add SaxParser to the finesse.utilities namespace 425 var finesse = finesse || {}; 426 finesse.utilities = finesse.utilities || {}; 427 428 (function (factory) { 429 430 431 // Define as an AMD module if possible 432 if ( typeof define === 'function' && define.amd ) 433 { 434 define('utilities/SaxParser', [], factory ); 435 } 436 /* Define using browser globals otherwise 437 * Prevent multiple instantiations if the script is loaded twice 438 */ 439 else 440 { 441 finesse.utilities.SaxParser = factory(); 442 } 443 }(function () { 444 return { 445 parse: function(xml, callback) { 446 // Event callbacks 447 /** @private */ 448 var triggerEvent = function (type, data) { 449 callback.call(null, type, data); 450 }, 451 /** @private */ 452 triggerStartElement = function (name) { 453 triggerEvent("StartElement", name); 454 }, 455 /** @private */ 456 triggerEndElement = function (name) { 457 triggerEvent("EndElement", name); 458 }, 459 /** @private */ 460 triggerAttribute = function (name, value) { 461 triggerEvent("Attribute", { "name": name, "value": value }); 462 }, 463 /** @private */ 464 triggerText = function (text) { 465 triggerEvent("Text", text); 466 }, 467 468 // Parsing 469 cursor = 0, 470 xmlLength = xml.length, 471 whitespaceRegex = /^[ \t\r\n]*$/, 472 /** @private */ 473 isWhitespace = function (text) { 474 return whitespaceRegex.test(text); 475 }, 476 /** @private */ 477 moveToNonWhitespace = function () { 478 while (isWhitespace(xml.charAt(cursor))) { 479 cursor += 1; 480 } 481 }, 482 /** @private */ 483 parseAttribute = function () { 484 var nameBuffer = [], 485 valueBuffer = [], 486 valueIsQuoted = false, 487 cursorChar = ""; 488 489 nameBuffer.push(xml.charAt(cursor)); 490 491 // Get the name 492 cursor += 1; 493 while (cursor < xmlLength) { 494 cursorChar = xml.charAt(cursor); 495 if (isWhitespace(cursorChar) || cursorChar === "=") { 496 // Move on to gathering value 497 break; 498 } 499 else { 500 nameBuffer.push(cursorChar); 501 } 502 cursor += 1; 503 } 504 505 // Skip the equals sign and any whitespace 506 moveToNonWhitespace(); 507 if (cursorChar === "=") { 508 cursor += 1; 509 } else { 510 throw new Error("Did not find = following attribute name at " + cursor); 511 } 512 moveToNonWhitespace(); 513 514 // Get the value 515 valueIsQuoted = cursor !== xmlLength - 1 ? xml.charAt(cursor) === "\"": false; 516 if (valueIsQuoted) { 517 cursor += 1; 518 while (cursor < xmlLength) { 519 cursorChar = xml.charAt(cursor); 520 if (cursorChar === "\"") { 521 // Found the closing quote, so end value 522 triggerAttribute(nameBuffer.join(""), valueBuffer.join("")); 523 break; 524 } 525 else { 526 valueBuffer.push(cursorChar); 527 } 528 cursor += 1; 529 } 530 } 531 else { 532 throw new Error("Found unquoted attribute value at " + cursor); 533 } 534 }, 535 /** @private */ 536 parseEndElement = function () { 537 var elementNameBuffer = [], 538 cursorChar = ""; 539 cursor += 2; 540 while (cursor < xmlLength) { 541 cursorChar = xml.charAt(cursor); 542 if (cursorChar === ">") { 543 triggerEndElement(elementNameBuffer.join("")); 544 break; 545 } 546 else { 547 elementNameBuffer.push(cursorChar); 548 } 549 cursor += 1; 550 } 551 }, 552 /** @private */ 553 parseReference = function() { 554 var type, 555 TYPE_DEC_CHAR_REF = 1, 556 TYPE_HEX_CHAR_REF = 2, 557 TYPE_ENTITY_REF = 3, 558 buffer = ""; 559 cursor += 1; 560 // Determine the type of reference. 561 if (xml.charAt(cursor) === "#") { 562 cursor += 1; 563 if (xml.charAt(cursor) === "x") { 564 type = TYPE_HEX_CHAR_REF; 565 cursor += 1; 566 } else { 567 type = TYPE_DEC_CHAR_REF; 568 } 569 } else { 570 type = TYPE_ENTITY_REF; 571 } 572 // Read the reference into a buffer. 573 while (xml.charAt(cursor) !== ";") { 574 buffer += xml.charAt(cursor); 575 cursor += 1; 576 if (cursor >= xmlLength) { 577 throw new Error("Unterminated XML reference: " + buffer); 578 } 579 } 580 // Convert the reference to the appropriate character. 581 switch (type) { 582 case TYPE_DEC_CHAR_REF: 583 return String.fromCharCode(parseInt(buffer, 10)); 584 case TYPE_HEX_CHAR_REF: 585 return String.fromCharCode(parseInt(buffer, 16)); 586 case TYPE_ENTITY_REF: 587 switch (buffer) { 588 case "amp": 589 return "&"; 590 case "lt": 591 return "<"; 592 case "gt": 593 return ">"; 594 case "apos": 595 return "'"; 596 case "quot": 597 return "\""; 598 default: 599 throw new Error("Invalid XML entity reference: " + buffer); 600 } 601 // break; (currently unreachable) 602 } 603 }, 604 /** @private */ 605 parseElement = function () { 606 var elementNameBuffer = [], 607 textBuffer = [], 608 cursorChar = "", 609 whitespace = false; 610 611 // Get element name 612 cursor += 1; 613 while (cursor < xmlLength) { 614 cursorChar = xml.charAt(cursor); 615 whitespace = isWhitespace(cursorChar); 616 if (!whitespace && cursorChar !== "/" && cursorChar !== ">") { 617 elementNameBuffer.push(cursorChar); 618 } 619 else { 620 elementNameBuffer = elementNameBuffer.join(""); 621 triggerStartElement(elementNameBuffer); 622 break; 623 } 624 cursor += 1; 625 } 626 627 // Get attributes 628 if (whitespace) { 629 while (cursor < xmlLength) { 630 moveToNonWhitespace(); 631 cursorChar = xml.charAt(cursor); 632 if (cursorChar !== "/" && cursorChar !== ">") { 633 // Start of attribute 634 parseAttribute(); 635 } 636 cursorChar = xml.charAt(cursor); 637 if (cursorChar === "/" || cursorChar === ">") { 638 break; 639 } 640 else { 641 cursor += 1; 642 } 643 } 644 } 645 646 // End tag if "/>" was found, 647 // otherwise we're at the end of the start tag and have to parse into it 648 if (cursorChar === "/") { 649 if (cursor !== xmlLength - 1 && xml.charAt(cursor + 1) === ">") { 650 cursor += 1; 651 triggerEndElement(elementNameBuffer); 652 } 653 } 654 else { 655 // cursor is on ">", so parse into element content. Assume text until we find a "<", 656 // which could be a child element or the current element's end tag. We do not support 657 // mixed content of text and elements as siblings unless the text is only whitespace. 658 // Text cannot contain <, >, ", or &. They should be <, >, ", & respectively. 659 cursor += 1; 660 while (cursor < xmlLength) { 661 cursorChar = xml.charAt(cursor); 662 if (cursorChar === "<") { 663 // Determine if end tag or element 664 if (cursor !== xmlLength - 1 && xml.charAt(cursor + 1) === "/") { 665 // At end tag 666 textBuffer = textBuffer.join(""); 667 if (!isWhitespace(textBuffer)) { 668 triggerText(textBuffer); 669 } 670 parseEndElement(); 671 break; 672 } 673 else { 674 // At start tag 675 textBuffer = textBuffer.join(""); 676 if (!isWhitespace(textBuffer)) { 677 triggerText(textBuffer); 678 } 679 parseElement(); 680 textBuffer = []; 681 } 682 } else if (cursorChar === "&") { 683 textBuffer.push(parseReference()); 684 } 685 else { 686 textBuffer.push(cursorChar); 687 } 688 cursor += 1; 689 } 690 } 691 }, 692 /** @private */ 693 skipXmlDeclaration = function() { 694 if (xml.substr(0, 5) === "<?xml" && isWhitespace(xml.charAt(5))) { 695 cursor = xml.indexOf(">") + 1; 696 } 697 moveToNonWhitespace(); 698 }; 699 700 // Launch. 701 skipXmlDeclaration(); 702 parseElement(); 703 } 704 }; 705 })); 706 707 /** 708 * Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601> 709 * ?? 2011 Colin Snover <http://zetafleet.com> 710 * Released under MIT license. 711 */ 712 (function (factory) { 713 714 715 // Define as an AMD module if possible 716 if ( typeof define === 'function' && define.amd ) 717 { 718 define('iso8601', [], factory ); 719 } 720 /* Define using browser globals otherwise 721 * Prevent multiple instantiations if the script is loaded twice 722 */ 723 else 724 { 725 factory(); 726 } 727 }(function () { 728 (function (Date, undefined) { 729 var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ]; 730 /** @private **/ 731 Date.parse = function (date) { 732 var timestamp, struct, minutesOffset = 0; 733 734 // ES5 ??15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string 735 // before falling back to any implementation-specific date parsing, so that???s what we do, even if native 736 // implementations could be faster 737 // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ?? 10 tzHH 11 tzmm 738 if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) { 739 // avoid NaN timestamps caused by ???undefined??? values being passed to Date.UTC 740 for (var i = 0, k; (k = numericKeys[i]); ++i) { 741 struct[k] = +struct[k] || 0; 742 } 743 744 // allow undefined days and months 745 struct[2] = (+struct[2] || 1) - 1; 746 struct[3] = +struct[3] || 1; 747 748 if (struct[8] !== 'Z' && struct[9] !== undefined) { 749 minutesOffset = struct[10] * 60 + struct[11]; 750 751 if (struct[9] === '+') { 752 minutesOffset = 0 - minutesOffset; 753 } 754 } 755 756 timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]); 757 } 758 else { 759 timestamp = origParse ? origParse(date) : NaN; 760 } 761 762 return timestamp; 763 }; 764 }(Date)); 765 })); 766 767 /*! 768 Math.uuid.js (v1.4) 769 http://www.broofa.com 770 mailto:robert@broofa.com 771 772 Copyright (c) 2010 Robert Kieffer 773 Dual licensed under the MIT and GPL licenses. 774 */ 775 776 /* 777 * Generate a random uuid. 778 * 779 * USAGE: Math.uuid(length, radix) 780 * length - the desired number of characters 781 * radix - the number of allowable values for each character. 782 * 783 * EXAMPLES: 784 * // No arguments - returns RFC4122, version 4 ID 785 * >>> Math.uuid() 786 * "92329D39-6F5C-4520-ABFC-AAB64544E172" 787 * 788 * // One argument - returns ID of the specified length 789 * >>> Math.uuid(15) // 15 character ID (default base=62) 790 * "VcydxgltxrVZSTV" 791 * 792 * // Two arguments - returns ID of the specified length, and radix. (Radix must be <= 62) 793 * >>> Math.uuid(8, 2) // 8 character ID (base=2) 794 * "01001010" 795 * >>> Math.uuid(8, 10) // 8 character ID (base=10) 796 * "47473046" 797 * >>> Math.uuid(8, 16) // 8 character ID (base=16) 798 * "098F4D35" 799 */ 800 (function (factory) { 801 802 803 // Define as an AMD module if possible 804 if ( typeof define === 'function' && define.amd ) 805 { 806 define('Math.uuid', [], factory ); 807 } 808 /* Define using browser globals otherwise 809 * Prevent multiple instantiations if the script is loaded twice 810 */ 811 else 812 { 813 factory(); 814 } 815 }(function () { 816 (function() { 817 // Private array of chars to use 818 var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); 819 820 /** @private **/ 821 Math.uuid = function (len, radix) { 822 var chars = CHARS, uuid = [], i; 823 radix = radix || chars.length; 824 825 if (len) { 826 // Compact form 827 for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix]; 828 } else { 829 // rfc4122, version 4 form 830 var r; 831 832 // rfc4122 requires these characters 833 uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; 834 uuid[14] = '4'; 835 836 // Fill in random data. At i==19 set the high bits of clock sequence as 837 // per rfc4122, sec. 4.1.5 838 for (i = 0; i < 36; i++) { 839 if (!uuid[i]) { 840 r = 0 | Math.random()*16; 841 uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; 842 } 843 } 844 } 845 846 return uuid.join(''); 847 }; 848 849 // A more performant, but slightly bulkier, RFC4122v4 solution. We boost performance 850 // by minimizing calls to random() 851 /** @private **/ 852 Math.uuidFast = function() { 853 var chars = CHARS, uuid = new Array(36), rnd=0, r; 854 for (var i = 0; i < 36; i++) { 855 if (i==8 || i==13 || i==18 || i==23) { 856 uuid[i] = '-'; 857 } else if (i==14) { 858 uuid[i] = '4'; 859 } else { 860 if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0; 861 r = rnd & 0xf; 862 rnd = rnd >> 4; 863 uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; 864 } 865 } 866 return uuid.join(''); 867 }; 868 869 // A more compact, but less performant, RFC4122v4 solution: 870 /** @private **/ 871 Math.uuidCompact = function() { 872 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 873 var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 874 return v.toString(16); 875 }); 876 }; 877 })(); 878 })); 879 880 /** 881 * The following comment prevents JSLint errors concerning undefined global variables. 882 * It tells JSLint that these identifiers are defined elsewhere. 883 */ 884 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true, plusplus: true, unparam: true, forin: true */ 885 886 /** The following comment is to prevent jslint errors about 887 * using variables before they are defined. 888 */ 889 /*global $, _prefs,_uiMsg,ciscowidgets,dojo,finesse,gadgets,hostUrl, Handlebars */ 890 891 /** 892 * A collection of utility functions. 893 * 894 * @requires finesse.Converter 895 */ 896 var finesse = finesse || {}; 897 /** @namespace Useful javascript utility methods. */ 898 finesse.utilities = finesse.utilities || {}; 899 900 (function (factory) { 901 902 903 // Define as an AMD module if possible 904 if ( typeof define === 'function' && define.amd ) 905 { 906 define('utilities/Utilities', ["../../thirdparty/util/converter", 907 "utilities/SaxParser", 908 "iso8601", 909 "Math.uuid"], factory ); 910 } 911 /* Define using browser globals otherwise 912 * Prevent multiple instantiations if the script is loaded twice 913 */ 914 else 915 { 916 finesse.utilities.Utilities = factory(finesse.Converter, finesse.utilities.SaxParser); 917 } 918 }(function (Converter, SaxParser) { 919 var Utilities = /** @lends finesse.utilities.Utilities */ { 920 921 /** 922 * @class 923 * A PhoneBook is a list of Contacts available to a User for quick dial. 924 * 925 * @augments finesse.restservices.RestBase 926 * @see finesse.restservices.Contacts 927 * @constructs 928 */ 929 _fakeConstuctor: function () { 930 /* This is here for jsdocs. */ 931 }, 932 933 /** 934 * @private 935 * Retrieves the specified item from window.localStorage 936 * @param {String} key 937 * The key of the item to retrieve 938 * @returns {String} 939 * The string with the value of the retrieved item; returns 940 * what the browser would return if not found (typically null or undefined) 941 * Returns false if window.localStorage feature is not even found. 942 */ 943 getDOMStoreItem: function (key) { 944 var store = window.localStorage; 945 if (store) { 946 return store.getItem(key); 947 } 948 }, 949 950 /** 951 * @private 952 * Sets an item into window.localStorage 953 * @param {String} key 954 * The key for the item to set 955 * @param {String} value 956 * The value to set 957 * @returns {Boolean} 958 * True if successful, false if window.localStorage is 959 * not even found. 960 */ 961 setDOMStoreItem: function (key, value) { 962 var store = window.localStorage; 963 if (store) { 964 store.setItem(key, value); 965 return true; 966 } 967 return false; 968 }, 969 970 /** 971 * @private 972 * Removes a particular item from window.localStorage 973 * @param {String} key 974 * The key of the item to remove 975 * @returns {Boolean} 976 * True if successful, false if not 977 * Returns false if window.localStorage feature is not even found. 978 */ 979 removeDOMStoreItem: function (key) { 980 var store = window.localStorage; 981 if (store) { 982 store.removeItem(key); 983 return true; 984 } 985 return false; 986 }, 987 988 /** 989 * @private 990 * Dumps all the contents of window.localStorage 991 * @returns {Boolean} 992 * True if successful, false if not. 993 * Returns false if window.localStorage feature is not even found. 994 */ 995 clearDOMStore: function () { 996 var store = window.localStorage; 997 if (store) { 998 store.clear(); 999 return true; 1000 } 1001 return false; 1002 }, 1003 1004 /** 1005 * @private 1006 * Creates a message listener for window.postMessage messages. 1007 * @param {Function} callback 1008 * The callback that will be invoked with the message. The callback 1009 * is responsible for any security checks. 1010 * @param {String} [origin] 1011 * The origin to check against for security. Allows all messages 1012 * if no origin is provided. 1013 * @returns {Function} 1014 * The callback function used to register with the message listener. 1015 * This is different than the one provided as a parameter because 1016 * the function is overloaded with origin checks. 1017 * @throws {Error} If the callback provided is not a function. 1018 */ 1019 receiveMessage: function (callback, origin) { 1020 if (typeof callback !== "function") { 1021 throw new Error("Callback is not a function."); 1022 } 1023 1024 //Create a function closure to perform origin check. 1025 /** @private */ 1026 var cb = function (e) { 1027 // If an origin check is requested (provided), we'll only invoke the callback if it passes 1028 if (typeof origin !== "string" || (typeof origin === "string" && typeof e.origin === "string" && e.origin.toLowerCase() === origin.toLowerCase())) { 1029 callback(e); 1030 } 1031 }; 1032 1033 if (window.addEventListener) { //Firefox, Opera, Chrome, Safari 1034 window.addEventListener("message", cb, false); 1035 } else { //Internet Explorer 1036 window.attachEvent("onmessage", cb); 1037 } 1038 1039 //Return callback used to register with listener so that invoker 1040 //could use it to remove. 1041 return cb; 1042 }, 1043 1044 /** 1045 * @private 1046 * Sends a message to a target frame using window.postMessage. 1047 * @param {Function} message 1048 * Message to be sent to target frame. 1049 * @param {Object} [target="parent"] 1050 * An object reference to the target frame. Default us the parent. 1051 * @param {String} [targetOrigin="*"] 1052 * The URL of the frame this frame is sending the message to. 1053 */ 1054 sendMessage: function (message, target, targetOrigin) { 1055 //Default to any target URL if none is specified. 1056 targetOrigin = targetOrigin || "*"; 1057 1058 //Default to parent target if none is specified. 1059 target = target || parent; 1060 1061 //Ensure postMessage is supported by browser before invoking. 1062 if (window.postMessage) { 1063 target.postMessage(message, targetOrigin); 1064 } 1065 }, 1066 1067 /** 1068 * Returns the passed in handler, if it is a function. 1069 * @param {Function} handler 1070 * The handler to validate 1071 * @returns {Function} 1072 * The provided handler if it is valid 1073 * @throws Error 1074 * If the handler provided is invalid 1075 */ 1076 validateHandler: function (handler) { 1077 if (handler === undefined || typeof handler === "function") { 1078 return handler; 1079 } else { 1080 throw new Error("handler must be a function"); 1081 } 1082 }, 1083 1084 /** 1085 * @private 1086 * Tries to get extract the AWS error code from a 1087 * finesse.clientservices.ClientServices parsed error response object. 1088 * @param {Object} rsp 1089 * The handler to validate 1090 * @returns {String} 1091 * The error code, HTTP status code, or undefined 1092 */ 1093 getErrCode: function (rsp) { 1094 try { // Best effort to get the error code 1095 return rsp.object.ApiErrors.ApiError.ErrorType; 1096 } catch (e) { // Second best effort to get the HTTP Status code 1097 if (rsp && rsp.status) { 1098 return "HTTP " + rsp.status; 1099 } 1100 } // Otherwise, don't return anything (undefined) 1101 }, 1102 1103 /** 1104 * @private 1105 * Tries to get extract the AWS error data from a 1106 * finesse.clientservices.ClientServices parsed error response object. 1107 * @param {Object} rsp 1108 * The handler to validate 1109 * @returns {String} 1110 * The error data, HTTP status code, or undefined 1111 */ 1112 getErrData: function (rsp) { 1113 try { // Best effort to get the error data 1114 return rsp.object.ApiErrors.ApiError.ErrorData; 1115 } catch (e) { // Second best effort to get the HTTP Status code 1116 if (rsp && rsp.status) { 1117 return "HTTP " + rsp.status; 1118 } 1119 } // Otherwise, don't return anything (undefined) 1120 }, 1121 1122 /** 1123 * @private 1124 * Tries to get extract the AWS overrideable boolean from a 1125 * finesse.clientservices.ClientServices parsed error response object. 1126 * @param {Object} rsp 1127 * The handler to validate 1128 * @returns {String} 1129 * The overrideable boolean, HTTP status code, or undefined 1130 */ 1131 getErrOverrideable: function (rsp) { 1132 try { // Best effort to get the override boolean 1133 return rsp.object.ApiErrors.ApiError.Overrideable; 1134 } catch (e) { // Second best effort to get the HTTP Status code 1135 if (rsp && rsp.status) { 1136 return "HTTP " + rsp.status; 1137 } 1138 } // Otherwise, don't return anything (undefined) 1139 }, 1140 1141 /** 1142 * Trims leading and trailing whitespace from a string. 1143 * @param {String} str 1144 * The string to trim 1145 * @returns {String} 1146 * The trimmed string 1147 */ 1148 trim: function (str) { 1149 return str.replace(/^\s*/, "").replace(/\s*$/, ""); 1150 }, 1151 1152 /** 1153 * Utility method for getting the current time in milliseconds. 1154 * @returns {String} 1155 * The current time in milliseconds 1156 */ 1157 currentTimeMillis : function () { 1158 return (new Date()).getTime(); 1159 }, 1160 1161 /** 1162 * Gets the current drift (between client and server) 1163 * 1164 * @returns {integer} which is the current drift (last calculated; 0 if we have not calculated yet) 1165 */ 1166 getCurrentDrift : function () { 1167 var drift; 1168 1169 //Get the current client drift from localStorage 1170 drift = window.sessionStorage.getItem("clientTimestampDrift"); 1171 if (drift) { 1172 drift = parseInt(drift, 10); 1173 if (isNaN(drift)) { 1174 drift = 0; 1175 } 1176 } 1177 return drift; 1178 }, 1179 1180 /** 1181 * Converts the specified clientTime to server time by adjusting by the current drift. 1182 * 1183 * @param clientTime is the time in milliseconds 1184 * @returns {int} serverTime in milliseconds 1185 */ 1186 convertToServerTimeMillis : function(clientTime) { 1187 var drift = this.getCurrentDrift(); 1188 return (clientTime + drift); 1189 }, 1190 1191 /** 1192 * Utility method for getting the current time, 1193 * adjusted by the calculated "drift" to closely 1194 * approximate the server time. This is used 1195 * when calculating durations based on a server 1196 * timestamp, which otherwise can produce unexpected 1197 * results if the times on client and server are 1198 * off. 1199 * 1200 * @returns {String} 1201 * The current server time in milliseconds 1202 */ 1203 currentServerTimeMillis : function () { 1204 var drift = this.getCurrentDrift(); 1205 return (new Date()).getTime() + drift; 1206 }, 1207 1208 /** 1209 * Given a specified timeInMs, this method will builds a string which displays minutes and seconds. 1210 * 1211 * @param timeInMs is the time in milliseconds 1212 * @returns {String} which corresponds to minutes and seconds (e.g. 11:23) 1213 */ 1214 buildTimeString : function (timeInMs) { 1215 var min, sec, timeStr = "00:00"; 1216 1217 if (timeInMs && timeInMs !== "-1") { 1218 // calculate minutes, and seconds 1219 min = this.pad(Math.floor(timeInMs / 60000)); 1220 sec = this.pad(Math.floor((timeInMs % 60000) / 1000)); 1221 1222 // construct MM:SS time string 1223 timeStr = min + ":" + sec; 1224 } 1225 return timeStr; 1226 }, 1227 1228 /** 1229 * Given a specified timeInMs, this method will builds a string which displays minutes and seconds (and optionally hours) 1230 * 1231 * @param timeInMs is the time in milliseconds 1232 * @returns {String} which corresponds to hours, minutes and seconds (e.g. 01:11:23 or 11:23) 1233 */ 1234 buildTimeStringWithOptionalHours: function (timeInMs) { 1235 var hour, min, sec, timeStr = "00:00", optionalHour = "", timeInSecs; 1236 1237 if (timeInMs && timeInMs !== "-1") { 1238 timeInSecs = timeInMs / 1000; 1239 1240 // calculate {hours}, minutes, and seconds 1241 hour = this.pad(Math.floor(timeInSecs / 3600)); 1242 min = this.pad(Math.floor((timeInSecs % 3600) / 60)); 1243 sec = this.pad(Math.floor((timeInSecs % 3600) % 60)); 1244 1245 //Optionally add the hour if we have hours 1246 if (hour > 0) { 1247 optionalHour = hour + ":"; 1248 } 1249 1250 // construct MM:SS time string (or optionally HH:MM:SS) 1251 timeStr = optionalHour + min + ":" + sec; 1252 } 1253 return timeStr; 1254 }, 1255 1256 1257 /** 1258 * Builds a string which specifies the amount of time user has been in this state (e.g. 11:23). 1259 * 1260 * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift) 1261 * @param stateStartTimeInMs is integer argument which specifies time call entered current state 1262 * @returns {String} which is the elapsed time (MINUTES:SECONDS) 1263 * 1264 */ 1265 buildElapsedTimeString : function (adjustedServerTimeInMs, stateStartTimeInMs) { 1266 var result, delta; 1267 1268 result = "--:--"; 1269 if (stateStartTimeInMs !== 0) { 1270 delta = adjustedServerTimeInMs - stateStartTimeInMs; 1271 1272 if (delta > 0) { 1273 result = this.buildTimeString(delta); 1274 } 1275 } 1276 return result; 1277 }, 1278 1279 /** 1280 * Builds a string which specifies the amount of time user has been in this state with optional hours (e.g. 01:11:23 or 11:23). 1281 * 1282 * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift) 1283 * @param startTimeInMs is integer argument which specifies the start time 1284 * @returns {String} which is the elapsed time (MINUTES:SECONDS) or (HOURS:MINUTES:SECONDS) 1285 * 1286 */ 1287 buildElapsedTimeStringWithOptionalHours : function (adjustedServerTimeInMs, stateStartTimeInMs) { 1288 var result, delta; 1289 1290 result = "--:--"; 1291 if (stateStartTimeInMs !== 0) { 1292 delta = adjustedServerTimeInMs - stateStartTimeInMs; 1293 1294 if (delta > 0) { 1295 result = this.buildTimeStringWithOptionalHours(delta); 1296 } 1297 } 1298 return result; 1299 }, 1300 1301 1302 /** 1303 * Builds a string which displays the total call time in minutes and seconds. 1304 * 1305 * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift) 1306 * @param callStartTimeInMs is integer argument which specifies time the call started 1307 * @returns {String} which is the elapsed time [MINUTES:SECONDS] 1308 */ 1309 buildTotalTimeString : function (adjustedServerTimeInMs, callStartTimeInMs) { 1310 return this.buildElapsedTimeString(adjustedServerTimeInMs, callStartTimeInMs); 1311 }, 1312 1313 /** 1314 * Builds a string which displays the hold time in minutes and seconds. 1315 * 1316 * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift) 1317 * @param holdStartTimeInMs is integer argument which specifies time the hold started 1318 * @returns {String} which is the elapsed time [MINUTES:SECONDS] 1319 */ 1320 buildHoldTimeString : function (adjustedServerTimeInMs, holdStartTimeInMs) { 1321 return this.buildElapsedTimeString(adjustedServerTimeInMs, holdStartTimeInMs); 1322 }, 1323 1324 /** 1325 * Builds a string which displays the elapsed time the call has been in wrap up. 1326 * 1327 * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift) 1328 * @param wrapupStartTimeInMs is integer argument which specifies time call entered wrapup state 1329 * @returns {String} which is the elapsed wrapup time 1330 * 1331 */ 1332 buildWrapupTimeString : function (adjustedServerTimeInMs, wrapupStartTimeInMs) { 1333 return this.buildElapsedTimeString(adjustedServerTimeInMs, wrapupStartTimeInMs); 1334 }, 1335 1336 /** 1337 * Extracts a time from the timeStr. Note: The timeStr could be empty. In this case, the time returned will be 0. 1338 * @param timeStr is a time string in ISO8601 format (note: could be empty) 1339 * @returns {long} is the time 1340 */ 1341 extractTime : function (timeStr) { 1342 var result = 0, theDate; 1343 if (timeStr === "") { 1344 result = 0; 1345 } else if (timeStr === null) { 1346 result = 0; 1347 } else { 1348 theDate = this.parseDateStringISO8601(timeStr); 1349 result = theDate.getTime(); 1350 } 1351 return result; 1352 }, 1353 1354 /** 1355 * @private 1356 * Generates an RFC1422v4-compliant UUID using pesudorandom numbers. 1357 * @returns {String} 1358 * An RFC1422v4-compliant UUID using pesudorandom numbers. 1359 **/ 1360 generateUUID: function () { 1361 return Math.uuidCompact(); 1362 }, 1363 1364 /** @private */ 1365 xml2json: Converter.xml2json, 1366 1367 1368 /** 1369 * @private 1370 * Utility method to get the JSON parser either from gadgets.json 1371 * or from window.JSON (which will be initialized by CUIC if 1372 * browser doesn't support 1373 */ 1374 getJSONParser: function() { 1375 var _container = window.gadgets || {}, 1376 parser = _container.json || window.JSON; 1377 return parser; 1378 }, 1379 1380 /** 1381 * @private 1382 * Utility method to convert a javascript object to XML. 1383 * @param {Object} object 1384 * The object to convert to XML. 1385 * @param {Boolean} escapeFlag 1386 * If escapeFlag evaluates to true: 1387 * - XML escaping is done on the element values. 1388 * - Attributes, #cdata, and #text is not supported. 1389 * - The XML is unformatted (no whitespace between elements). 1390 * If escapeFlag evaluates to false: 1391 * - Element values are written 'as is' (no escaping). 1392 * - Attributes, #cdata, and #text is supported. 1393 * - The XML is formatted. 1394 * @returns The XML string. 1395 */ 1396 json2xml: function (object, escapeFlag) { 1397 var xml; 1398 if (escapeFlag) { 1399 xml = this._json2xmlWithEscape(object); 1400 } 1401 else { 1402 xml = Converter.json2xml(object, '\t'); 1403 } 1404 return xml; 1405 }, 1406 1407 /** 1408 * @private 1409 * Utility method to convert XML string into javascript object. 1410 */ 1411 xml2JsObj : function (event) { 1412 var parser = this.getJSONParser(); 1413 return parser.parse(Converter.xml2json(jQuery.parseXML(event), "")); 1414 }, 1415 1416 /** 1417 * @private 1418 * Utility method to convert an XML string to a javascript object. 1419 * @desc This function calls to the SAX parser and responds to callbacks 1420 * received from the parser. Entity translation is not handled here. 1421 * @param {String} xml 1422 * The XML to parse. 1423 * @returns The javascript object. 1424 */ 1425 xml2js: function (xml) { 1426 var STATES = { 1427 INVALID: 0, 1428 NEW_NODE: 1, 1429 ATTRIBUTE_NODE: 2, 1430 TEXT_NODE: 3, 1431 END_NODE: 4 1432 }, 1433 state = STATES.INVALID, 1434 rootObj = {}, 1435 newObj, 1436 objStack = [rootObj], 1437 nodeName = "", 1438 1439 /** 1440 * @private 1441 * Adds a property to the current top JSO. 1442 * @desc This is also where we make considerations for arrays. 1443 * @param {String} name 1444 * The name of the property to add. 1445 * @param (String) value 1446 * The value of the property to add. 1447 */ 1448 addProperty = function (name, value) { 1449 var current = objStack[objStack.length - 1]; 1450 if(current.hasOwnProperty(name) && current[name] instanceof Array){ 1451 current[name].push(value); 1452 }else if(current.hasOwnProperty(name)){ 1453 current[name] = [current[name], value]; 1454 }else{ 1455 current[name] = value; 1456 } 1457 }, 1458 1459 /** 1460 * @private 1461 * The callback passed to the SAX parser which processes events from 1462 * the SAX parser in order to construct the resulting JSO. 1463 * @param (String) type 1464 * The type of event received. 1465 * @param (String) data 1466 * The data received from the SAX parser. The contents of this 1467 * parameter vary based on the type of event. 1468 */ 1469 xmlFound = function (type, data) { 1470 switch (type) { 1471 case "StartElement": 1472 // Because different node types have different expectations 1473 // of parenting, we don't push another JSO until we know 1474 // what content we're getting 1475 1476 // If we're already in the new node state, we're running 1477 // into a child node. There won't be any text here, so 1478 // create another JSO 1479 if(state === STATES.NEW_NODE){ 1480 newObj = {}; 1481 addProperty(nodeName, newObj); 1482 objStack.push(newObj); 1483 } 1484 state = STATES.NEW_NODE; 1485 nodeName = data; 1486 break; 1487 case "EndElement": 1488 // If we're in the new node state, we've found no content 1489 // set the tag property to null 1490 if(state === STATES.NEW_NODE){ 1491 addProperty(nodeName, null); 1492 }else if(state === STATES.END_NODE){ 1493 objStack.pop(); 1494 } 1495 state = STATES.END_NODE; 1496 break; 1497 case "Attribute": 1498 // If were in the new node state, no JSO has yet been created 1499 // for this node, create one 1500 if(state === STATES.NEW_NODE){ 1501 newObj = {}; 1502 addProperty(nodeName, newObj); 1503 objStack.push(newObj); 1504 } 1505 // Attributes are differentiated from child elements by a 1506 // preceding "@" in the property name 1507 addProperty("@" + data.name, data.value); 1508 state = STATES.ATTRIBUTE_NODE; 1509 break; 1510 case "Text": 1511 // In order to maintain backwards compatibility, when no 1512 // attributes are assigned to a tag, its text contents are 1513 // assigned directly to the tag property instead of a JSO. 1514 1515 // If we're in the attribute node state, then the JSO for 1516 // this tag was already created when the attribute was 1517 // assigned, differentiate this property from a child 1518 // element by naming it "#text" 1519 if(state === STATES.ATTRIBUTE_NODE){ 1520 addProperty("#text", data); 1521 }else{ 1522 addProperty(nodeName, data); 1523 } 1524 state = STATES.TEXT_NODE; 1525 break; 1526 } 1527 }; 1528 SaxParser.parse(xml, xmlFound); 1529 return rootObj; 1530 }, 1531 1532 /** 1533 * @private 1534 * Traverses a plain-old-javascript-object recursively and outputs its XML representation. 1535 * @param {Object} obj 1536 * The javascript object to be converted into XML. 1537 * @returns {String} The XML representation of the object. 1538 */ 1539 js2xml: function (obj) { 1540 var xml = "", i, elem; 1541 1542 if (obj !== null) { 1543 if (obj.constructor === Object) { 1544 for (elem in obj) { 1545 if (obj[elem] === null || typeof(obj[elem]) === 'undefined') { 1546 xml += '<' + elem + '/>'; 1547 } else if (obj[elem].constructor === Array) { 1548 for (i = 0; i < obj[elem].length; i++) { 1549 xml += '<' + elem + '>' + this.js2xml(obj[elem][i]) + '</' + elem + '>'; 1550 } 1551 } else if (elem[0] !== '@') { 1552 if (this.js2xmlObjIsEmpty(obj[elem])) { 1553 xml += '<' + elem + this.js2xmlAtt(obj[elem]) + '/>'; 1554 } else if (elem === "#text") { 1555 xml += obj[elem]; 1556 } else { 1557 xml += '<' + elem + this.js2xmlAtt(obj[elem]) + '>' + this.js2xml(obj[elem]) + '</' + elem + '>'; 1558 } 1559 } 1560 } 1561 } else { 1562 xml = obj; 1563 } 1564 } 1565 1566 return xml; 1567 }, 1568 1569 /** 1570 * @private 1571 * Utility method called exclusively by js2xml() to find xml attributes. 1572 * @desc Traverses children one layer deep of a javascript object to "look ahead" 1573 * for properties flagged as such (with '@'). 1574 * @param {Object} obj 1575 * The obj to traverse. 1576 * @returns {String} Any attributes formatted for xml, if any. 1577 */ 1578 js2xmlAtt: function (obj) { 1579 var elem; 1580 1581 if (obj !== null) { 1582 if (obj.constructor === Object) { 1583 for (elem in obj) { 1584 if (obj[elem] !== null && typeof(obj[elem]) !== "undefined" && obj[elem].constructor !== Array) { 1585 if (elem[0] === '@'){ 1586 return ' ' + elem.substring(1) + '="' + obj[elem] + '"'; 1587 } 1588 } 1589 } 1590 } 1591 } 1592 1593 return ''; 1594 }, 1595 1596 /** 1597 * @private 1598 * Utility method called exclusively by js2xml() to determine if 1599 * a node has any children, with special logic for ignoring attributes. 1600 * @desc Attempts to traverse the elements in the object while ignoring attributes. 1601 * @param {Object} obj 1602 * The obj to traverse. 1603 * @returns {Boolean} whether or not the JS object is "empty" 1604 */ 1605 js2xmlObjIsEmpty: function (obj) { 1606 var elem; 1607 1608 if (obj !== null) { 1609 if (obj.constructor === Object) { 1610 for (elem in obj) { 1611 if (obj[elem] !== null) { 1612 if (obj[elem].constructor === Array){ 1613 return false; 1614 } 1615 1616 if (elem[0] !== '@'){ 1617 return false; 1618 } 1619 } else { 1620 return false; 1621 } 1622 } 1623 } else { 1624 return false; 1625 } 1626 } 1627 1628 return true; 1629 }, 1630 1631 /** 1632 * Encodes the given string into base64. 1633 *<br> 1634 * <b>NOTE:</b> {input} is assumed to be UTF-8; only the first 1635 * 8 bits of each input element are significant. 1636 * 1637 * @param {String} input 1638 * The string to convert to base64. 1639 * @returns {String} 1640 * The converted string. 1641 */ 1642 b64Encode: function (input) { 1643 var output = "", idx, data, 1644 table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 1645 1646 for (idx = 0; idx < input.length; idx += 3) { 1647 data = input.charCodeAt(idx) << 16 | 1648 input.charCodeAt(idx + 1) << 8 | 1649 input.charCodeAt(idx + 2); 1650 1651 //assume the first 12 bits are valid 1652 output += table.charAt((data >>> 18) & 0x003f) + 1653 table.charAt((data >>> 12) & 0x003f); 1654 output += ((idx + 1) < input.length) ? 1655 table.charAt((data >>> 6) & 0x003f) : 1656 "="; 1657 output += ((idx + 2) < input.length) ? 1658 table.charAt(data & 0x003f) : 1659 "="; 1660 } 1661 1662 return output; 1663 }, 1664 1665 /** 1666 * Decodes the given base64 string. 1667 * <br> 1668 * <b>NOTE:</b> output is assumed to be UTF-8; only the first 1669 * 8 bits of each output element are significant. 1670 * 1671 * @param {String} input 1672 * The base64 encoded string 1673 * @returns {String} 1674 * Decoded string 1675 */ 1676 b64Decode: function (input) { 1677 var output = "", idx, h, data, 1678 table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 1679 1680 for (idx = 0; idx < input.length; idx += 4) { 1681 h = [ 1682 table.indexOf(input.charAt(idx)), 1683 table.indexOf(input.charAt(idx + 1)), 1684 table.indexOf(input.charAt(idx + 2)), 1685 table.indexOf(input.charAt(idx + 3)) 1686 ]; 1687 1688 data = (h[0] << 18) | (h[1] << 12) | (h[2] << 6) | h[3]; 1689 if (input.charAt(idx + 2) === '=') { 1690 data = String.fromCharCode( 1691 (data >>> 16) & 0x00ff 1692 ); 1693 } else if (input.charAt(idx + 3) === '=') { 1694 data = String.fromCharCode( 1695 (data >>> 16) & 0x00ff, 1696 (data >>> 8) & 0x00ff 1697 ); 1698 } else { 1699 data = String.fromCharCode( 1700 (data >>> 16) & 0x00ff, 1701 (data >>> 8) & 0x00ff, 1702 data & 0x00ff 1703 ); 1704 } 1705 output += data; 1706 } 1707 1708 return output; 1709 }, 1710 1711 /** 1712 * @private 1713 * Extracts the username and the password from the Base64 encoded string. 1714 * @params {String} 1715 * A base64 encoded string containing credentials that (when decoded) 1716 * are colon delimited. 1717 * @returns {Object} 1718 * An object with the following structure: 1719 * {id:string, password:string} 1720 */ 1721 getCredentials: function (authorization) { 1722 var credObj = {}, 1723 credStr = this.b64Decode(authorization), 1724 colonIndx = credStr.indexOf(":"); 1725 1726 //Check to ensure that string is colon delimited. 1727 if (colonIndx === -1) { 1728 throw new Error("String is not colon delimited."); 1729 } 1730 1731 //Extract ID and password. 1732 credObj.id = credStr.substring(0, colonIndx); 1733 credObj.password = credStr.substring(colonIndx + 1); 1734 return credObj; 1735 }, 1736 1737 /** 1738 * Takes a string and removes any spaces within the string. 1739 * @param {String} string 1740 * The string to remove spaces from 1741 * @returns {String} 1742 * The string without spaces 1743 */ 1744 removeSpaces: function (string) { 1745 return string.split(' ').join(''); 1746 }, 1747 1748 /** 1749 * Escapes spaces as encoded " " characters so they can 1750 * be safely rendered by jQuery.text(string) in all browsers. 1751 * 1752 * (Although IE behaves as expected, Firefox collapses spaces if this function is not used.) 1753 * 1754 * @param text 1755 * The string whose spaces should be escaped 1756 * 1757 * @returns 1758 * The string with spaces escaped 1759 */ 1760 escapeSpaces: function (string) { 1761 return string.replace(/\s/g, '\u00a0'); 1762 }, 1763 1764 /** 1765 * Adds a span styled to line break at word edges around the string passed in. 1766 * @param str String to be wrapped in word-breaking style. 1767 * @private 1768 */ 1769 addWordWrapping : function (str) { 1770 return '<span style="word-wrap: break-word;">' + str + '</span>'; 1771 }, 1772 1773 /** 1774 * Takes an Object and determines whether it is an Array or not. 1775 * @param {Object} obj 1776 * The Object in question 1777 * @returns {Boolean} 1778 * true if the object is an Array, else false. 1779 */ 1780 isArray: function (obj) { 1781 return obj.constructor.toString().indexOf("Array") !== -1; 1782 }, 1783 1784 /** 1785 * @private 1786 * Takes a data object and returns an array extracted 1787 * @param {Object} data 1788 * JSON payload 1789 * 1790 * @returns {array} 1791 * extracted array 1792 */ 1793 getArray: function (data) { 1794 if (this.isArray(data)) { 1795 //Return if already an array. 1796 return data; 1797 } else { 1798 //Create an array, iterate through object, and push to array. This 1799 //should only occur with one object, and therefore one obj in array. 1800 var arr = []; 1801 arr.push(data); 1802 return arr; 1803 } 1804 }, 1805 1806 /** 1807 * @private 1808 * Extracts the ID for an entity given the Finesse REST URI. The ID is 1809 * assumed to be the last element in the URI (after the last "/"). 1810 * @param {String} uri 1811 * The Finesse REST URI to extract the ID from. 1812 * @returns {String} 1813 * The ID extracted from the REST URI. 1814 */ 1815 getId: function (uri) { 1816 if (!uri) { 1817 return ""; 1818 } 1819 var strLoc = uri.lastIndexOf("/"); 1820 return uri.slice(strLoc + 1); 1821 }, 1822 1823 /** 1824 * Compares two objects for equality. 1825 * @param {Object} obj1 1826 * First of two objects to compare. 1827 * @param {Object} obj2 1828 * Second of two objects to compare. 1829 */ 1830 getEquals: function (objA, objB) { 1831 var key; 1832 1833 for (key in objA) { 1834 if (objA.hasOwnProperty(key)) { 1835 if (!objA[key]) { 1836 objA[key] = ""; 1837 } 1838 1839 if (typeof objB[key] === 'undefined') { 1840 return false; 1841 } 1842 if (typeof objB[key] === 'object') { 1843 if (!objB[key].equals(objA[key])) { 1844 return false; 1845 } 1846 } 1847 if (objB[key] !== objA[key]) { 1848 return false; 1849 } 1850 } 1851 } 1852 return true; 1853 }, 1854 1855 /** 1856 * Regular expressions used in translating HTML and XML entities 1857 */ 1858 ampRegEx : new RegExp('&', 'gi'), 1859 ampEntityRefRegEx : new RegExp('&', 'gi'), 1860 ltRegEx : new RegExp('<', 'gi'), 1861 ltEntityRefRegEx : new RegExp('<', 'gi'), 1862 gtRegEx : new RegExp('>', 'gi'), 1863 gtEntityRefRegEx : new RegExp('>', 'gi'), 1864 xmlSpecialCharRegEx: new RegExp('[&<>"\']', 'g'), 1865 entityRefRegEx: new RegExp('&[^;]+(?:;|$)', 'g'), 1866 1867 /** 1868 * Translates between special characters and HTML entities 1869 * 1870 * @param text 1871 * The text to translate 1872 * 1873 * @param makeEntityRefs 1874 * If true, encode special characters as HTML entities; if 1875 * false, decode HTML entities back to special characters 1876 * 1877 * @private 1878 */ 1879 translateHTMLEntities: function (text, makeEntityRefs) { 1880 if (typeof(text) !== "undefined" && text !== null && text !== "") { 1881 if (makeEntityRefs) { 1882 text = text.replace(this.ampRegEx, '&'); 1883 text = text.replace(this.ltRegEx, '<'); 1884 text = text.replace(this.gtRegEx, '>'); 1885 } else { 1886 text = text.replace(this.gtEntityRefRegEx, '>'); 1887 text = text.replace(this.ltEntityRefRegEx, '<'); 1888 text = text.replace(this.ampEntityRefRegEx, '&'); 1889 } 1890 } 1891 1892 return text; 1893 }, 1894 1895 /** 1896 * Translates between special characters and XML entities 1897 * 1898 * @param text 1899 * The text to translate 1900 * 1901 * @param makeEntityRefs 1902 * If true, encode special characters as XML entities; if 1903 * false, decode XML entities back to special characters 1904 * 1905 * @private 1906 */ 1907 translateXMLEntities: function (text, makeEntityRefs) { 1908 /** @private */ 1909 var escape = function (character) { 1910 switch (character) { 1911 case "&": 1912 return "&"; 1913 case "<": 1914 return "<"; 1915 case ">": 1916 return ">"; 1917 case "'": 1918 return "'"; 1919 case "\"": 1920 return """; 1921 default: 1922 return character; 1923 } 1924 }, 1925 /** @private */ 1926 unescape = function (entity) { 1927 switch (entity) { 1928 case "&": 1929 return "&"; 1930 case "<": 1931 return "<"; 1932 case ">": 1933 return ">"; 1934 case "'": 1935 return "'"; 1936 case """: 1937 return "\""; 1938 default: 1939 if (entity.charAt(1) === "#" && entity.charAt(entity.length - 1) === ";") { 1940 if (entity.charAt(2) === "x") { 1941 return String.fromCharCode(parseInt(entity.slice(3, -1), 16)); 1942 } else { 1943 return String.fromCharCode(parseInt(entity.slice(2, -1), 10)); 1944 } 1945 } else { 1946 throw new Error("Invalid XML entity: " + entity); 1947 } 1948 } 1949 }; 1950 1951 if (typeof(text) !== "undefined" && text !== null && text !== "") { 1952 if (makeEntityRefs) { 1953 text = text.replace(this.xmlSpecialCharRegEx, escape); 1954 } else { 1955 text = text.replace(this.entityRefRegEx, unescape); 1956 } 1957 } 1958 1959 return text; 1960 }, 1961 1962 /** 1963 * @private 1964 * Utility method to pad the number with a leading 0 for single digits 1965 * @param (Number) num 1966 * the number to pad 1967 */ 1968 pad : function (num) { 1969 if (num < 10) { 1970 return "0" + num; 1971 } 1972 1973 return String(num); 1974 }, 1975 1976 /** 1977 * Pad with zeros based on a padWidth. 1978 * 1979 * @param num 1980 * @param padWidth 1981 * @returns {String} with padded zeros (based on padWidth) 1982 */ 1983 padWithWidth : function (num, padWidth) { 1984 var value, index, result; 1985 1986 result = ""; 1987 for(index=padWidth;index>1;index--) 1988 { 1989 value = Math.pow(10, index-1); 1990 1991 if (num < value) { 1992 result = result + "0"; 1993 } 1994 } 1995 result = result + num; 1996 1997 return String(result); 1998 }, 1999 2000 /** 2001 * Converts a date to an ISO date string. 2002 * 2003 * @param aDate 2004 * @returns {String} in ISO date format 2005 * 2006 * Note: Some browsers don't support this method (e.g. IE8). 2007 */ 2008 convertDateToISODateString : function (aDate) { 2009 var result; 2010 2011 result = aDate.getUTCFullYear() + "-" + this.padWithWidth(aDate.getUTCMonth()+1, 2) + "-" + this.padWithWidth(aDate.getUTCDate(), 2) + "T" + this.padWithWidth(aDate.getUTCHours(), 2) + ":" + this.padWithWidth(aDate.getUTCMinutes(), 2) + ":" + this.padWithWidth(aDate.getUTCSeconds(), 2)+ "." + this.padWithWidth(aDate.getUTCMilliseconds(), 3) + "Z"; 2012 return result; 2013 }, 2014 2015 /** 2016 * Get the date in ISO date format. 2017 * 2018 * @param aDate is the date 2019 * @returns {String} date in ISO format 2020 * 2021 * Note: see convertDateToISODateString() above. 2022 */ 2023 dateToISOString : function (aDate) { 2024 var result; 2025 2026 try { 2027 result = aDate.toISOString(); 2028 } catch (e) { 2029 result = this.convertDateToISODateString(aDate); 2030 } 2031 return result; 2032 }, 2033 2034 /** 2035 * Parse string (which is formated as ISO8601 date) into Javascript Date object. 2036 * 2037 * @param s ISO8601 string 2038 * @return {Date} 2039 * Note: Some browsers don't support Date constructor which take ISO8601 date (e.g. IE 8). 2040 */ 2041 parseDateStringISO8601 : function (s) { 2042 var i, re = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:.(\d+))?(Z|[+\-]\d{2})(?::(\d{2}))?/, 2043 d = s.match(re); 2044 if( !d ) { 2045 return null; 2046 } 2047 for( i in d ) { 2048 d[i] = ~~d[i]; 2049 } 2050 return new Date(Date.UTC(d[1], d[2] - 1, d[3], d[4], d[5], d[6], d[7]) + (d[8] * 60 + d[9]) * 60000); 2051 }, 2052 2053 /** 2054 * Utility method to render a timestamp value (in seconds) into HH:MM:SS format. 2055 * @param {Number} time 2056 * The timestamp in ms to render 2057 * @returns {String} 2058 * Time string in HH:MM:SS format. 2059 */ 2060 getDisplayTime : function (time) { 2061 var hour, min, sec, timeStr = "00:00:00"; 2062 2063 if (time && time !== "-1") { 2064 // calculate hours, minutes, and seconds 2065 hour = this.pad(Math.floor(time / 3600)); 2066 min = this.pad(Math.floor((time % 3600) / 60)); 2067 sec = this.pad(Math.floor((time % 3600) % 60)); 2068 // construct HH:MM:SS time string 2069 timeStr = hour + ":" + min + ":" + sec; 2070 } 2071 2072 return timeStr; 2073 }, 2074 2075 /** 2076 * Checks if the string is null. If it is, return empty string; else return 2077 * the string itself. 2078 * 2079 * @param {String} str 2080 * The string to check 2081 * @return {String} 2082 * Empty string or string itself 2083 */ 2084 convertNullToEmptyString : function (str) { 2085 return str || ""; 2086 }, 2087 2088 /** 2089 * Utility method to render a timestamp string (of format 2090 * YYYY-MM-DDTHH:MM:SSZ) into a duration of HH:MM:SS format. 2091 * 2092 * @param {String} timestamp 2093 * The timestamp to render 2094 * @param {Date} [now] 2095 * Optional argument to provide the time from which to 2096 * calculate the duration instead of using the current time 2097 * @returns {String} 2098 * Duration string in HH:MM:SS format. 2099 */ 2100 convertTsToDuration : function (timestamp, now) { 2101 return this.convertTsToDurationWithFormat(timestamp, false, now); 2102 }, 2103 2104 /** 2105 * Utility method to render a timestamp string (of format 2106 * YYYY-MM-DDTHH:MM:SSZ) into a duration of HH:MM:SS format, 2107 * with optional -1 for null or negative times. 2108 * 2109 * @param {String} timestamp 2110 * The timestamp to render 2111 * @param {Boolean} forFormat 2112 * If True, if duration is null or negative, return -1 so that the duration can be formated 2113 * as needed in the Gadget. 2114 * @param {Date} [now] 2115 * Optional argument to provide the time from which to 2116 * calculate the duration instead of using the current time 2117 * @returns {String} 2118 * Duration string in HH:MM:SS format. 2119 */ 2120 convertTsToDurationWithFormat : function (timestamp, forFormat, now) { 2121 var startTimeInMs, nowInMs, durationInSec = "-1"; 2122 2123 // Calculate duration 2124 if (timestamp && typeof timestamp === "string") { 2125 // first check it '--' for a msg in grid 2126 if (timestamp === '--' || timestamp ==="" || timestamp === "-1") { 2127 return "-1"; 2128 } 2129 // else try convert string into a time 2130 startTimeInMs = Date.parse(timestamp); 2131 if (!isNaN(startTimeInMs)) { 2132 if (!now || !(now instanceof Date)) { 2133 nowInMs = this.currentServerTimeMillis(); 2134 } else { 2135 nowInMs = this.convertToServerTimeMillis(now.getTime()); 2136 } 2137 durationInSec = Math.floor((nowInMs - startTimeInMs) / 1000); 2138 // Since currentServerTime is not exact (lag between sending and receiving 2139 // messages will differ slightly), treat a slightly negative (less than 1 sec) 2140 // value as 0, to avoid "--" showing up when a state first changes. 2141 if (durationInSec === -1) { 2142 durationInSec = 0; 2143 } 2144 2145 if (durationInSec < 0) { 2146 if (forFormat) { 2147 return "-1"; 2148 } else { 2149 return this.getDisplayTime("-1"); 2150 } 2151 } 2152 } 2153 }else { 2154 if(forFormat){ 2155 return "-1"; 2156 } 2157 } 2158 return this.getDisplayTime(durationInSec); 2159 }, 2160 2161 /** 2162 * Takes a string (typically from window.location) and finds the value which corresponds to a name. For 2163 * example: http://www.company.com/?param1=value1¶m2=value2 2164 * 2165 * @param str is the string to search 2166 * @param name is the name to search for 2167 */ 2168 getParameterByName : function(str, name) { 2169 var regex, results; 2170 name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); 2171 regex = new RegExp("[\\?&]" + name + "=([^]*)"); 2172 results = regex.exec(str); 2173 return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); 2174 }, 2175 2176 /** 2177 * @private 2178 * Gets the user Authorization String from the window session. 2179 * @returns the Authorization String 2180 * 2181 */ 2182 getUserAuthString: function () { 2183 var authString = window.sessionStorage.getItem('userFinesseAuth'); 2184 return authString; 2185 }, 2186 2187 /** 2188 * @private 2189 * Adds a new cookie to the page with a default domain. 2190 * @param {String} key 2191 * the key to assign a value to 2192 * @param {String} value 2193 * the value to assign to the key 2194 * @param {Number} days 2195 * number of days (from current) until the cookie should expire 2196 */ 2197 addCookie : function (key, value, days) { 2198 var date, expires = "", 2199 cookie = key + "=" + escape(value); 2200 if (typeof days === "number") { 2201 date = new Date(); 2202 date.setTime(date.getTime() + (days * 24 * 3600 * 1000)); 2203 cookie += "; expires=" + date.toGMTString(); 2204 } 2205 document.cookie = cookie + "; path=/"; 2206 }, 2207 2208 /** 2209 * @private 2210 * Get the value of a cookie given a key. 2211 * @param {String} key 2212 * a key to lookup 2213 * @returns {String} 2214 * the value mapped to a key, null if key doesn't exist 2215 */ 2216 getCookie : function (key) { 2217 var i, pairs, pair; 2218 if (document.cookie) { 2219 pairs = document.cookie.split(";"); 2220 for (i = 0; i < pairs.length; i += 1) { 2221 pair = this.trim(pairs[i]).split("="); 2222 if (pair[0] === key) { 2223 return unescape(pair[1]); 2224 } 2225 } 2226 } 2227 return null; 2228 }, 2229 2230 /** 2231 * @private 2232 * Deletes the cookie mapped to specified key. 2233 * @param {String} key 2234 * the key to delete 2235 */ 2236 deleteCookie : function (key) { 2237 this.addCookie(key, "", -1); 2238 }, 2239 2240 /** 2241 * @private 2242 * Case insensitive sort for use with arrays or Dojox stores 2243 * @param {String} a 2244 * first value 2245 * @param {String} b 2246 * second value 2247 */ 2248 caseInsensitiveSort: function (a, b) { 2249 var ret = 0, emptyString = ""; 2250 a = a + emptyString; 2251 b = b + emptyString; 2252 a = a.toLowerCase(); 2253 b = b.toLowerCase(); 2254 if (a > b) { 2255 ret = 1; 2256 } 2257 if (a < b) { 2258 ret = -1; 2259 } 2260 return ret; 2261 }, 2262 2263 /** 2264 * @private 2265 * Calls the specified function to render the dojo wijit for a gadget when the gadget first becomes visible. 2266 * 2267 * The displayWjitFunc function will be called once and only once when the div for our wijit 2268 * becomes visible for the first time. This is necessary because some dojo wijits such as the grid 2269 * throw exceptions and do not render properly if they are created in a display:none div. 2270 * If our gadget is visisble the function will be called immediately. 2271 * If our gadget is not yet visisble, then it sets a timer and waits for it to become visible. 2272 * NOTE: The timer may seem inefficent, originally I tried connecting to the tab onclick handler, but 2273 * there is a problem with dojo.connnect to an iframe's parent node in Internet Explorer. 2274 * In Firefox the click handler works OK, but it happens before the node is actually visisble, so you 2275 * end up waiting for the node to become visisble anyway. 2276 * @displayWjitFunc: A function to be called once our gadget has become visisble for th first time. 2277 */ 2278 onGadgetFirstVisible: function (displayWjitFunc) { 2279 var i, q, frameId, gadgetNbr, gadgetTitleId, panelId, panelNode, link, iterval, once = false, active = false, tabId = "#finesse-tab-selector"; 2280 try { 2281 frameId = dojo.attr(window.frameElement, "id"); // Figure out what gadget number we are by looking at our frameset 2282 gadgetNbr = frameId.match(/\d+$/)[0]; // Strip the number off the end of the frame Id, that's our gadget number 2283 gadgetTitleId = "#finesse_gadget_" + gadgetNbr + "_title"; // Create a a gadget title id from the number 2284 2285 // Loop through all of the tab panels to find one that has our gadget id 2286 dojo.query('.tab-panel', window.parent.document).some(function (node, index, arr) { 2287 q = dojo.query(gadgetTitleId, node); // Look in this panel for our gadget id 2288 if (q.length > 0) { // You found it 2289 panelNode = node; 2290 panelId = dojo.attr(panelNode, "id"); // Get panel id e.g. panel_Workgroups 2291 active = dojo.hasClass(panelNode, "active"); 2292 tabId = "#tab_" + panelId.slice(6); // Turn it into a tab id e.g.tab_Workgroups 2293 return; 2294 } 2295 }); 2296 // If panel is already active - execute the function - we're done 2297 if (active) { 2298 //?console.log(frameId + " is visible display it"); 2299 setTimeout(displayWjitFunc); 2300 } 2301 // If its not visible - wait for the active class to show up. 2302 else { 2303 //?console.log(frameId + " (" + tabId + ") is NOT active wait for it"); 2304 iterval = setInterval(dojo.hitch(this, function () { 2305 if (dojo.hasClass(panelNode, "active")) { 2306 //?console.log(frameId + " (" + tabId + ") is visible display it"); 2307 clearInterval(iterval); 2308 setTimeout(displayWjitFunc); 2309 } 2310 }), 250); 2311 } 2312 } catch (err) { 2313 //?console.log("Could not figure out what tab " + frameId + " is in: " + err); 2314 } 2315 }, 2316 2317 /** 2318 * @private 2319 * Downloads the specified url using a hidden iframe. In order to cause the browser to download rather than render 2320 * in the hidden iframe, the server code must append the header "Content-Disposition" with a value of 2321 * "attachment; filename=\"<WhateverFileNameYouWant>\"". 2322 */ 2323 downloadFile : function (url) { 2324 var iframe = document.getElementById("download_iframe"); 2325 2326 if (!iframe) 2327 { 2328 iframe = document.createElement("iframe"); 2329 $(document.body).append(iframe); 2330 $(iframe).css("display", "none"); 2331 } 2332 2333 iframe.src = url; 2334 }, 2335 2336 /** 2337 * @private 2338 * bitMask has functions for testing whether bit flags specified by integers are set in the supplied value 2339 */ 2340 bitMask: { 2341 /** @private */ 2342 isSet: function (value, mask) { 2343 return (value & mask) === mask; 2344 }, 2345 /** 2346 * Returns true if all flags in the intArray are set on the specified value 2347 * @private 2348 */ 2349 all: function (value, intArray) { 2350 var i = intArray.length; 2351 if (typeof(i) === "undefined") 2352 { 2353 intArray = [intArray]; 2354 i = 1; 2355 } 2356 while ((i = i - 1) !== -1) 2357 { 2358 if (!this.isSet(value, intArray[i])) 2359 { 2360 return false; 2361 } 2362 } 2363 return true; 2364 }, 2365 /** 2366 * @private 2367 * Returns true if any flags in the intArray are set on the specified value 2368 */ 2369 any: function (value, intArray) { 2370 var i = intArray.length; 2371 if (typeof(i) === "undefined") 2372 { 2373 intArray = [intArray]; 2374 i = 1; 2375 } 2376 while ((i = i - 1) !== -1) 2377 { 2378 if (this.isSet(value, intArray[i])) 2379 { 2380 return true; 2381 } 2382 } 2383 return false; 2384 } 2385 }, 2386 2387 /** @private */ 2388 renderDojoGridOffScreen: function (grid) { 2389 var offscreenDiv = $("<div style='position: absolute; left: -5001px; width: 5000px;'></div>")[0]; 2390 $(document.body).append(offscreenDiv); 2391 grid.placeAt(offscreenDiv); 2392 grid.startup(); 2393 document.body.removeChild(offscreenDiv); 2394 return grid; 2395 }, 2396 2397 /** @private */ 2398 initializeSearchInput: function(searchInput, changeCallback, callbackDelay, callbackScope, placeholderText) { 2399 var timerId = null, 2400 theControl = typeof(searchInput) === "string" ? $("#" + searchInput) : $(searchInput), 2401 theInputControl = theControl.find("input"), 2402 theClearButton = theControl.find("a"), 2403 inputControlWidthWithClear = 204, 2404 inputControlWidthNoClear = 230, 2405 sPreviousInput = theInputControl.val(), 2406 /** @private **/ 2407 toggleClearButton = function(){ 2408 if (theInputControl.val() === "") { 2409 theClearButton.hide(); 2410 theControl.removeClass("input-append"); 2411 theInputControl.width(inputControlWidthNoClear); 2412 } else { 2413 theInputControl.width(inputControlWidthWithClear); 2414 theClearButton.show(); 2415 theControl.addClass("input-append"); 2416 } 2417 }; 2418 2419 // set placeholder text 2420 theInputControl.attr('placeholder', placeholderText); 2421 2422 theInputControl.unbind('keyup').bind('keyup', function() { 2423 if (sPreviousInput !== theInputControl.val()) { 2424 window.clearTimeout(timerId); 2425 sPreviousInput = theInputControl.val(); 2426 timerId = window.setTimeout(function() { 2427 changeCallback.call((callbackScope || window), theInputControl.val()); 2428 theInputControl[0].focus(); 2429 }, callbackDelay); 2430 } 2431 2432 toggleClearButton(); 2433 }); 2434 2435 theClearButton.bind('click', function() { 2436 theInputControl.val(''); 2437 changeCallback.call((callbackScope || window), ''); 2438 2439 toggleClearButton(); 2440 theInputControl[0].focus(); // jquery and dojo on the same page break jquery's focus() method 2441 }); 2442 2443 theInputControl.val(""); 2444 toggleClearButton(); 2445 }, 2446 2447 DataTables: { 2448 /** @private */ 2449 createDataTable: function (options, dataTableOptions) { 2450 var grid, 2451 table = $('<table cellpadding="0" cellspacing="0" border="0" class="finesse"><thead><tr></tr></thead></table>'), 2452 headerRow = table.find("tr"), 2453 defaultOptions = { 2454 "aaData": [], 2455 "bPaginate": false, 2456 "bLengthChange": false, 2457 "bFilter": false, 2458 "bInfo": false, 2459 "sScrollY": "176", 2460 "oLanguage": { 2461 "sEmptyTable": "", 2462 "sZeroRecords": "" 2463 } 2464 }, 2465 gridOptions = $.extend({}, defaultOptions, dataTableOptions), 2466 columnDefs = [], 2467 columnFormatter; 2468 2469 // Create a header cell for each column, and set up the datatable definition for the column 2470 $(options.columns).each(function (index, column) { 2471 headerRow.append($("<th></th>")); 2472 columnDefs[index] = { 2473 "mData": column.propertyName, 2474 "sTitle": column.columnHeader, 2475 "sWidth": column.width, 2476 "aTargets": [index], 2477 "bSortable": column.sortable, 2478 "bVisible": column.visible, 2479 "mRender": column.render 2480 }; 2481 if (typeof(column.renderFunction) === "function") 2482 { 2483 /** @ignore **/ 2484 columnDefs[index].mRender = /** @ignore **/ function (value, type, dataObject) { 2485 var returnValue; 2486 2487 //Apply column render logic to value before applying extra render function 2488 if (typeof(column.render) === "function") 2489 { 2490 value = column.render.call(value, value, value); 2491 } 2492 2493 if (typeof(type) === "string") 2494 { 2495 switch (type) 2496 { 2497 case "undefined": 2498 case "sort": 2499 returnValue = value; 2500 break; 2501 case "set": 2502 throw new Error("Unsupported set data in Finesse Grid"); 2503 case "filter": 2504 case "display": 2505 case "type": 2506 returnValue = column.renderFunction.call(dataObject, value, dataObject); 2507 break; 2508 default: 2509 break; 2510 } 2511 } 2512 else 2513 { 2514 throw new Error("type param not specified in Finesse DataTable mData"); 2515 } 2516 2517 return returnValue; 2518 }; 2519 } 2520 }); 2521 gridOptions.aoColumnDefs = columnDefs; 2522 2523 // Set the height 2524 if (typeof(options.bodyHeightPixels) !== "undefined" && options.bodyHeightPixels !== null) 2525 { 2526 gridOptions.sScrollY = options.bodyHeightPixels + "px"; 2527 } 2528 2529 // Place it into the DOM 2530 if (typeof(options.container) !== "undefined" && options.container !== null) 2531 { 2532 $(options.container).append(table); 2533 } 2534 2535 // Create the DataTable 2536 table.dataTable(gridOptions); 2537 2538 return table; 2539 } 2540 }, 2541 2542 /** 2543 * @private 2544 * Sets a dojo button to the specified disable state, removing it from 2545 * the tab order if disabling, and restoring it to the tab order if enabling. 2546 * @param {Object} dojoButton Reference to the dijit.form.Button object. This is not the DOM element. 2547 * @param {bool} disabled 2548 */ 2549 setDojoButtonDisabledAttribute: function (dojoButton, disabled) { 2550 var labelNode, 2551 tabIndex; 2552 2553 dojoButton.set("disabled", disabled); 2554 2555 // Remove the tabindex attribute on disabled buttons, store it, 2556 // and replace it when it becomes enabled again 2557 labelNode = $("#" + dojoButton.id + "_label"); 2558 if (disabled) 2559 { 2560 labelNode.data("finesse:dojoButton:tabIndex", labelNode.attr("tabindex")); 2561 labelNode.removeAttr("tabindex"); 2562 } 2563 else 2564 { 2565 tabIndex = labelNode.data("finesse:dojoButton:tabIndex"); 2566 if (typeof(tabIndex) === "string") 2567 { 2568 labelNode.attr("tabindex", Number(tabIndex)); 2569 } 2570 } 2571 }, 2572 2573 /** 2574 * @private 2575 * Measures the given text using the supplied fontFamily and fontSize 2576 * @param {string} text text to measure 2577 * @param {string} fontFamily 2578 * @param {string} fontSize 2579 * @return {number} pixel width 2580 */ 2581 measureText: function (text, fontFamily, fontSize) { 2582 var width, 2583 element = $("<div></div>").text(text).css({ 2584 "fontSize": fontSize, 2585 "fontFamily": fontFamily 2586 }).addClass("offscreen").appendTo(document.body); 2587 2588 width = element.width(); 2589 element.remove(); 2590 2591 return width; 2592 }, 2593 2594 /** 2595 * Adjusts the gadget height. Shindig's gadgets.window.adjustHeight fails when 2596 * needing to resize down in IE. This gets around that by calculating the height 2597 * manually and passing it in. 2598 * @return {undefined} 2599 */ 2600 "adjustGadgetHeight": function () { 2601 var bScrollHeight = $("body").height() + 20; 2602 gadgets.window.adjustHeight(bScrollHeight); 2603 }, 2604 2605 /** 2606 * Private helper method for converting a javascript object to xml, where the values of the elements are 2607 * appropriately escaped for XML. 2608 * This is a simple implementation that does not implement cdata or attributes. It is also 'unformatted' in that 2609 * there is no whitespace between elements. 2610 * @param object The javascript object to convert to XML. 2611 * @returns The XML string. 2612 * @private 2613 */ 2614 _json2xmlWithEscape: function(object) { 2615 var that = this, 2616 xml = "", 2617 m, 2618 /** @private **/ 2619 toXmlHelper = function(value, name) { 2620 var xml = "", 2621 i, 2622 m; 2623 if (value instanceof Array) { 2624 for (i = 0; i < value.length; ++i) { 2625 xml += toXmlHelper(value[i], name); 2626 } 2627 } 2628 else if (typeof value === "object") { 2629 xml += "<" + name + ">"; 2630 for (m in value) { 2631 if (value.hasOwnProperty(m)) { 2632 xml += toXmlHelper(value[m], m); 2633 } 2634 } 2635 xml += "</" + name + ">"; 2636 } 2637 else { 2638 // is a leaf node 2639 xml += "<" + name + ">" + that.translateHTMLEntities(value.toString(), true) + 2640 "</" + name + ">"; 2641 } 2642 return xml; 2643 }; 2644 for (m in object) { 2645 if (object.hasOwnProperty(m)) { 2646 xml += toXmlHelper(object[m], m); 2647 } 2648 } 2649 return xml; 2650 }, 2651 2652 /** 2653 * Utility to detect the browser version of IE, whether it is IE9 or IE8 or in compatibility mode. 2654 * If IE is earlier than IE8, version returns undefined. 2655 * 2656 * @return {Object} browser object has the following form: 2657 * { 2658 * isIE: {Boolean}, 2659 * version: {String}, // 8 for IE8, 9 or IE9, 11 or IE11 2660 * isCompatibilityMode: {Boolean} 2661 * } 2662 */ 2663 detectIEBrowserVersion: function () { 2664 var browser = { 2665 isIE: false, 2666 isCompatibilityMode: false 2667 }, 2668 2669 useragent = navigator.userAgent, 2670 2671 appName = navigator.appName; 2672 2673 // Check to see if app is IE 2674 if (appName.indexOf("Microsoft Internet Explorer") !== -1) { 2675 browser.isIE = true; 2676 // Check for Trident version 2677 if (useragent.indexOf("Trident/5.0") !== -1) { // IE9 2678 browser.version = "9"; 2679 if (useragent.indexOf("MSIE 9.0") === -1) { 2680 browser.isCompatibilityMode = true; 2681 } 2682 } else if (useragent.indexOf("Trident/4.0") !== -1) { // IE8 2683 browser.version = "8"; 2684 if (useragent.indexOf("MSIE 8.0") === -1) { 2685 browser.isCompatibilityMode = true; 2686 } 2687 } 2688 } 2689 //IE 11 follows different rules. Non standard. 2690 else if (appName.indexOf("Netscape") !== -1) { 2691 if(useragent.match(/Trident.*rv\:11\./) !== -1) { 2692 browser.isIE = true; 2693 browser.version = "11"; 2694 } 2695 } 2696 return browser; 2697 }, 2698 2699 /** 2700 * Use JQuery's implementation of Promises (Deferred) to execute code when 2701 * multiple async processes have finished. An example use: 2702 * 2703 * var asyncProcess1 = $.Deferred(), 2704 * asyncProcess2 = $.Deferred(); 2705 * 2706 * finesse.utilities.Utilities.whenAllDone(asyncProcess1, asyncProcess2) // WHEN both asyncProcess1 and asyncProcess2 are resolved or rejected ... 2707 * .then( 2708 * // First function passed to then() is called when all async processes are complete, regardless of errors 2709 * function () { 2710 * console.log("all processes completed"); 2711 * }, 2712 * // Second function passed to then() is called if any async processed threw an exception 2713 * function (failures) { // Array of failure messages 2714 * console.log("Number of failed async processes: " + failures.length); 2715 * }); 2716 * 2717 * Found at: 2718 * http://stackoverflow.com/a/15094263/1244030 2719 * 2720 * Pass in any number of $.Deferred instances. 2721 * @returns {Object} 2722 */ 2723 whenAllDone: function () { 2724 var deferreds = [], 2725 result = $.Deferred(); 2726 2727 $.each(arguments, function(i, current) { 2728 var currentDeferred = $.Deferred(); 2729 current.then(function() { 2730 currentDeferred.resolve(false, arguments); 2731 }, function() { 2732 currentDeferred.resolve(true, arguments); 2733 }); 2734 deferreds.push(currentDeferred); 2735 }); 2736 2737 $.when.apply($, deferreds).then(function() { 2738 var failures = [], 2739 successes = []; 2740 2741 $.each(arguments, function(i, args) { 2742 // If we resolved with `true` as the first parameter 2743 // we have a failure, a success otherwise 2744 var target = args[0] ? failures : successes, 2745 data = args[1]; 2746 // Push either all arguments or the only one 2747 target.push(data.length === 1 ? data[0] : args); 2748 }); 2749 2750 if(failures.length) { 2751 return result.reject.apply(result, failures); 2752 } 2753 2754 return result.resolve.apply(result, successes); 2755 }); 2756 2757 return result; 2758 } 2759 }; 2760 2761 return Utilities; 2762 })); 2763 2764 /** The following comment is to prevent jslint errors about 2765 * using variables before they are defined. 2766 */ 2767 /*global finesse*/ 2768 2769 /** 2770 * Initiated by the Master to create a shared BOSH connection. 2771 * 2772 * @requires Utilities 2773 */ 2774 2775 /** 2776 * @class 2777 * Establishes a shared event connection by creating a communication tunnel 2778 * with the notification server and consume events which could be published. 2779 * Public functions are exposed to register to the connection status information 2780 * and events. 2781 * @constructor 2782 * @param {String} host 2783 * The host name/ip of the Finesse server. 2784 * @throws {Error} If required constructor parameter is missing. 2785 */ 2786 /** @private */ 2787 (function (factory) { 2788 2789 2790 // Define as an AMD module if possible 2791 if ( typeof define === 'function' && define.amd ) 2792 { 2793 define('clientservices/MasterTunnel',["utilities/Utilities"], factory ); 2794 } 2795 2796 /* Define using browser globals otherwise 2797 * Prevent multiple instantiations if the script is loaded twice 2798 */ 2799 else 2800 { 2801 factory(finesse.utilities.Utilities); 2802 } 2803 2804 }(function (Utilities) { 2805 var MasterTunnel = function (host, scheme) { 2806 if (typeof host !== "string" || host.length === 0) { 2807 throw new Error("Required host parameter missing."); 2808 } 2809 2810 var 2811 2812 /** 2813 * Flag to indicate whether the tunnel frame is loaded. 2814 * @private 2815 */ 2816 _isTunnelLoaded = false, 2817 2818 /** 2819 * Short reference to the Finesse utility. 2820 * @private 2821 */ 2822 _util = Utilities, 2823 2824 /** 2825 * The URL with host and port to the Finesse server. 2826 * @private 2827 */ 2828 _tunnelOrigin, 2829 2830 /** 2831 * Location of the tunnel HTML URL. 2832 * @private 2833 */ 2834 _tunnelURL, 2835 2836 /** 2837 * The port on which to connect to the Finesse server to load the eventing resources. 2838 * @private 2839 */ 2840 _tunnelOriginPort, 2841 2842 /** 2843 * Flag to indicate whether we have processed the tunnel config yet. 2844 * @private 2845 */ 2846 _isTunnelConfigInit = false, 2847 2848 /** 2849 * The tunnel frame window object. 2850 * @private 2851 */ 2852 _tunnelFrame, 2853 2854 /** 2855 * The handler registered with the object to be invoked when an event is 2856 * delivered by the notification server. 2857 * @private 2858 */ 2859 _eventHandler, 2860 2861 /** 2862 * The handler registered with the object to be invoked when presence is 2863 * delivered by the notification server. 2864 * @private 2865 */ 2866 _presenceHandler, 2867 2868 /** 2869 * The handler registered with the object to be invoked when the BOSH 2870 * connection has changed states. The object will contain the "status" 2871 * property and a "resourceID" property only if "status" is "connected". 2872 * @private 2873 */ 2874 _connInfoHandler, 2875 2876 /** 2877 * The last connection status published by the JabberWerx library. 2878 * @private 2879 */ 2880 _statusCache, 2881 2882 /** 2883 * The last event sent by notification server. 2884 * @private 2885 */ 2886 _eventCache, 2887 2888 /** 2889 * The ID of the user logged into notification server. 2890 * @private 2891 */ 2892 _id, 2893 2894 /** 2895 * The domain of the XMPP server, representing the portion of the JID 2896 * following '@': userid@domain.com 2897 * @private 2898 */ 2899 _xmppDomain, 2900 2901 /** 2902 * The password of the user logged into notification server. 2903 * @private 2904 */ 2905 _password, 2906 2907 /** 2908 * The jid of the pubsub service on the XMPP server 2909 * @private 2910 */ 2911 _pubsubDomain, 2912 2913 /** 2914 * The resource to use for the BOSH connection. 2915 * @private 2916 */ 2917 _resource, 2918 2919 /** 2920 * The resource ID identifying the client device (that we receive from the server). 2921 * @private 2922 */ 2923 _resourceID, 2924 2925 /** 2926 * The different types of messages that could be sent to the parent frame. 2927 * The types here should be understood by the parent frame and used to 2928 * identify how the message is formatted. 2929 * @private 2930 */ 2931 _TYPES = { 2932 EVENT: 0, 2933 ID: 1, 2934 PASSWORD: 2, 2935 RESOURCEID: 3, 2936 STATUS: 4, 2937 XMPPDOMAIN: 5, 2938 PUBSUBDOMAIN: 6, 2939 SUBSCRIBE: 7, 2940 UNSUBSCRIBE: 8, 2941 PRESENCE: 9, 2942 CONNECT_REQ: 10 2943 }, 2944 2945 _handlers = { 2946 subscribe: {}, 2947 unsubscribe: {} 2948 }, 2949 2950 2951 /** 2952 * Create a connection info object. 2953 * @returns {Object} 2954 * A connection info object containing a "status" and "resourceID". 2955 * @private 2956 */ 2957 _createConnInfoObj = function () { 2958 return { 2959 status: _statusCache, 2960 resourceID: _resourceID 2961 }; 2962 }, 2963 2964 /** 2965 * Utility function which sends a message to the dynamic tunnel frame 2966 * event frame formatted as follows: "type|message". 2967 * @param {Number} type 2968 * The category type of the message. 2969 * @param {String} message 2970 * The message to be sent to the tunnel frame. 2971 * @private 2972 */ 2973 _sendMessage = function (type, message) { 2974 message = type + "|" + message; 2975 _util.sendMessage(message, _tunnelFrame, _tunnelOrigin); 2976 }, 2977 2978 /** 2979 * Utility to process the response of a subscribe request from 2980 * the tunnel frame, then invoking the stored callback handler 2981 * with the respective data (error, when applicable) 2982 * @param {String} data 2983 * The response in the format of "node[|error]" 2984 * @private 2985 */ 2986 _processSubscribeResponse = function (data) { 2987 var dataArray = data.split("|"), 2988 node = dataArray[0], 2989 err; 2990 2991 //Error is optionally the second item in the array 2992 if (dataArray.length) { 2993 err = dataArray[1]; 2994 } 2995 2996 // These response handlers are short lived and should be removed and cleaned up immediately after invocation. 2997 if (_handlers.subscribe[node]) { 2998 _handlers.subscribe[node](err); 2999 delete _handlers.subscribe[node]; 3000 } 3001 }, 3002 3003 /** 3004 * Utility to process the response of an unsubscribe request from 3005 * the tunnel frame, then invoking the stored callback handler 3006 * with the respective data (error, when applicable) 3007 * @param {String} data 3008 * The response in the format of "node[|error]" 3009 * @private 3010 */ 3011 _processUnsubscribeResponse = function (data) { 3012 var dataArray = data.split("|"), 3013 node = dataArray[0], 3014 err; 3015 3016 //Error is optionally the second item in the array 3017 if (dataArray.length) { 3018 err = dataArray[1]; 3019 } 3020 3021 // These response handlers are short lived and should be removed and cleaned up immediately after invocation. 3022 if (_handlers.unsubscribe[node]) { 3023 _handlers.unsubscribe[node](err); 3024 delete _handlers.unsubscribe[node]; 3025 } 3026 }, 3027 3028 /** 3029 * Handler for messages delivered by window.postMessage. Listens for events 3030 * published by the notification server, connection status published by 3031 * the JabberWerx library, and the resource ID created when the BOSH 3032 * connection has been established. 3033 * @param {Object} e 3034 * The message object as provided by the window.postMessage feature. 3035 * @private 3036 */ 3037 _messageHandler = function (e) { 3038 var 3039 3040 //Extract the message type and message data. The expected format is 3041 //"type|data" where type is a number represented by the TYPES object. 3042 delimPos = e.data.indexOf("|"), 3043 type = Number(e.data.substr(0, delimPos)), 3044 data = e.data.substr(delimPos + 1); 3045 3046 //Accepts messages and invoke the correct registered handlers. 3047 switch (type) { 3048 case _TYPES.EVENT: 3049 _eventCache = data; 3050 if (typeof _eventHandler === "function") { 3051 _eventHandler(data); 3052 } 3053 break; 3054 case _TYPES.STATUS: 3055 _statusCache = data; 3056 3057 //A "loaded" status means that the frame is ready to accept 3058 //credentials for establishing a BOSH connection. 3059 if (data === "loaded") { 3060 _isTunnelLoaded = true; 3061 if(_resource) { 3062 _sendMessage(_TYPES.RESOURCEID, _resource); 3063 } 3064 _sendMessage(_TYPES.ID, _id); 3065 _sendMessage(_TYPES.XMPPDOMAIN, _xmppDomain); 3066 _sendMessage(_TYPES.PASSWORD, _password); 3067 _sendMessage(_TYPES.PUBSUBDOMAIN, _pubsubDomain); 3068 } else if (typeof _connInfoHandler === "function") { 3069 _connInfoHandler(_createConnInfoObj()); 3070 } 3071 break; 3072 case _TYPES.RESOURCEID: 3073 _resourceID = data; 3074 break; 3075 case _TYPES.SUBSCRIBE: 3076 _processSubscribeResponse(data); 3077 break; 3078 case _TYPES.UNSUBSCRIBE: 3079 _processUnsubscribeResponse(data); 3080 break; 3081 case _TYPES.PRESENCE: 3082 if (typeof _presenceHandler === "function") { 3083 _presenceHandler(data); 3084 } 3085 break; 3086 default: 3087 break; 3088 } 3089 }, 3090 3091 /** 3092 * Initialize the tunnel config so that the url can be http or https with the appropriate port 3093 * @private 3094 */ 3095 _initTunnelConfig = function () { 3096 if (_isTunnelConfigInit === true) { 3097 return; 3098 } 3099 3100 //Initialize tunnel origin 3101 //Determine tunnel origin based on host and scheme 3102 _tunnelOriginPort = (scheme && scheme.indexOf("https") !== -1) ? "7443" : "7071"; 3103 if (scheme) { 3104 _tunnelOrigin = scheme + "://" + host + ":" + _tunnelOriginPort; 3105 } else { 3106 _tunnelOrigin = "http://" + host + ":" + _tunnelOriginPort; 3107 } 3108 _tunnelURL = _tunnelOrigin + "/tunnel/"; 3109 3110 _isTunnelConfigInit = true; 3111 }, 3112 3113 /** 3114 * Create the tunnel iframe which establishes the shared BOSH connection. 3115 * Messages are sent across frames using window.postMessage. 3116 * @private 3117 */ 3118 _createTunnel = function () { 3119 var tunnelID = ((self === parent) ? "tunnel-frame" : "autopilot-tunnel-frame"), 3120 iframe = document.createElement("iframe"); 3121 iframe.style.display = "none"; 3122 iframe.setAttribute("id", tunnelID); 3123 iframe.setAttribute("name", tunnelID); 3124 iframe.setAttribute("src", _tunnelURL); 3125 document.body.appendChild(iframe); 3126 _tunnelFrame = window.frames[tunnelID]; 3127 }; 3128 3129 /** 3130 * Sends a message via postmessage to the EventTunnel to attempt to connect to the XMPP server 3131 * @private 3132 */ 3133 this.makeConnectReq = function () { 3134 _sendMessage(_TYPES.PASSWORD, _password); 3135 }; 3136 3137 /** 3138 * @private 3139 * Returns the host of the Finesse server. 3140 * @returns {String} 3141 * The host specified during the creation of the object. 3142 */ 3143 this.getHost = function () { 3144 return host; 3145 }; 3146 3147 /** 3148 * @private 3149 * The resource ID of the user who is logged into the notification server. 3150 * @returns {String} 3151 * The resource ID generated by the notification server. 3152 */ 3153 this.getResourceID = function () { 3154 return _resourceID; 3155 }; 3156 3157 /** 3158 * @private 3159 * Indicates whether the tunnel frame is loaded. 3160 * @returns {Boolean} 3161 * True if the tunnel frame is loaded, false otherwise. 3162 */ 3163 this.isTunnelLoaded = function () { 3164 return _isTunnelLoaded; 3165 }; 3166 3167 /** 3168 * @private 3169 * The location of the tunnel HTML URL. 3170 * @returns {String} 3171 * The location of the tunnel HTML URL. 3172 */ 3173 this.getTunnelURL = function () { 3174 return _tunnelURL; 3175 }; 3176 3177 /** 3178 * @private 3179 * Tunnels a subscribe request to the eventing iframe. 3180 * @param {String} node 3181 * The node to subscribe to 3182 * @param {Function} handler 3183 * Handler to invoke upon success or failure 3184 */ 3185 this.subscribe = function (node, handler) { 3186 if (handler && typeof handler !== "function") { 3187 throw new Error("Parameter is not a function."); 3188 } 3189 _handlers.subscribe[node] = handler; 3190 _sendMessage(_TYPES.SUBSCRIBE, node); 3191 }; 3192 3193 /** 3194 * @private 3195 * Tunnels an unsubscribe request to the eventing iframe. 3196 * @param {String} node 3197 * The node to unsubscribe from 3198 * @param {Function} handler 3199 * Handler to invoke upon success or failure 3200 */ 3201 this.unsubscribe = function (node, handler) { 3202 if (handler && typeof handler !== "function") { 3203 throw new Error("Parameter is not a function."); 3204 } 3205 _handlers.unsubscribe[node] = handler; 3206 _sendMessage(_TYPES.UNSUBSCRIBE, node); 3207 }; 3208 3209 /** 3210 * @private 3211 * Registers a handler to be invoked when an event is delivered. Only one 3212 * is registered at a time. If there has already been an event that was 3213 * delivered, the handler will be invoked immediately. 3214 * @param {Function} handler 3215 * Invoked when an event is delivered through the event connection. 3216 */ 3217 this.registerEventHandler = function (handler) { 3218 if (typeof handler !== "function") { 3219 throw new Error("Parameter is not a function."); 3220 } 3221 _eventHandler = handler; 3222 if (_eventCache) { 3223 handler(_eventCache); 3224 } 3225 }; 3226 3227 /** 3228 * @private 3229 * Unregisters the event handler completely. 3230 */ 3231 this.unregisterEventHandler = function () { 3232 _eventHandler = undefined; 3233 }; 3234 3235 /** 3236 * @private 3237 * Registers a handler to be invoked when a presence event is delivered. Only one 3238 * is registered at a time. 3239 * @param {Function} handler 3240 * Invoked when a presence event is delivered through the event connection. 3241 */ 3242 this.registerPresenceHandler = function (handler) { 3243 if (typeof handler !== "function") { 3244 throw new Error("Parameter is not a function."); 3245 } 3246 _presenceHandler = handler; 3247 }; 3248 3249 /** 3250 * @private 3251 * Unregisters the presence event handler completely. 3252 */ 3253 this.unregisterPresenceHandler = function () { 3254 _presenceHandler = undefined; 3255 }; 3256 3257 /** 3258 * @private 3259 * Registers a handler to be invoked when a connection status changes. The 3260 * object passed will contain a "status" property, and a "resourceID" 3261 * property, which will contain the most current resource ID assigned to 3262 * the client. If there has already been an event that was delivered, the 3263 * handler will be invoked immediately. 3264 * @param {Function} handler 3265 * Invoked when a connection status changes. 3266 */ 3267 this.registerConnectionInfoHandler = function (handler) { 3268 if (typeof handler !== "function") { 3269 throw new Error("Parameter is not a function."); 3270 } 3271 _connInfoHandler = handler; 3272 if (_statusCache) { 3273 handler(_createConnInfoObj()); 3274 } 3275 }; 3276 3277 /** 3278 * @private 3279 * Unregisters the connection information handler. 3280 */ 3281 this.unregisterConnectionInfoHandler = function () { 3282 _connInfoHandler = undefined; 3283 }; 3284 3285 /** 3286 * @private 3287 * Start listening for events and create a event tunnel for the shared BOSH 3288 * connection. 3289 * @param {String} id 3290 * The ID of the user for the notification server. 3291 * @param {String} password 3292 * The password of the user for the notification server. 3293 * @param {String} xmppDomain 3294 * The XMPP domain of the notification server 3295 * @param {String} pubsubDomain 3296 * The location (JID) of the XEP-0060 PubSub service 3297 * @param {String} resource 3298 * The resource to connect to the notification servier with. 3299 */ 3300 this.init = function (id, password, xmppDomain, pubsubDomain, resource) { 3301 3302 if (typeof id !== "string" || typeof password !== "string" || typeof xmppDomain !== "string" || typeof pubsubDomain !== "string") { 3303 throw new Error("Invalid or missing required parameters."); 3304 } 3305 3306 _initTunnelConfig(); 3307 3308 _id = id; 3309 _password = password; 3310 _xmppDomain = xmppDomain; 3311 _pubsubDomain = pubsubDomain; 3312 _resource = resource; 3313 3314 //Attach a listener for messages sent from tunnel frame. 3315 _util.receiveMessage(_messageHandler, _tunnelOrigin); 3316 3317 //Create the tunnel iframe which will establish the shared connection. 3318 _createTunnel(); 3319 }; 3320 3321 //BEGIN TEST CODE// 3322 // /** 3323 // * Test code added to expose private functions that are used by unit test 3324 // * framework. This section of code is removed during the build process 3325 // * before packaging production code. The [begin|end]TestSection are used 3326 // * by the build to identify the section to strip. 3327 // * @ignore 3328 // */ 3329 // this.beginTestSection = 0; 3330 // 3331 // /** 3332 // * @ignore 3333 // */ 3334 // this.getTestObject = function () { 3335 // //Load mock dependencies. 3336 // var _mock = new MockControl(); 3337 // _util = _mock.createMock(finesse.utilities.Utilities); 3338 // 3339 // return { 3340 // //Expose mock dependencies 3341 // mock: _mock, 3342 // util: _util, 3343 // 3344 // //Expose internal private functions 3345 // types: _TYPES, 3346 // createConnInfoObj: _createConnInfoObj, 3347 // sendMessage: _sendMessage, 3348 // messageHandler: _messageHandler, 3349 // createTunnel: _createTunnel, 3350 // handlers: _handlers, 3351 // initTunnelConfig : _initTunnelConfig 3352 // }; 3353 // }; 3354 // 3355 // /** 3356 // * @ignore 3357 // */ 3358 // this.endTestSection = 0; 3359 // //END TEST CODE// 3360 }; 3361 3362 /** @namespace JavaScript class objects and methods to handle the subscription to Finesse events.*/ 3363 finesse.clientservices = finesse.clientservices || {}; 3364 3365 window.finesse = window.finesse || {}; 3366 window.finesse.clientservices = window.finesse.clientservices || {}; 3367 window.finesse.clientservices.MasterTunnel = MasterTunnel; 3368 3369 })); 3370 3371 /** 3372 * Contains a list of topics used for client side pubsub. 3373 * 3374 */ 3375 3376 /** @private */ 3377 (function (factory) { 3378 3379 3380 // Define as an AMD module if possible 3381 if ( typeof define === 'function' && define.amd ) 3382 { 3383 define('clientservices/Topics',[], factory ); 3384 } 3385 3386 /* Define using browser globals otherwise 3387 * Prevent multiple instantiations if the script is loaded twice 3388 */ 3389 else 3390 { 3391 factory(); 3392 } 3393 3394 }(function () { 3395 3396 var Topics = (function () { 3397 3398 /** 3399 * @private 3400 * The namespace prepended to all Finesse topics. 3401 */ 3402 this.namespace = "finesse.info"; 3403 3404 /** 3405 * @private 3406 * Gets the full topic name with the Finesse namespace prepended. 3407 * @param {String} topic 3408 * The topic category. 3409 * @returns {String} 3410 * The full topic name with prepended namespace. 3411 */ 3412 var _getNSTopic = function (topic) { 3413 return this.namespace + "." + topic; 3414 }; 3415 3416 /** @scope finesse.clientservices.Topics */ 3417 return { 3418 /** 3419 * @private 3420 * Client side request channel. 3421 */ 3422 REQUESTS: _getNSTopic("requests"), 3423 3424 /** 3425 * @private 3426 * Client side response channel. 3427 */ 3428 RESPONSES: _getNSTopic("responses"), 3429 3430 /** 3431 * @private 3432 * Connection status. 3433 */ 3434 EVENTS_CONNECTION_INFO: _getNSTopic("connection"), 3435 3436 /** 3437 * @private 3438 * Presence channel 3439 */ 3440 PRESENCE: _getNSTopic("presence"), 3441 3442 /** 3443 * @private 3444 * Convert a Finesse REST URI to a OpenAjax compatible topic name. 3445 */ 3446 getTopic: function (restUri) { 3447 //The topic should not start with '/' else it will get replaced with 3448 //'.' which is invalid. 3449 //Thus, remove '/' if it is at the beginning of the string 3450 if (restUri.indexOf('/') === 0) { 3451 restUri = restUri.substr(1); 3452 } 3453 3454 //Replace every instance of "/" with ".". This is done to follow the 3455 //OpenAjaxHub topic name convention. 3456 return restUri.replace(/\//g, "."); 3457 } 3458 }; 3459 }()); 3460 window.finesse = window.finesse || {}; 3461 window.finesse.clientservices = window.finesse.clientservices || {}; 3462 /** @private */ 3463 window.finesse.clientservices.Topics = Topics; 3464 3465 return Topics; 3466 })); 3467 3468 /** The following comment is to prevent jslint errors about 3469 * using variables before they are defined. 3470 */ 3471 /*global finesse*/ 3472 3473 /** 3474 * Registers with the MasterTunnel to receive events, which it 3475 * could publish to the OpenAjax gadget pubsub infrastructure. 3476 * 3477 * @requires OpenAjax, finesse.clientservices.MasterTunnel, finesse.clientservices.Topics 3478 */ 3479 3480 /** @private */ 3481 (function (factory) { 3482 3483 3484 // Define as an AMD module if possible 3485 if ( typeof define === 'function' && define.amd ) 3486 { 3487 define('clientservices/MasterPublisher',["clientservices/MasterTunnel", 3488 "clientservices/Topics", 3489 "utilities/Utilities"], factory ); 3490 } 3491 3492 /* Define using browser globals otherwise 3493 * Prevent multiple instantiations if the script is loaded twice 3494 */ 3495 else 3496 { 3497 factory(finesse.clientservices.MasterTunnel, finesse.clientservices.Topics, finesse.utilities.Utilities); 3498 } 3499 3500 }(function (MasterTunnel, Topics, Utilities) { 3501 3502 var MasterPublisher = function (tunnel, hub) { 3503 if (!(tunnel instanceof MasterTunnel)) { 3504 throw new Error("Required tunnel object missing or invalid."); 3505 } 3506 3507 var 3508 3509 ClientServices = finesse.clientservices.ClientServices, 3510 3511 /** 3512 * Reference to the gadget pubsub Hub instance. 3513 * @private 3514 */ 3515 _hub = hub, 3516 3517 /** 3518 * Reference to the Topics class. 3519 * @private 3520 */ 3521 _topics = Topics, 3522 3523 /** 3524 * Reference to conversion utilities class. 3525 * @private 3526 */ 3527 _utils = Utilities, 3528 3529 /** 3530 * References to ClientServices logger methods 3531 * @private 3532 */ 3533 _logger = { 3534 log: ClientServices.log 3535 }, 3536 3537 /** 3538 * Store the passed in tunnel. 3539 * @private 3540 */ 3541 _tunnel = tunnel, 3542 3543 /** 3544 * Caches the connection info event so that it could be published if there 3545 * is a request for it. 3546 * @private 3547 */ 3548 _connInfoCache = {}, 3549 3550 /** 3551 * The types of possible request types supported when listening to the 3552 * requests channel. Each request type could result in different operations. 3553 * @private 3554 */ 3555 _REQTYPES = { 3556 CONNECTIONINFO: "ConnectionInfoReq", 3557 SUBSCRIBE: "SubscribeNodeReq", 3558 UNSUBSCRIBE: "UnsubscribeNodeReq", 3559 CONNECT: "ConnectionReq" 3560 }, 3561 3562 /** 3563 * Will store list of nodes that have OF subscriptions created 3564 * _nodesList[node][subscribing].reqIds[subid] 3565 * _nodesList[node][active].reqIds[subid] 3566 * _nodesList[node][unsubscribing].reqIds[subid] 3567 * _nodesList[node][holding].reqIds[subid] 3568 * @private 3569 */ 3570 _nodesList = {}, 3571 3572 /** 3573 * The states that a subscription can be in 3574 * @private 3575 */ 3576 _CHANNELSTATES = { 3577 UNINITIALIZED: "Uninitialized", 3578 PENDING: "Pending", 3579 OPERATIONAL: "Operational" 3580 }, 3581 3582 /** 3583 * Checks if the payload is JSON 3584 * @returns {Boolean} 3585 * @private 3586 */ 3587 _isJsonPayload = function(event) { 3588 var delimStart, delimEnd, retval = false; 3589 3590 try { 3591 delimStart = event.indexOf('{'); 3592 delimEnd = event.lastIndexOf('}'); 3593 3594 if ((delimStart !== -1 ) && (delimEnd === (event.length - 1))) { 3595 retval = true; //event contains JSON payload 3596 } 3597 } catch (err) { 3598 _logger.log("MasterPublisher._isJsonPayload() - Caught error: " + err); 3599 } 3600 return retval; 3601 }, 3602 3603 /** 3604 * Parses a JSON event and then publishes. 3605 * 3606 * @param {String} event 3607 * The full event payload. 3608 * @throws {Error} If the payload object is malformed. 3609 * @private 3610 */ 3611 _parseAndPublishJSONEvent = function(event) { 3612 var topic, eventObj, publishEvent, 3613 delimPos = event.indexOf("{"), 3614 node, parser, 3615 eventJson = event, 3616 returnObj = {node: null, data: null}; 3617 3618 try { 3619 //Extract and strip the node path from the message 3620 if (delimPos > 0) 3621 { 3622 //We need to decode the URI encoded node path 3623 //TODO: make sure this is kosher with OpenAjax topic naming 3624 node = decodeURI(event.substr(0, delimPos)); 3625 eventJson = event.substr(delimPos); 3626 3627 //Converting the node path to openAjaxhub topic 3628 topic = _topics.getTopic(node); 3629 3630 returnObj.node = node; 3631 returnObj.payload = eventJson; 3632 } 3633 else 3634 { 3635 _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] node is not given in postMessage: " + eventJson); 3636 throw new Error("node is not given in postMessage: " + eventJson); 3637 } 3638 3639 parser = _utils.getJSONParser(); 3640 3641 eventObj = parser.parse(eventJson); 3642 returnObj.data = eventObj; 3643 3644 } catch (err) { 3645 _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] Malformed event payload: " + err); 3646 throw new Error("Malformed event payload : " + err); 3647 } 3648 3649 _logger.log("MasterPublisher._parseAndPublishJSONEvent() - Received JSON event on node '" + node + "': " + eventJson); 3650 3651 publishEvent = {content : event, object : eventObj }; 3652 3653 //Publish event to proper event topic. 3654 if (topic && eventObj) { 3655 _hub.publish(topic, publishEvent); 3656 } 3657 }, 3658 3659 /** 3660 * Parses an XML event and then publishes. 3661 * 3662 * @param {String} event 3663 * The full event payload. 3664 * @throws {Error} If the payload object is malformed. 3665 * @private 3666 */ 3667 _parseAndPublishXMLEvent = function(event) { 3668 var topic, eventObj, publishEvent, restTopic, 3669 delimPos = event.indexOf("<"), 3670 node, 3671 eventXml = event; 3672 3673 try { 3674 //Extract and strip the node path from the message 3675 if (delimPos > 0) { 3676 //We need to decode the URI encoded node path 3677 //TODO: make sure this is kosher with OpenAjax topic naming 3678 node = decodeURI(event.substr(0, delimPos)); 3679 eventXml = event.substr(delimPos); 3680 //Converting the node path to openAjaxhub topic 3681 topic = _topics.getTopic(node); 3682 } else { 3683 _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] node is not given in postMessage: " + eventXml); 3684 throw new Error("node is not given in postMessage: " + eventXml); 3685 } 3686 3687 eventObj = _utils.xml2JsObj(eventXml); 3688 3689 } catch (err) { 3690 _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] Malformed event payload: " + err); 3691 throw new Error("Malformed event payload : " + err); 3692 } 3693 3694 _logger.log("MasterPublisher._parseAndPublishXMLEvent() - Received XML event on node '" + node + "': " + eventXml); 3695 3696 publishEvent = {content : event, object : eventObj }; 3697 3698 //Publish event to proper event topic. 3699 if (topic && eventObj) { 3700 _hub.publish(topic, publishEvent); 3701 } 3702 }, 3703 3704 /** 3705 * Publishes events to the appropriate topic. The topic name is determined 3706 * by fetching the source value from the event. 3707 * @param {String} event 3708 * The full event payload. 3709 * @throws {Error} If the payload object is malformed. 3710 * @private 3711 */ 3712 _eventHandler = function (event) { 3713 3714 //Handle JSON or XML events 3715 if (!_isJsonPayload(event)) 3716 { 3717 //XML 3718 _parseAndPublishXMLEvent(event); 3719 } 3720 else 3721 { 3722 //JSON 3723 _parseAndPublishJSONEvent(event); 3724 } 3725 }, 3726 3727 3728 /** 3729 * Handler for when presence events are sent through the MasterTunnel. 3730 * @returns {Object} 3731 * A presence xml event. 3732 * @private 3733 */ 3734 _presenceHandler = function (event) { 3735 var eventObj = _utils.xml2JsObj(event), publishEvent; 3736 3737 publishEvent = {content : event, object : eventObj}; 3738 3739 if (eventObj) { 3740 _hub.publish(_topics.PRESENCE, publishEvent); 3741 } 3742 }, 3743 3744 /** 3745 * Clone the connection info object from cache. 3746 * @returns {Object} 3747 * A connection info object containing a "status" and "resourceID". 3748 * @private 3749 */ 3750 _cloneConnInfoObj = function () { 3751 if (_connInfoCache) { 3752 return { 3753 status: _connInfoCache.status, 3754 resourceID: _connInfoCache.resourceID 3755 }; 3756 } else { 3757 return null; 3758 } 3759 }, 3760 3761 /** 3762 * Cleans up any outstanding subscribe/unsubscribe requests and notifies them of errors. 3763 * This is done if we get disconnected because we cleanup explicit subscriptions on disconnect. 3764 * @private 3765 */ 3766 _cleanupPendingRequests = function () { 3767 var node, curSubid, errObj = { 3768 error: { 3769 errorType: "Disconnected", 3770 errorMessage: "Outstanding request will never complete." 3771 } 3772 }; 3773 3774 // Iterate through all outstanding subscribe requests to notify them that it will never return 3775 for (node in _nodesList) { 3776 if (_nodesList.hasOwnProperty(node)) { 3777 for (curSubid in _nodesList[node].subscribing.reqIds) { 3778 if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) { 3779 // Notify this outstanding subscribe request to give up and error out 3780 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 3781 } 3782 } 3783 for (curSubid in _nodesList[node].unsubscribing.reqIds) { 3784 if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) { 3785 // Notify this outstanding unsubscribe request to give up and error out 3786 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 3787 } 3788 } 3789 } 3790 } 3791 }, 3792 3793 /** 3794 * Publishes the connection info to the connection info topic. 3795 * @param {Object} connInfo 3796 * The connection info object containing the status and resource ID. 3797 * @private 3798 */ 3799 _connInfoHandler = function (connInfo) { 3800 var before = _connInfoCache.status; 3801 _connInfoCache = connInfo; 3802 _logger.log("MasterPublisher._connInfoHandler() - Connection status: " + connInfo.status); 3803 _hub.publish(_topics.EVENTS_CONNECTION_INFO, _cloneConnInfoObj()); 3804 if (before === "connected" && connInfo.status !== "connected") { 3805 // Fail all pending requests when we transition to disconnected 3806 _cleanupPendingRequests(); 3807 } 3808 }, 3809 3810 3811 /** 3812 * Utility method to bookkeep node subscription requests and determine 3813 * whehter it is necessary to tunnel the request to JabberWerx. 3814 * @param {String} node 3815 * The node of interest 3816 * @param {String} reqId 3817 * A unique string identifying the request/subscription 3818 * @private 3819 */ 3820 _subscribeNode = function (node, subid) { 3821 if (_connInfoCache.status !== "connected") { 3822 _hub.publish(_topics.RESPONSES + "." + subid, { 3823 error: { 3824 errorType: "Not connected", 3825 errorMessage: "Cannot subscribe without connection." 3826 } 3827 }); 3828 return; 3829 } 3830 // NODE DOES NOT YET EXIST 3831 if (!_nodesList[node]) { 3832 _nodesList[node] = { 3833 "subscribing": { 3834 "reqIds": {}, 3835 "length": 0 3836 }, 3837 "active": { 3838 "reqIds": {}, 3839 "length": 0 3840 }, 3841 "unsubscribing": { 3842 "reqIds": {}, 3843 "length": 0 3844 }, 3845 "holding": { 3846 "reqIds": {}, 3847 "length": 0 3848 } 3849 }; 3850 } 3851 if (_nodesList[node].active.length === 0) { 3852 if (_nodesList[node].unsubscribing.length === 0) { 3853 if (_nodesList[node].subscribing.length === 0) { 3854 _nodesList[node].subscribing.reqIds[subid] = true; 3855 _nodesList[node].subscribing.length += 1; 3856 3857 _logger.log("MasterPublisher._subscribeNode() - Attempting to subscribe to node '" + node + "'"); 3858 _tunnel.subscribe(node, function (err) { 3859 var errObj, curSubid; 3860 if (err) { 3861 errObj = { 3862 subscribe: { 3863 content: err 3864 } 3865 }; 3866 3867 try { 3868 errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), ""))); 3869 } catch (e) { 3870 errObj.error = { 3871 errorType: "parseError", 3872 errorMessage: "Could not serialize XML: " + e 3873 }; 3874 } 3875 _logger.log("MasterPublisher._subscribeNode() - Error subscribing to node '" + node + "': " + err); 3876 } else { 3877 _logger.log("MasterPublisher._subscribeNode() - Subscribed to node '" + node + "'"); 3878 } 3879 3880 for (curSubid in _nodesList[node].subscribing.reqIds) { 3881 if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) { 3882 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 3883 if (!err) { 3884 _nodesList[node].active.reqIds[curSubid] = true; 3885 _nodesList[node].active.length += 1; 3886 } 3887 delete _nodesList[node].subscribing.reqIds[curSubid]; 3888 _nodesList[node].subscribing.length -= 1; 3889 } 3890 } 3891 }); 3892 3893 } else { //other ids are subscribing 3894 _nodesList[node].subscribing.reqIds[subid] = true; 3895 _nodesList[node].subscribing.length += 1; 3896 } 3897 } else { //An unsubscribe request is pending, hold onto these subscribes until it is done 3898 _nodesList[node].holding.reqIds[subid] = true; 3899 _nodesList[node].holding.length += 1; 3900 } 3901 } else { // The node has active subscriptions; add this subid and return successful response 3902 _nodesList[node].active.reqIds[subid] = true; 3903 _nodesList[node].active.length += 1; 3904 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 3905 } 3906 }, 3907 3908 /** 3909 * Utility method to bookkeep node unsubscribe requests and determine 3910 * whehter it is necessary to tunnel the request to JabberWerx. 3911 * @param {String} node 3912 * The node to unsubscribe from 3913 * @param {String} reqId 3914 * A unique string identifying the subscription to remove 3915 * @private 3916 */ 3917 _unsubscribeNode = function (node, subid) { 3918 if (!_nodesList[node]) { //node DNE, publish success response 3919 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 3920 } else { 3921 if (_connInfoCache.status !== "connected") { 3922 _hub.publish(_topics.RESPONSES + "." + subid, { 3923 error: { 3924 errorType: "Not connected", 3925 errorMessage: "Cannot unsubscribe without connection." 3926 } 3927 }); 3928 return; 3929 } 3930 if (_nodesList[node].active.length > 1) { 3931 delete _nodesList[node].active.reqIds[subid]; 3932 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 3933 _nodesList[node].active.length -= 1; 3934 } else if (_nodesList[node].active.length === 1) { // transition subid from active category to unsubscribing category 3935 _nodesList[node].unsubscribing.reqIds[subid] = true; 3936 _nodesList[node].unsubscribing.length += 1; 3937 delete _nodesList[node].active.reqIds[subid]; 3938 _nodesList[node].active.length -= 1; 3939 3940 _logger.log("MasterPublisher._unsubscribeNode() - Attempting to unsubscribe from node '" + node + "'"); 3941 _tunnel.unsubscribe(node, function (err) { 3942 var errObj, curSubid; 3943 if (err) { 3944 errObj = { 3945 subscribe: { 3946 content: err 3947 } 3948 }; 3949 3950 try { 3951 errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), ""))); 3952 } catch (e) { 3953 errObj.error = { 3954 errorType: "parseError", 3955 errorMessage: "Could not serialize XML: " + e 3956 }; 3957 } 3958 _logger.log("MasterPublisher._unsubscribeNode() - Error unsubscribing from node '" + node + "': " + err); 3959 } else { 3960 _logger.log("MasterPublisher._unsubscribeNode() - Unsubscribed from node '" + node + "'"); 3961 } 3962 3963 for (curSubid in _nodesList[node].unsubscribing.reqIds) { 3964 if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) { 3965 // publish to all subids whether unsubscribe failed or succeeded 3966 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 3967 if (!err) { 3968 delete _nodesList[node].unsubscribing.reqIds[curSubid]; 3969 _nodesList[node].unsubscribing.length -= 1; 3970 } else { // Just remove the subid from unsubscribing; the next subscribe request will operate with node already created 3971 delete _nodesList[node].unsubscribing.reqIds[curSubid]; 3972 _nodesList[node].unsubscribing.length -= 1; 3973 } 3974 } 3975 } 3976 3977 if (!err && _nodesList[node].holding.length > 0) { // if any subscribe requests came in while unsubscribing from OF, now transition from holding to subscribing 3978 for (curSubid in _nodesList[node].holding.reqIds) { 3979 if (_nodesList[node].holding.reqIds.hasOwnProperty(curSubid)) { 3980 delete _nodesList[node].holding.reqIds[curSubid]; 3981 _nodesList[node].holding.length -= 1; 3982 _subscribeNode(node, curSubid); 3983 } 3984 } 3985 } 3986 }); 3987 } else { // length <= 0? 3988 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 3989 } 3990 } 3991 }, 3992 3993 /** 3994 * Handles client requests to establish a BOSH connection. 3995 * @param {String} id 3996 * id of the xmpp user 3997 * @param {String} password 3998 * password of the xmpp user 3999 * @param {String} xmppDomain 4000 * xmppDomain of the xmpp user account 4001 * @private 4002 */ 4003 _connect = function (id, password, xmppDomain) { 4004 _tunnel.makeConnectReq(id, password, xmppDomain); 4005 }, 4006 4007 /** 4008 * Handles client requests made to the request topic. The type of the 4009 * request is described in the "type" property within the data payload. Each 4010 * type can result in a different operation. 4011 * @param {String} topic 4012 * The topic which data was published to. 4013 * @param {Object} data 4014 * The data containing requests information published by clients. 4015 * @param {String} data.type 4016 * The type of the request. Supported: "ConnectionInfoReq" 4017 * @param {Object} data.data 4018 * May contain data relevant for the particular requests. 4019 * @param {String} [data.invokeID] 4020 * The ID used to identify the request with the response. The invoke ID 4021 * will be included in the data in the publish to the topic. It is the 4022 * responsibility of the client to correlate the published data to the 4023 * request made by using the invoke ID. 4024 * @private 4025 */ 4026 _clientRequestHandler = function (topic, data) { 4027 var dataCopy; 4028 4029 //Ensure a valid data object with "type" and "data" properties. 4030 if (typeof data === "object" && 4031 typeof data.type === "string" && 4032 typeof data.data === "object") { 4033 switch (data.type) { 4034 case _REQTYPES.CONNECTIONINFO: 4035 //It is possible that Slave clients come up before the Master 4036 //client. If that is the case, the Slaves will need to make a 4037 //request for the Master to send the latest connection info to the 4038 //connectionInfo topic. 4039 dataCopy = _cloneConnInfoObj(); 4040 if (dataCopy) { 4041 if (data.invokeID !== undefined) { 4042 dataCopy.invokeID = data.invokeID; 4043 } 4044 _hub.publish(_topics.EVENTS_CONNECTION_INFO, dataCopy); 4045 } 4046 break; 4047 case _REQTYPES.SUBSCRIBE: 4048 if (typeof data.data.node === "string") { 4049 _subscribeNode(data.data.node, data.invokeID); 4050 } 4051 break; 4052 case _REQTYPES.UNSUBSCRIBE: 4053 if (typeof data.data.node === "string") { 4054 _unsubscribeNode(data.data.node, data.invokeID); 4055 } 4056 break; 4057 case _REQTYPES.CONNECT: 4058 // Deprecated: Disallow others (non-masters) from administering BOSH connections that are not theirs 4059 _logger.log("MasterPublisher._clientRequestHandler(): F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq"); 4060 break; 4061 default: 4062 break; 4063 } 4064 } 4065 }; 4066 4067 (function () { 4068 //Register to receive events and connection status from tunnel. 4069 _tunnel.registerEventHandler(_eventHandler); 4070 _tunnel.registerPresenceHandler(_presenceHandler); 4071 _tunnel.registerConnectionInfoHandler(_connInfoHandler); 4072 4073 //Listen to a request channel to respond to any requests made by other 4074 //clients because the Master may have access to useful information. 4075 _hub.subscribe(_topics.REQUESTS, _clientRequestHandler); 4076 }()); 4077 4078 /** 4079 * @private 4080 * Handles client requests to establish a BOSH connection. 4081 * @param {String} id 4082 * id of the xmpp user 4083 * @param {String} password 4084 * password of the xmpp user 4085 * @param {String} xmppDomain 4086 * xmppDomain of the xmpp user account 4087 */ 4088 this.connect = function (id, password, xmppDomain) { 4089 _connect(id, password, xmppDomain); 4090 }; 4091 4092 /** 4093 * @private 4094 * Resets the list of explicit subscriptions 4095 */ 4096 this.wipeout = function () { 4097 _cleanupPendingRequests(); 4098 _nodesList = {}; 4099 }; 4100 4101 //BEGIN TEST CODE// 4102 /** 4103 * Test code added to expose private functions that are used by unit test 4104 * framework. This section of code is removed during the build process 4105 * before packaging production code. The [begin|end]TestSection are used 4106 * by the build to identify the section to strip. 4107 * @ignore 4108 */ 4109 this.beginTestSection = 0; 4110 4111 /** 4112 * @ignore 4113 */ 4114 this.getTestObject = function () { 4115 //Load mock dependencies. 4116 var _mock = new MockControl(); 4117 _hub = _mock.createMock(gadgets.Hub); 4118 _tunnel = _mock.createMock(); 4119 4120 return { 4121 //Expose mock dependencies 4122 mock: _mock, 4123 hub: _hub, 4124 tunnel: _tunnel, 4125 setTunnel: function (tunnel) { 4126 _tunnel = tunnel; 4127 }, 4128 getTunnel: function () { 4129 return _tunnel; 4130 }, 4131 4132 //Expose internal private functions 4133 reqtypes: _REQTYPES, 4134 eventHandler: _eventHandler, 4135 presenceHandler: _presenceHandler, 4136 4137 subscribeNode: _subscribeNode, 4138 unsubscribeNode: _unsubscribeNode, 4139 4140 getNodeList: function () { 4141 return _nodesList; 4142 }, 4143 setNodeList: function (nodelist) { 4144 _nodesList = nodelist; 4145 }, 4146 4147 cloneConnInfoObj: _cloneConnInfoObj, 4148 connInfoHandler: _connInfoHandler, 4149 clientRequestHandler: _clientRequestHandler 4150 4151 }; 4152 }; 4153 4154 4155 /** 4156 * @ignore 4157 */ 4158 this.endTestSection = 0; 4159 //END TEST CODE// 4160 4161 }; 4162 4163 window.finesse = window.finesse || {}; 4164 window.finesse.clientservices = window.finesse.clientservices || {}; 4165 window.finesse.clientservices.MasterPublisher = MasterPublisher; 4166 4167 })); 4168 4169 /** The following comment is to prevent jslint errors about 4170 * using variables before they are defined. 4171 */ 4172 /*global publisher:true */ 4173 4174 /** 4175 * Exposes a set of API wrappers that will hide the dirty work of 4176 * constructing Finesse API requests and consuming Finesse events. 4177 * 4178 * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities 4179 */ 4180 4181 4182 /** 4183 * Allow clients to make Finesse API requests and consume Finesse events by 4184 * calling a set of exposed functions. The Services layer will do the dirty 4185 * work of establishing a shared BOSH connection (for designated Master 4186 * modules), consuming events for client subscriptions, and constructing API 4187 * requests. 4188 */ 4189 /** @private */ 4190 (function (factory) { 4191 4192 4193 // Define as an AMD module if possible 4194 if ( typeof define === 'function' && define.amd ) 4195 { 4196 define('clientservices/ClientServices',["clientservices/MasterTunnel", 4197 "clientservices/MasterPublisher", 4198 "clientservices/Topics", 4199 "utilities/Utilities"], factory ); 4200 } 4201 4202 /* Define using browser globals otherwise 4203 * Prevent multiple instantiations if the script is loaded twice 4204 */ 4205 else 4206 { 4207 factory(finesse.clientservices.MasterTunnel, finesse.clientservices.MasterPublisher, finesse.clientservices.Topics, finesse.utilities.Utilities); 4208 } 4209 4210 }(function (MasterTunnel, MasterPublisher, Topics, Utilities) { 4211 4212 var ClientServices = (function () {/** @lends finesse.clientservices.ClientServices.prototype */ 4213 var 4214 4215 /** 4216 * Shortcut reference to the master tunnel 4217 * @private 4218 */ 4219 _tunnel, 4220 4221 _publisher, 4222 4223 /** 4224 * Shortcut reference to the finesse.utilities.Utilities singleton 4225 * This will be set by init() 4226 * @private 4227 */ 4228 _util, 4229 4230 /** 4231 * Shortcut reference to the gadgets.io object. 4232 * This will be set by init() 4233 * @private 4234 */ 4235 _io, 4236 4237 /** 4238 * Shortcut reference to the gadget pubsub Hub instance. 4239 * This will be set by init() 4240 * @private 4241 */ 4242 _hub, 4243 4244 /** 4245 * Logger object set externally by setLogger, defaults to nothing. 4246 * @private 4247 */ 4248 _logger = {}, 4249 4250 /** 4251 * Shortcut reference to the Topics class. 4252 * This will be set by init() 4253 * @private 4254 */ 4255 _topics, 4256 4257 /** 4258 * Config object needed to initialize this library 4259 * This must be set by init() 4260 * @private 4261 */ 4262 _config, 4263 4264 /** 4265 * @private 4266 * Whether or not this ClientService instance is a Master. 4267 */ 4268 _isMaster = false, 4269 4270 /** 4271 * @private 4272 * Whether the Client Services have been initiated yet. 4273 */ 4274 _inited = false, 4275 4276 /** 4277 * Stores the list of subscription IDs for all subscriptions so that it 4278 * could be retrieve for unsubscriptions. 4279 * @private 4280 */ 4281 _subscriptionID = {}, 4282 4283 /** 4284 * The possible states of the JabberWerx BOSH connection. 4285 * @private 4286 */ 4287 _STATUS = { 4288 CONNECTING: "connecting", 4289 CONNECTED: "connected", 4290 DISCONNECTED: "disconnected", 4291 DISCONNECTED_CONFLICT: "conflict", 4292 DISCONNECTED_UNAUTHORIZED: "unauthorized", 4293 DISCONNECTING: "disconnecting", 4294 RECONNECTING: "reconnecting", 4295 UNLOADING: "unloading", 4296 FAILING: "failing", 4297 RECOVERED: "recovered" 4298 }, 4299 4300 _failoverMode = false, 4301 4302 /** 4303 * Handler function to be invoked when BOSH connection is connecting. 4304 * @private 4305 */ 4306 _onConnectingHandler, 4307 4308 /** 4309 * Handler function to be invoked when BOSH connection is connected 4310 * @private 4311 */ 4312 _onConnectHandler, 4313 4314 /** 4315 * Handler function to be invoked when BOSH connection is disconnecting. 4316 * @private 4317 */ 4318 _onDisconnectingHandler, 4319 4320 /** 4321 * Handler function to be invoked when the BOSH is disconnected. 4322 * @private 4323 */ 4324 _onDisconnectHandler, 4325 4326 /** 4327 * Handler function to be invoked when the BOSH is reconnecting. 4328 * @private 4329 */ 4330 _onReconnectingHandler, 4331 4332 /** 4333 * Handler function to be invoked when the BOSH is unloading. 4334 * @private 4335 */ 4336 _onUnloadingHandler, 4337 4338 /** 4339 * Contains a cache of the latest connection info containing the current 4340 * state of the BOSH connection and the resource ID. 4341 * @private 4342 */ 4343 _connInfo, 4344 4345 /** 4346 * Keeps track of all the objects that need to be refreshed when we recover 4347 * due to our resilient connection. Only objects that we subscribe to will 4348 * be added to this list. 4349 * @private 4350 */ 4351 _refreshList = [], 4352 4353 /** 4354 * @private 4355 * Centralized logger.log method for external logger 4356 * @param {String} msg 4357 * Message to log 4358 */ 4359 _log = function (msg) { 4360 // If the external logger throws up, it stops here. 4361 try { 4362 if (_logger.log) { 4363 _logger.log("[ClientServices] " + msg); 4364 } 4365 } catch (e) { } 4366 }, 4367 4368 /** 4369 * Go through each object in the _refreshList and call its refresh() function 4370 * @private 4371 */ 4372 _refreshObjects = function () { 4373 var i; 4374 4375 // wipe out the explicit subscription list before we refresh objects 4376 if (_publisher) { 4377 _publisher.wipeout(); 4378 } 4379 4380 // refresh each item in the refresh list 4381 for (i = _refreshList.length - 1; i >= 0; i -= 1) { 4382 _log("Refreshing " + _refreshList[i].getRestUrl()); 4383 _refreshList[i].refresh(10); 4384 } 4385 }, 4386 4387 /** 4388 * Handler to process connection info publishes. 4389 * @param {Object} data 4390 * The connection info data object. 4391 * @param {String} data.status 4392 * The BOSH connection status. 4393 * @param {String} data.resourceID 4394 * The resource ID for the connection. 4395 * @private 4396 */ 4397 _connInfoHandler = function (data) { 4398 4399 //Invoke registered handler depending on status received. Due to the 4400 //request topic where clients can make request for the Master to publish 4401 //the connection info, there is a chance that duplicate connection info 4402 //events may be sent, so ensure that there has been a state change 4403 //before invoking the handlers. 4404 if (_connInfo === undefined || _connInfo.status !== data.status) { 4405 _connInfo = data; 4406 switch (data.status) { 4407 case _STATUS.CONNECTING: 4408 if (_isMaster && _onConnectingHandler) { 4409 _onConnectingHandler(); 4410 } 4411 break; 4412 case _STATUS.CONNECTED: 4413 if ((_isMaster || !_failoverMode) && _onConnectHandler) { 4414 _onConnectHandler(); 4415 } 4416 break; 4417 case _STATUS.DISCONNECTED: 4418 if (_isMaster && _onDisconnectHandler) { 4419 _onDisconnectHandler(); 4420 } 4421 break; 4422 case _STATUS.DISCONNECTED_CONFLICT: 4423 if (_isMaster && _onDisconnectHandler) { 4424 _onDisconnectHandler("conflict"); 4425 } 4426 break; 4427 case _STATUS.DISCONNECTED_UNAUTHORIZED: 4428 if (_isMaster && _onDisconnectHandler) { 4429 _onDisconnectHandler("unauthorized"); 4430 } 4431 break; 4432 case _STATUS.DISCONNECTING: 4433 if (_isMaster && _onDisconnectingHandler) { 4434 _onDisconnectingHandler(); 4435 } 4436 break; 4437 case _STATUS.RECONNECTING: 4438 if (_isMaster && _onReconnectingHandler) { 4439 _onReconnectingHandler(); 4440 } 4441 break; 4442 case _STATUS.UNLOADING: 4443 if (_isMaster && _onUnloadingHandler) { 4444 _onUnloadingHandler(); 4445 } 4446 break; 4447 case _STATUS.FAILING: 4448 if (!_isMaster) { 4449 // Stop 4450 _failoverMode = true; 4451 if (_onDisconnectHandler) { 4452 _onDisconnectHandler(); 4453 } 4454 } 4455 break; 4456 case _STATUS.RECOVERED: 4457 if (!_isMaster) { 4458 _failoverMode = false; 4459 if (_onConnectHandler) { 4460 _onConnectHandler(); 4461 } 4462 } 4463 // Whenever we are recovered, we need to refresh any objects 4464 // that are stored. 4465 _refreshObjects(); 4466 break; 4467 } 4468 } 4469 }, 4470 4471 /** 4472 * Ensure that ClientServices have been inited. 4473 * @private 4474 */ 4475 _isInited = function () { 4476 if (!_inited) { 4477 throw new Error("ClientServices needs to be inited."); 4478 } 4479 }, 4480 4481 /** 4482 * Have the client become the Master by initiating a tunnel to a shared 4483 * event BOSH connection. The Master is responsible for publishing all 4484 * events to the pubsub infrastructure. 4485 * @private 4486 */ 4487 _becomeMaster = function () { 4488 _tunnel = new MasterTunnel(_config.host, _config.scheme); 4489 _publisher = new MasterPublisher(_tunnel, _hub); 4490 _tunnel.init(_config.id, _config.password, _config.xmppDomain, _config.pubsubDomain, _config.resource); 4491 _isMaster = true; 4492 }, 4493 4494 /** 4495 * Make a request to the request channel to have the Master publish the 4496 * connection info object. 4497 * @private 4498 */ 4499 _makeConnectionInfoReq = function () { 4500 var data = { 4501 type: "ConnectionInfoReq", 4502 data: {}, 4503 invokeID: (new Date()).getTime() 4504 }; 4505 _hub.publish(_topics.REQUESTS, data); 4506 }, 4507 4508 /** 4509 * Utility method to register a handler which is associated with a 4510 * particular connection status. 4511 * @param {String} status 4512 * The connection status string. 4513 * @param {Function} handler 4514 * The handler to associate with a particular connection status. 4515 * @throws {Error} 4516 * If the handler provided is not a function. 4517 * @private 4518 */ 4519 _registerHandler = function (status, handler) { 4520 if (typeof handler === "function") { 4521 if (_connInfo && _connInfo.status === status) { 4522 handler(); 4523 } 4524 switch (status) { 4525 case _STATUS.CONNECTING: 4526 _onConnectingHandler = handler; 4527 break; 4528 case _STATUS.CONNECTED: 4529 _onConnectHandler = handler; 4530 break; 4531 case _STATUS.DISCONNECTED: 4532 _onDisconnectHandler = handler; 4533 break; 4534 case _STATUS.DISCONNECTING: 4535 _onDisconnectingHandler = handler; 4536 break; 4537 case _STATUS.RECONNECTING: 4538 _onReconnectingHandler = handler; 4539 break; 4540 case _STATUS.UNLOADING: 4541 _onUnloadingHandler = handler; 4542 break; 4543 } 4544 4545 } else { 4546 throw new Error("Callback is not a function"); 4547 } 4548 }; 4549 4550 return { 4551 4552 /** 4553 * @private 4554 * Adds an item to the list to be refreshed upon reconnect 4555 * @param {RestBase} object - rest object to be refreshed 4556 */ 4557 addToRefreshList: function (object) { 4558 _refreshList.push(object); 4559 }, 4560 4561 /** 4562 * @private 4563 * Removes the given item from the refresh list 4564 * @param {RestBase} object - rest object to be removed 4565 */ 4566 removeFromRefreshList: function (object) { 4567 var i; 4568 for (i = _refreshList.length - 1; i >= 0; i -= 1) { 4569 if (_refreshList[i] === object) { 4570 _refreshList.splice(i, 1); 4571 break; 4572 } 4573 } 4574 }, 4575 4576 /** 4577 * @private 4578 * The location of the tunnel HTML URL. 4579 * @returns {String} 4580 * The location of the tunnel HTML URL. 4581 */ 4582 getTunnelURL: function () { 4583 return _tunnel.getTunnelURL(); 4584 }, 4585 4586 /** 4587 * @private 4588 * Indicates whether the tunnel frame is loaded. 4589 * @returns {Boolean} 4590 * True if the tunnel frame is loaded, false otherwise. 4591 */ 4592 isTunnelLoaded: function () { 4593 return _tunnel.isTunnelLoaded(); 4594 }, 4595 4596 /** 4597 * @private 4598 * Indicates whether the ClientServices instance is a Master. 4599 * @returns {Boolean} 4600 * True if this instance of ClientServices is a Master, false otherwise. 4601 */ 4602 isMaster: function () { 4603 return _isMaster; 4604 }, 4605 4606 /** 4607 * @private 4608 * Get the resource ID. An ID is only available if the BOSH connection has 4609 * been able to connect successfully. 4610 * @returns {String} 4611 * The resource ID string. Null if the BOSH connection was never 4612 * successfully created and/or the resource ID has not been associated. 4613 */ 4614 getResourceID: function () { 4615 if (_connInfo !== undefined) { 4616 return _connInfo.resourceID; 4617 } 4618 return null; 4619 }, 4620 4621 /* 4622 getHub: function () { 4623 return _hub; 4624 }, 4625 */ 4626 /** 4627 * @private 4628 * Add a callback to be invoked when the BOSH connection is attempting 4629 * to connect. If the connection is already trying to connect, the 4630 * callback will be invoked immediately. 4631 * @param {Function} handler 4632 * An empty param function to be invoked on connecting. Only one 4633 * handler can be registered at a time. Handlers already registered 4634 * will be overwritten. 4635 */ 4636 registerOnConnectingHandler: function (handler) { 4637 _registerHandler(_STATUS.CONNECTING, handler); 4638 }, 4639 4640 /** 4641 * @private 4642 * Removes the on connecting callback that was registered. 4643 */ 4644 unregisterOnConnectingHandler: function () { 4645 _onConnectingHandler = undefined; 4646 }, 4647 4648 /** 4649 * @private 4650 * Add a callback to be invoked when the BOSH connection has been 4651 * established. If the connection has already been established, the 4652 * callback will be invoked immediately. 4653 * @param {Function} handler 4654 * An empty param function to be invoked on connect. Only one handler 4655 * can be registered at a time. Handlers already registered will be 4656 * overwritten. 4657 */ 4658 registerOnConnectHandler: function (handler) { 4659 _registerHandler(_STATUS.CONNECTED, handler); 4660 }, 4661 4662 /** 4663 * @private 4664 * Removes the on connect callback that was registered. 4665 */ 4666 unregisterOnConnectHandler: function () { 4667 _onConnectHandler = undefined; 4668 }, 4669 4670 /** 4671 * @private 4672 * Add a callback to be invoked when the BOSH connection goes down. If 4673 * the connection is already down, invoke the callback immediately. 4674 * @param {Function} handler 4675 * An empty param function to be invoked on disconnected. Only one 4676 * handler can be registered at a time. Handlers already registered 4677 * will be overwritten. 4678 */ 4679 registerOnDisconnectHandler: function (handler) { 4680 _registerHandler(_STATUS.DISCONNECTED, handler); 4681 }, 4682 4683 /** 4684 * @private 4685 * Removes the on disconnect callback that was registered. 4686 */ 4687 unregisterOnDisconnectHandler: function () { 4688 _onDisconnectHandler = undefined; 4689 }, 4690 4691 /** 4692 * @private 4693 * Add a callback to be invoked when the BOSH is currently disconnecting. If 4694 * the connection is already disconnecting, invoke the callback immediately. 4695 * @param {Function} handler 4696 * An empty param function to be invoked on disconnected. Only one 4697 * handler can be registered at a time. Handlers already registered 4698 * will be overwritten. 4699 */ 4700 registerOnDisconnectingHandler: function (handler) { 4701 _registerHandler(_STATUS.DISCONNECTING, handler); 4702 }, 4703 4704 /** 4705 * @private 4706 * Removes the on disconnecting callback that was registered. 4707 */ 4708 unregisterOnDisconnectingHandler: function () { 4709 _onDisconnectingHandler = undefined; 4710 }, 4711 4712 /** 4713 * @private 4714 * Add a callback to be invoked when the BOSH connection is attempting 4715 * to connect. If the connection is already trying to connect, the 4716 * callback will be invoked immediately. 4717 * @param {Function} handler 4718 * An empty param function to be invoked on connecting. Only one 4719 * handler can be registered at a time. Handlers already registered 4720 * will be overwritten. 4721 */ 4722 registerOnReconnectingHandler: function (handler) { 4723 _registerHandler(_STATUS.RECONNECTING, handler); 4724 }, 4725 4726 /** 4727 * @private 4728 * Removes the on reconnecting callback that was registered. 4729 */ 4730 unregisterOnReconnectingHandler: function () { 4731 _onReconnectingHandler = undefined; 4732 }, 4733 4734 /** 4735 * @private 4736 * Add a callback to be invoked when the BOSH connection is unloading 4737 * 4738 * @param {Function} handler 4739 * An empty param function to be invoked on connecting. Only one 4740 * handler can be registered at a time. Handlers already registered 4741 * will be overwritten. 4742 */ 4743 registerOnUnloadingHandler: function (handler) { 4744 _registerHandler(_STATUS.UNLOADING, handler); 4745 }, 4746 4747 /** 4748 * @private 4749 * Removes the on unloading callback that was registered. 4750 */ 4751 unregisterOnUnloadingHandler: function () { 4752 _onUnloadingHandler = undefined; 4753 }, 4754 4755 /** 4756 * @private 4757 * Proxy method for gadgets.io.makeRequest. The will be identical to gadgets.io.makeRequest 4758 * ClientServices will mixin the BASIC Auth string, locale, and host, since the 4759 * configuration is encapsulated in here anyways. 4760 * This removes the dependency 4761 * @param {String} url 4762 * The relative url to make the request to (the host from the passed in config will be 4763 * appended). It is expected that any encoding to the URL is already done. 4764 * @param {Function} handler 4765 * Callback handler for makeRequest to invoke when the response returns. 4766 * Completely passed through to gadgets.io.makeRequest 4767 * @param {Object} params 4768 * The params object that gadgets.io.makeRequest expects. Authorization and locale 4769 * headers are mixed in. 4770 */ 4771 makeRequest: function (url, handler, params) { 4772 var requestedScheme, scheme = "http"; 4773 4774 // ClientServices needs to be initialized with a config for restHost, auth, and locale 4775 _isInited(); 4776 4777 // Allow mixin of auth and locale headers 4778 params = params || {}; 4779 4780 // Override refresh interval to 0 instead of default 3600 as a way to workaround makeRequest 4781 // using GET http method because then the params are added to the url as query params, which 4782 // exposes the authorization string in the url. This is a placeholder until oauth comes in 4783 params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = params[gadgets.io.RequestParameters.REFRESH_INTERVAL] || 0; 4784 4785 params[gadgets.io.RequestParameters.HEADERS] = params[gadgets.io.RequestParameters.HEADERS] || {}; 4786 4787 // Add Basic auth to request header 4788 params[gadgets.io.RequestParameters.HEADERS].Authorization = "Basic " + _config.authorization; 4789 //Locale 4790 params[gadgets.io.RequestParameters.HEADERS].locale = _config.locale; 4791 4792 //Allow clients to override the scheme: 4793 // - If not specified => we use HTTP 4794 // - If null specified => we use _config.scheme 4795 // - Otherwise => we use whatever they provide 4796 requestedScheme = params.SCHEME; 4797 if (!(requestedScheme === undefined || requestedScheme === "undefined")) { 4798 if (requestedScheme === null) { 4799 scheme = _config.scheme; 4800 } else { 4801 scheme = requestedScheme; 4802 } 4803 } 4804 4805 _log("RequestedScheme: " + requestedScheme + "; Scheme: " + scheme); 4806 gadgets.io.makeRequest(encodeURI(scheme + "://" + _config.restHost + ":" + _config.localhostPort) + url, handler, params); 4807 }, 4808 4809 /** 4810 * @private 4811 * Utility function to make a subscription to a particular topic. Only one 4812 * callback function is registered to a particular topic at any time. 4813 * @param {String} topic 4814 * The full topic name. The topic name should follow the OpenAjax 4815 * convention using dot notation (ex: finesse.api.User.1000). 4816 * @param {Function} callback 4817 * The function that should be invoked with the data when an event 4818 * is delivered to the specific topic. 4819 * @returns {Boolean} 4820 * True if the subscription was made successfully and the callback was 4821 * been registered. False if the subscription already exist, the 4822 * callback was not overwritten. 4823 */ 4824 subscribe: function (topic, callback, disableDuringFailover) { 4825 _isInited(); 4826 4827 //Ensure that the same subscription isn't made twice. 4828 if (!_subscriptionID[topic]) { 4829 //Store the subscription ID using the topic name as the key. 4830 _subscriptionID[topic] = _hub.subscribe(topic, 4831 //Invoke the callback just with the data object. 4832 function (topic, data) { 4833 if (!disableDuringFailover || _isMaster || !_failoverMode) { 4834 // Halt all intermediate event processing while the master instance attempts to rebuild the connection. This only occurs: 4835 // - For RestBase objects (which pass the disableDuringFailover flag), so that intermediary events while recovering are hidden away until everything is all good 4836 // - We shouldn't be halting anything else because we have infrastructure requests that use the hub pub sub 4837 // - Master instance will get all events regardless, because it is responsible for managing failover 4838 // - If we are not in a failover mode, everything goes 4839 // _refreshObjects will reconcile anything that was missed once we are back in action 4840 callback(data); 4841 } 4842 }); 4843 return true; 4844 } 4845 return false; 4846 }, 4847 4848 /** 4849 * @private 4850 * Unsubscribe from a particular topic. 4851 * @param {String} topic 4852 * The full topic name. 4853 */ 4854 unsubscribe: function (topic) { 4855 _isInited(); 4856 4857 //Unsubscribe from the topic using the subscription ID recorded when 4858 //the subscription was made, then delete the ID from data structure. 4859 if (_subscriptionID[topic]) { 4860 _hub.unsubscribe(_subscriptionID[topic]); 4861 delete _subscriptionID[topic]; 4862 } 4863 }, 4864 4865 /** 4866 * @private 4867 * Make a request to the request channel to have the Master subscribe 4868 * to a node. 4869 * @param {String} node 4870 * The node to subscribe to. 4871 */ 4872 subscribeNode: function (node, handler) { 4873 if (handler && typeof handler !== "function") { 4874 throw new Error("ClientServices.subscribeNode: handler is not a function"); 4875 } 4876 4877 // Construct the request to send to MasterPublisher through the OpenAjax Hub 4878 var data = { 4879 type: "SubscribeNodeReq", 4880 data: {node: node}, 4881 invokeID: _util.generateUUID() 4882 }, 4883 responseTopic = _topics.RESPONSES + "." + data.invokeID, 4884 _this = this; 4885 4886 // We need to first subscribe to the response channel 4887 this.subscribe(responseTopic, function (rsp) { 4888 // Since this channel is only used for this singular request, 4889 // we are not interested anymore. 4890 // This is also critical to not leaking memory by having OpenAjax 4891 // store a bunch of orphaned callback handlers that enclose on 4892 // our entire ClientServices singleton 4893 _this.unsubscribe(responseTopic); 4894 if (handler) { 4895 handler(data.invokeID, rsp); 4896 } 4897 }); 4898 // Then publish the request on the request channel 4899 _hub.publish(_topics.REQUESTS, data); 4900 }, 4901 4902 /** 4903 * @private 4904 * Make a request to the request channel to have the Master unsubscribe 4905 * from a node. 4906 * @param {String} node 4907 * The node to unsubscribe from. 4908 */ 4909 unsubscribeNode: function (node, subid, handler) { 4910 if (handler && typeof handler !== "function") { 4911 throw new Error("ClientServices.unsubscribeNode: handler is not a function"); 4912 } 4913 4914 // Construct the request to send to MasterPublisher through the OpenAjax Hub 4915 var data = { 4916 type: "UnsubscribeNodeReq", 4917 data: { 4918 node: node, 4919 subid: subid 4920 }, 4921 invokeID: _util.generateUUID() 4922 }, 4923 responseTopic = _topics.RESPONSES + "." + data.invokeID, 4924 _this = this; 4925 4926 // We need to first subscribe to the response channel 4927 this.subscribe(responseTopic, function (rsp) { 4928 // Since this channel is only used for this singular request, 4929 // we are not interested anymore. 4930 // This is also critical to not leaking memory by having OpenAjax 4931 // store a bunch of orphaned callback handlers that enclose on 4932 // our entire ClientServices singleton 4933 _this.unsubscribe(responseTopic); 4934 if (handler) { 4935 handler(rsp); 4936 } 4937 }); 4938 // Then publish the request on the request channel 4939 _hub.publish(_topics.REQUESTS, data); 4940 }, 4941 4942 /** 4943 * @private 4944 * Make a request to the request channel to have the Master connect to the XMPP server via BOSH 4945 */ 4946 makeConnectionReq : function () { 4947 // Disallow others (non-masters) from administering BOSH connections that are not theirs 4948 if (_isMaster && _publisher) { 4949 _publisher.connect(_config.id, _config.password, _config.xmppDomain); 4950 } else { 4951 _log("F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq"); 4952 } 4953 }, 4954 4955 /** 4956 * @private 4957 * Set's the global logger for this Client Services instance. 4958 * @param {Object} logger 4959 * Logger object with the following attributes defined:<ul> 4960 * <li><b>log:</b> function (msg) to simply log a message 4961 * </ul> 4962 */ 4963 setLogger: function (logger) { 4964 // We want to check the logger coming in so we don't have to check every time it is called. 4965 if (logger && typeof logger === "object" && typeof logger.log === "function") { 4966 _logger = logger; 4967 } else { 4968 // We are resetting it to an empty object so that _logger.log in .log is falsy. 4969 _logger = {}; 4970 } 4971 }, 4972 4973 /** 4974 * @private 4975 * Centralized logger.log method for external logger 4976 * @param {String} msg 4977 * Message to log 4978 */ 4979 log: _log, 4980 4981 /** 4982 * @class 4983 * Allow clients to make Finesse API requests and consume Finesse events by 4984 * calling a set of exposed functions. The Services layer will do the dirty 4985 * work of establishing a shared BOSH connection (for designated Master 4986 * modules), consuming events for client subscriptions, and constructing API 4987 * requests. 4988 * 4989 * @constructs 4990 */ 4991 _fakeConstuctor: function () { 4992 /* This is here so we can document init() as a method rather than as a constructor. */ 4993 }, 4994 4995 /** 4996 * Initiates the Client Services with the specified config parameters. 4997 * Enabling the Client Services as Master will trigger the establishment 4998 * of a BOSH event connection. 4999 * @param {Object} config 5000 * Configuration object containing properties used for making REST requests:<ul> 5001 * <li><b>host:</b> The Finesse server IP/host as reachable from the browser 5002 * <li><b>restHost:</b> The Finesse API IP/host as reachable from the gadget container 5003 * <li><b>id:</b> The ID of the user. This is an optional param as long as the 5004 * appropriate authorization string is provided, otherwise it is 5005 * required.</li> 5006 * <li><b>password:</b> The password belonging to the user. This is an optional param as 5007 * long as the appropriate authorization string is provided, 5008 * otherwise it is required.</li> 5009 * <li><b>authorization:</b> The base64 encoded "id:password" authentication string. This 5010 * param is provided to allow the ability to hide the password 5011 * param. If provided, the id and the password extracted from this 5012 * string will be used over the config.id and config.password.</li> 5013 * </ul> 5014 * @throws {Error} If required constructor parameter is missing. 5015 * @example 5016 * finesse.clientservices.ClientServices.init(finesse.gadget.Config); 5017 */ 5018 init: function (config) { 5019 if (!_inited) { 5020 //Validate the properties within the config object if one is provided. 5021 if (!(typeof config === "object" && 5022 typeof config.host === "string" && config.host.length > 0 && config.restHost && 5023 (typeof config.authorization === "string" || 5024 (typeof config.id === "string" && 5025 typeof config.password === "string")))) { 5026 throw new Error("Config object contains invalid properties."); 5027 } 5028 5029 // Initialize configuration 5030 _config = config; 5031 5032 // Set shortcuts 5033 _util = Utilities; 5034 _topics = Topics; 5035 5036 //TODO: document when this is properly supported 5037 // Allows hub and io dependencies to be passed in. Currently only used for unit tests. 5038 _hub = config.hub || gadgets.Hub; 5039 _io = config.io || gadgets.io; 5040 5041 //If the authorization string is provided, then use that to 5042 //extract the ID and the password. Otherwise use the ID and 5043 //password from the respective ID and password params. 5044 if (_config.authorization) { 5045 var creds = _util.getCredentials(_config.authorization); 5046 _config.id = creds.id; 5047 _config.password = creds.password; 5048 } 5049 else { 5050 _config.authorization = _util.b64Encode( 5051 _config.id + ":" + _config.password); 5052 } 5053 5054 _inited = true; 5055 5056 if (_hub) { 5057 //Subscribe to receive connection information. Since it is possible that 5058 //the client comes up after the Master comes up, the client will need 5059 //to make a request to have the Master send the latest connection info. 5060 //It would be possible that all clients get connection info again. 5061 this.subscribe(_topics.EVENTS_CONNECTION_INFO, _connInfoHandler); 5062 _makeConnectionInfoReq(); 5063 } 5064 } 5065 5066 //Return the CS object for object chaining. 5067 return this; 5068 }, 5069 5070 /** 5071 * @private 5072 * Initializes the BOSH component of this ClientServices instance. This establishes 5073 * the BOSH connection and will trigger the registered handlers as the connection 5074 * status changes respectively:<ul> 5075 * <li>registerOnConnectingHandler</li> 5076 * <li>registerOnConnectHandler</li> 5077 * <li>registerOnDisconnectHandler</li> 5078 * <li>registerOnDisconnectingHandler</li> 5079 * <li>registerOnReconnectingHandler</li> 5080 * <li>registerOnUnloadingHandler</li> 5081 * <ul> 5082 * 5083 * @param {Object} config 5084 * An object containing the following (optional) handlers for the request:<ul> 5085 * <li><b>xmppDomain:</b> {String} The domain of the XMPP server. Available from the SystemInfo object. 5086 * This is used to construct the JID: user@domain.com</li> 5087 * <li><b>pubsubDomain:</b> {String} The pub sub domain where the pub sub service is running. 5088 * Available from the SystemInfo object. 5089 * This is used for creating or removing subscriptions.</li> 5090 * <li><b>resource:</b> {String} The resource to connect to the notification server with.</li> 5091 * </ul> 5092 */ 5093 initBosh: function (config) { 5094 //Validate the properties within the config object if one is provided. 5095 if (!(typeof config === "object" && typeof config.xmppDomain === "string" && typeof config.pubsubDomain === "string")) { 5096 throw new Error("Config object contains invalid properties."); 5097 } 5098 5099 // Mixin the required information for establishing the BOSH connection 5100 _config.xmppDomain = config.xmppDomain; 5101 _config.pubsubDomain = config.pubsubDomain; 5102 _config.resource = config.resource; 5103 5104 //Initiate Master launch sequence 5105 _becomeMaster(); 5106 }, 5107 5108 /** 5109 * @private 5110 * Sets the failover mode to either FAILING or RECOVERED. This will only occur in the master instance and is meant to be 5111 * used by a stereotypical failover monitor type module to notify non-master instances (i.e. in gadgets) 5112 * @param {Object} failoverMode 5113 * true if failing, false or something falsy when recovered 5114 */ 5115 setFailoverMode: function (failoverMode) { 5116 if (_isMaster) { 5117 _hub.publish(_topics.EVENTS_CONNECTION_INFO, { 5118 status: (failoverMode ? _STATUS.FAILING : _STATUS.RECOVERED) 5119 }); 5120 } 5121 }, 5122 5123 /** 5124 * @private 5125 * Private accessor used to inject mocked private dependencies for unit testing 5126 */ 5127 _getTestObj: function () { 5128 return { 5129 setPublisher: function (publisher) { 5130 _publisher = publisher; 5131 } 5132 }; 5133 } 5134 }; 5135 }()); 5136 5137 window.finesse = window.finesse || {}; 5138 window.finesse.clientservices = window.finesse.clientservices || {}; 5139 window.finesse.clientservices.ClientServices = ClientServices; 5140 5141 return ClientServices; 5142 5143 })); 5144 5145 /** 5146 * The following comment prevents JSLint errors concerning undefined global variables. 5147 * It tells JSLint that these identifiers are defined elsewhere. 5148 */ 5149 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */ 5150 5151 /** The following comment is to prevent jslint errors about 5152 * using variables before they are defined. 5153 */ 5154 /*global Handlebars */ 5155 5156 /** 5157 * JavaScript class to implement common notification 5158 * functionality. 5159 * 5160 * @requires Class 5161 * @requires finesse.FinesseBase 5162 */ 5163 /** @private */ 5164 (function (factory) { 5165 5166 5167 // Define as an AMD module if possible 5168 if ( typeof define === 'function' && define.amd ) 5169 { 5170 define('restservices/Notifier',['FinesseBase', 5171 'clientservices/ClientServices'], factory ); 5172 } 5173 5174 /* Define using browser globals otherwise 5175 * Prevent multiple instantiations if the script is loaded twice 5176 */ 5177 else 5178 { 5179 factory(finesse.FinesseBase, finesse.clientservices.ClientServices); 5180 } 5181 }(function (FinesseBase, ClientServices) { 5182 var Notifier = FinesseBase.extend({ 5183 /** 5184 * Initializes the notifier object. 5185 */ 5186 init : function () { 5187 this._super(); 5188 this._listenerCallback = []; 5189 }, 5190 5191 /** 5192 * Add a listener. 5193 * 5194 * @param callback_function 5195 * @param scope 5196 * is the callback function to add 5197 */ 5198 addListener : function (callback_function, scope) { 5199 var len = this._listenerCallback.length, i, cb, add = true; 5200 for (i = 0; i < len; i += 1) { 5201 cb = this._listenerCallback[i].callback; 5202 if (cb === callback_function) { 5203 // this callback already exists 5204 add = false; 5205 break; 5206 } 5207 } 5208 if (add) { 5209 this._listenerCallback.push({ "callback": this._isAFunction(callback_function), "scope": (scope || window) }); 5210 } 5211 }, 5212 5213 /** 5214 * Remove a listener. 5215 * 5216 * @param callback_function 5217 * is the callback function to remove 5218 * @return {Boolean} true if removed 5219 */ 5220 removeListener : function (callback_function) { 5221 5222 var result = false, len = this._listenerCallback.length, i, cb; 5223 for (i = len - 1; i >= 0; i -=1) { 5224 cb = this._listenerCallback[i].callback; 5225 if (cb === callback_function) { 5226 this._listenerCallback[i] = undefined; 5227 this._listenerCallback.splice(i, 1); 5228 result = true; 5229 break; 5230 } 5231 } 5232 5233 return result; 5234 }, 5235 5236 /** 5237 * Removes all listeners 5238 * @return {undefined} 5239 */ 5240 reset: function () { 5241 this._listenerCallback = []; 5242 }, 5243 5244 /** 5245 * Notify all listeners. 5246 * 5247 * @param obj 5248 * is the object that has changed 5249 */ 5250 notifyListeners : function (obj) { 5251 var len = this._listenerCallback.length, i, callbackFunction, scope; 5252 5253 for (i = 0; i < len; i += 1) { 5254 // Be sure that one bad callback does not prevent other listeners 5255 // from receiving. 5256 try { 5257 callbackFunction = this._listenerCallback[i].callback; 5258 scope = this._listenerCallback[i].scope; 5259 if (typeof callbackFunction === 'function') { 5260 callbackFunction.call(scope, obj); 5261 } 5262 } catch (err) { 5263 ClientServices.log("Exception caught: " + err); 5264 } 5265 } 5266 }, 5267 5268 /** 5269 * Gets a copy of the listeners. 5270 * @return changeListenerCopy (array of callbacks) 5271 */ 5272 getListeners : function () { 5273 var changeListenerCopy = [], len = this._listenerCallback.length, i; 5274 5275 for (i = 0; i < len; i += 1) { 5276 changeListenerCopy.push(this._listenerCallback[i].callback); 5277 } 5278 5279 return changeListenerCopy; 5280 }, 5281 5282 /** 5283 * Verifies that the handler is function. 5284 * @param handler to verify 5285 * @return the handler 5286 * @throws Error if not a function 5287 */ 5288 _isAFunction : function (handler) { 5289 if (handler === undefined || typeof handler === "function") { 5290 return handler; 5291 } else { 5292 throw new Error("handler must be a function"); 5293 } 5294 } 5295 }); 5296 5297 window.finesse = window.finesse || {}; 5298 window.finesse.restservices = window.finesse.restservices || {}; 5299 window.finesse.restservices.Notifier = Notifier; 5300 5301 /** @namespace JavaScript classes and methods that represent REST objects and collections. */ 5302 finesse.restservices = finesse.restservices || {}; 5303 5304 return Notifier; 5305 })); 5306 5307 /** 5308 * JavaScript base object that all REST objects should inherit 5309 * from because it encapsulates and provides the common functionality that 5310 * all REST objects need. 5311 * 5312 * @requires finesse.clientservices.ClientServices 5313 * @requires Class 5314 */ 5315 5316 /** @private */ 5317 (function (factory) { 5318 5319 5320 // Define as an AMD module if possible 5321 if ( typeof define === 'function' && define.amd ) 5322 { 5323 define('restservices/RestBase', ["FinesseBase", 5324 "utilities/Utilities", 5325 "restservices/Notifier", 5326 "clientservices/ClientServices", 5327 "clientservices/Topics"], factory ); 5328 } 5329 /* Define using browser globals otherwise 5330 * Prevent multiple instantiations if the script is loaded twice 5331 */ 5332 else 5333 { 5334 factory(finesse.FinesseBase, finesse.utilities.Utilities, 5335 finesse.restservices.Notifier, 5336 finesse.clientservices.ClientServices, 5337 finesse.clientservices.Topics); 5338 } 5339 }(function (FinesseBase, Utilities, Notifier, ClientServices, Topics) { 5340 5341 var RestBase = FinesseBase.extend(/** @lends finesse.restservices.RestBase.prototype */{ 5342 5343 doNotLog: false, 5344 5345 /** 5346 * Used by _processUpdate() and restRequest(). 5347 * Maps requestIds to object-wrapped callbacks passed to restRequest(), 5348 * so that one of the callbacks can be fired when a corresponding event is 5349 * received inside _processUpdate(). 5350 * @private 5351 */ 5352 _pendingCallbacks: {}, 5353 5354 /** 5355 * Gets the REST class for the current object. This object throws an 5356 * exception because subtype must implement. 5357 * @throws {Error} because subtype must implement 5358 * @private 5359 */ 5360 getRestClass: function () { 5361 throw new Error("getRestClass(): Not implemented in subtype."); 5362 }, 5363 5364 /** 5365 * Gets the REST type for the current object. This object throws an 5366 * exception because subtype must implement. 5367 * @throws {Error} because subtype must implement. 5368 * @private 5369 */ 5370 getRestType: function () { 5371 throw new Error("getRestType(): Not implemented in subtype."); 5372 }, 5373 5374 /** 5375 * Gets the node path for the current object. This object throws an 5376 * exception because subtype must implement. 5377 * @throws {Error} because subtype must implement. 5378 * @private 5379 */ 5380 getXMPPNodePath: function () { 5381 throw new Error("getXMPPNodePath(): Not implemented in subtype."); 5382 }, 5383 5384 /** 5385 * Boolean function that specifies whether the REST object supports 5386 * requests. True by default. Subclasses should override if false. 5387 * @private 5388 */ 5389 supportsRequests: true, 5390 5391 /** 5392 * Boolean function that specifies whether the REST object supports 5393 * subscriptions. True by default. Subclasses should override if false. 5394 * @private 5395 */ 5396 supportsSubscriptions: true, 5397 5398 /** 5399 * Boolean function that specifies whether the REST object should retain 5400 * a copy of the REST response. False by default. Subclasses should override if true. 5401 * @private 5402 */ 5403 keepRestResponse: false, 5404 5405 /** 5406 * Boolean function that specifies whether the REST object explicitly 5407 * subscribes. False by default. Subclasses should override if true. 5408 * @private 5409 */ 5410 explicitSubscription: false, 5411 5412 /** 5413 * Boolean function that specifies whether subscribing should be 5414 * automatically done at construction. Defaults to true. 5415 * This be overridden at object construction, not by implementing subclasses 5416 * @private 5417 */ 5418 autoSubscribe: true, 5419 5420 /** 5421 * Private reference to default logger 5422 * @private 5423 */ 5424 _logger: { 5425 log: ClientServices.log, 5426 error: ClientServices.log 5427 }, 5428 5429 /** 5430 * @class 5431 * JavaScript representation of a REST object. Also exposes methods to operate 5432 * on the object against the server. This object is typically extended into individual 5433 * REST Objects (like Dialog, User, etc...), and shouldn't be used directly. 5434 * 5435 * @constructor 5436 * @param {String} id 5437 * The ID that uniquely identifies the REST object. 5438 * @param {Object} callbacks 5439 * An object containing callbacks for instantiation and runtime 5440 * Callback to invoke upon successful instantiation, passes in REST object. 5441 * @param {Function} callbacks.onLoad(this) 5442 * Callback to invoke upon loading the data for the first time. 5443 * @param {Function} callbacks.onChange(this) 5444 * Callback to invoke upon successful update object (PUT) 5445 * @param {Function} callbacks.onAdd(this) 5446 * Callback to invoke upon successful update to add object (POST) 5447 * @param {Function} callbacks.onDelete(this) 5448 * Callback to invoke upon successful update to delete object (DELETE) 5449 * @param {Function} callbacks.onError(rsp) 5450 * Callback to invoke on update error (refresh or event) 5451 * as passed by finesse.restservices.RestBase.restRequest() 5452 * { 5453 * status: {Number} The HTTP status code returned 5454 * content: {String} Raw string of response 5455 * object: {Object} Parsed object of response 5456 * error: {Object} Wrapped exception that was caught 5457 * error.errorType: {String} Type of error that was caught 5458 * error.errorMessage: {String} Message associated with error 5459 * } 5460 * @param {RestBase} [restObj] 5461 * A RestBase parent object which this object has an association with. 5462 * @constructs 5463 */ 5464 init: function (options, callbacks, restObj) { 5465 /** 5466 * Initialize the base class 5467 */ 5468 var _this = this; 5469 5470 this._super(); 5471 5472 if (typeof options === "object") { 5473 this._id = options.id; 5474 this._restObj = options.parentObj; 5475 this.autoSubscribe = (options.autoSubscribe === false) ? false : true; 5476 this.doNotSubscribe = options.doNotSubscribe; 5477 this.doNotRefresh = this.doNotRefresh || options.doNotRefresh; 5478 callbacks = { 5479 onLoad: options.onLoad, 5480 onChange: options.onChange, 5481 onAdd: options.onAdd, 5482 onDelete: options.onDelete, 5483 onError: options.onError 5484 }; 5485 } else { 5486 this._id = options; 5487 this._restObj = restObj; 5488 } 5489 5490 // Common stuff 5491 5492 this._data = {}; 5493 5494 //Contains the full rest response to be processed by upper layers if needed 5495 this._restResponse = undefined; 5496 5497 this._lastUpdate = {}; 5498 5499 this._util = Utilities; 5500 5501 //Should be correctly initialized in either a window OR gadget context 5502 this._config = finesse.container.Config; 5503 5504 // Setup all the notifiers - change, load and error. 5505 this._changeNotifier = new Notifier(); 5506 this._loadNotifier = new Notifier(); 5507 this._addNotifier = new Notifier(); 5508 this._deleteNotifier = new Notifier(); 5509 this._errorNotifier = new Notifier(); 5510 5511 this._loaded = false; 5512 5513 // Protect against null dereferencing of options allowing its 5514 // (nonexistent) keys to be read as undefined 5515 callbacks = callbacks || {}; 5516 5517 this.addHandler('load', callbacks.onLoad); 5518 this.addHandler('change', callbacks.onChange); 5519 this.addHandler('add', callbacks.onAdd); 5520 this.addHandler('delete', callbacks.onDelete); 5521 this.addHandler('error', callbacks.onError); 5522 5523 // Attempt to get the RestType then synchronize 5524 try { 5525 this.getRestType(); 5526 5527 // Only subscribe if this REST object supports subscriptions 5528 // and autoSubscribe was not requested to be disabled as a construction option 5529 if (this.supportsSubscriptions && this.autoSubscribe && !this.doNotSubscribe) { 5530 this.subscribe({ 5531 success: function () { 5532 //TODO: figure out how to use Function.call() or Function.apply() here... 5533 //this is exactly the same as the below else case other than the scope of "this" 5534 if (typeof options === "object" && options.data) { 5535 if (!_this._processObject(_this._normalize(options.data))) { 5536 // notify of error if we fail to construct 5537 _this._errorNotifier.notifyListeners(_this); 5538 } 5539 } else { 5540 // Only subscribe if this REST object supports requests 5541 if (_this.supportsRequests) { 5542 _this._synchronize(); 5543 } 5544 } 5545 }, 5546 error: function (err) { 5547 _this._errorNotifier.notifyListeners(err); 5548 } 5549 }); 5550 } else { 5551 if (typeof options === "object" && options.data) { 5552 if (!this._processObject(this._normalize(options.data))) { 5553 // notify of error if we fail to construct 5554 this._errorNotifier.notifyListeners(this); 5555 } 5556 } else { 5557 // Only subscribe if this REST object supports requests 5558 if (this.supportsRequests) { 5559 this._synchronize(); 5560 } 5561 } 5562 } 5563 5564 } catch (err) { 5565 this._logger.error('id=' + this._id + ': ' + err); 5566 } 5567 }, 5568 5569 /** 5570 * Determines if the object has a particular property. 5571 * @param obj is the object to examine 5572 * @param property is the property to check for 5573 * @returns {Boolean} 5574 */ 5575 hasProperty: function (obj, prop) { 5576 return (obj !== null) && (obj.hasOwnProperty(prop)); 5577 }, 5578 5579 /** 5580 * Gets a property from the object. 5581 * @param obj is the object to examine 5582 * @param property is the property to get 5583 * @returns {Property Value} or {Null} if not found 5584 */ 5585 getProperty: function (obj, property) { 5586 var result = null; 5587 5588 if (this.hasProperty(obj, property) === false) { 5589 result = null; 5590 } else { 5591 result = obj[property]; 5592 } 5593 return result; 5594 }, 5595 5596 /** 5597 * Utility to extracts the ID from the specified REST URI. This is with the 5598 * assumption that the ID is always the last element in the URI after the 5599 * "/" delimiter. 5600 * @param {String} restUri 5601 * The REST uri (i.e. /finesse/api/User/1000). 5602 * @private 5603 */ 5604 _extractId: function (restObj) { 5605 var obj, restUri = "", strLoc; 5606 for (obj in restObj) { 5607 if (restObj.hasOwnProperty(obj)) { 5608 restUri = restObj[obj].uri; 5609 break; 5610 } 5611 } 5612 return Utilities.getId(restUri); 5613 }, 5614 5615 /** 5616 * Gets the data for this object. 5617 * @returns {Object} which is contained in data 5618 */ 5619 getData: function () { 5620 return this._data; 5621 }, 5622 5623 /** 5624 * Gets the complete REST response to the request made 5625 * @returns {Object} which is contained in data 5626 * @private 5627 */ 5628 getRestResponse: function () { 5629 return this._restResponse; 5630 }, 5631 5632 /** 5633 * The REST URL in which this object can be referenced. 5634 * @return {String} 5635 * The REST URI for this object. 5636 * @private 5637 */ 5638 getRestUrl: function () { 5639 var 5640 restObj = this._restObj, 5641 restUrl = ""; 5642 5643 //Prepend the base REST object if one was provided. 5644 if (restObj instanceof RestBase) { 5645 restUrl += restObj.getRestUrl(); 5646 } 5647 //Otherwise prepend with the default webapp name. 5648 else { 5649 restUrl += "/finesse/api"; 5650 } 5651 5652 //Append the REST type. 5653 restUrl += "/" + this.getRestType(); 5654 5655 //Append ID if it is not undefined, null, or empty. 5656 if (this._id) { 5657 restUrl += "/" + this._id; 5658 } 5659 return restUrl; 5660 }, 5661 5662 /** 5663 * Getter for the id of this RestBase 5664 * @returns {String} 5665 * The id of this RestBase 5666 */ 5667 getId: function () { 5668 return this._id; 5669 }, 5670 5671 /** 5672 * Synchronize this object with the server using REST GET request. 5673 * @private 5674 */ 5675 _synchronize: function (retries) { 5676 // Fetch this REST object 5677 if (typeof this._id === "string") { 5678 var _this = this, isLoaded = this._loaded, _RETRY_INTERVAL = 10 * 1000; 5679 5680 this._doGET( 5681 { 5682 success: function (rsp) { 5683 if (!_this._processResponse(rsp)) { 5684 if (retries > 0) { 5685 setTimeout(function () { 5686 _this._synchronize(retries - 1); 5687 }, _RETRY_INTERVAL); 5688 } else { 5689 _this._errorNotifier.notifyListeners(_this); 5690 } 5691 } else { 5692 // If this object was already "loaded" prior to 5693 // the _doGET request, then call the 5694 // changeNotifier 5695 if (isLoaded) { 5696 _this._changeNotifier.notifyListeners(_this); 5697 } 5698 } 5699 }, 5700 error: function (rsp) { 5701 if (retries > 0) { 5702 setTimeout(function () { 5703 _this._synchronize(retries - 1); 5704 }, _RETRY_INTERVAL); 5705 5706 } else { 5707 _this._errorNotifier.notifyListeners(rsp); 5708 } 5709 } 5710 } 5711 ); 5712 5713 } else { 5714 throw new Error("Can't construct a <" + this.getRestType() + "> due to invalid id type."); 5715 } 5716 }, 5717 5718 /** 5719 * Adds an handler to this object. 5720 * If notifierType is 'load' and the object has already loaded, the callback is invoked immediately 5721 * @param {String} notifierType 5722 * The type of notifier to add to ('load', 'change', 'add', 'delete', 'error') 5723 * @param {Function} callback 5724 * The function callback to invoke. 5725 * @example 5726 * // Handler for additions to the Dialogs collection object. 5727 * // When Dialog (a RestBase object) is created, add a change handler. 5728 * _handleDialogAdd = function(dialog) { 5729 * dialog.addHandler('change', _handleDialogChange); 5730 * } 5731 */ 5732 addHandler: function (notifierType, callback, scope) { 5733 var notifier = null; 5734 try { 5735 Utilities.validateHandler(callback); 5736 5737 notifier = this._getNotifierReference(notifierType); 5738 5739 notifier.addListener(callback, scope); 5740 5741 // If load handler is added and object has 5742 // already been loaded, invoke callback 5743 // immediately 5744 if (notifierType === 'load' && this._loaded) { 5745 callback.call((scope || window), this); 5746 } 5747 } catch (err) { 5748 this._logger.error('id=' + this._id + ': ' + err); 5749 } 5750 }, 5751 5752 /** 5753 * Removes a handler from this object. 5754 * @param {String} notifierType 5755 * The type of notifier to remove ('load', 'change', 'add', 'delete', 'error') 5756 * @param {Function} callback 5757 * The function to remove. 5758 */ 5759 removeHandler: function (notifierType, callback) { 5760 var notifier = null; 5761 try { 5762 Utilities.validateHandler(callback); 5763 5764 notifier = this._getNotifierReference(notifierType); 5765 5766 if (typeof(callback) === "undefined") 5767 { 5768 // Remove all listeners for the type 5769 notifier.reset(); 5770 } 5771 else 5772 { 5773 // Remove the specified listener 5774 finesse.utilities.Utilities.validateHandler(callback); 5775 notifier.removeListener(callback); 5776 } 5777 } catch (err) { 5778 this._logger.error('id=' + this._id + ': ' + err); 5779 } 5780 }, 5781 5782 /** 5783 * Utility method gating any operations that require complete instantiation 5784 * @throws Error 5785 * If this object was not fully instantiated yet 5786 * @returns {finesse.restservices.RestBase} 5787 * This RestBase object to allow cascading 5788 */ 5789 isLoaded: function () { 5790 if (!this._loaded) { 5791 throw new Error("Cannot operate on object that is not fully instantiated, use onLoad/onLoadError handlers"); 5792 } 5793 return this; // Allow cascading 5794 }, 5795 5796 5797 5798 /** 5799 * Force an update on this object. Since an asynchronous GET is performed, 5800 * it is necessary to have an onChange handler registered in order to be 5801 * notified when the response of this returns. 5802 * @param {Integer} retries 5803 * The number or retry attempts to make. 5804 * @returns {finesse.restservices.RestBase} 5805 * This RestBase object to allow cascading 5806 */ 5807 refresh: function (retries) { 5808 var _this = this; 5809 5810 if (this.explicitSubscription) { 5811 this._subscribeNode({ 5812 success: function () { 5813 //Disallow GETs if object doesn't support it. 5814 if (!_this.supportsRequests) { 5815 throw new Error("Object doesn't support request operations."); 5816 } 5817 5818 _this._synchronize(retries); 5819 5820 return this; // Allow cascading 5821 }, 5822 error: function (err) { 5823 _this._errorNotifier.notifyListeners(err); 5824 } 5825 }); 5826 } else { 5827 //Disallow GETs if object doesn't support it. 5828 if (!this.supportsRequests) { 5829 throw new Error("Object doesn't support request operations."); 5830 } 5831 5832 this._synchronize(retries); 5833 5834 return this; // Allow cascading 5835 } 5836 }, 5837 5838 /** 5839 * Utility method to validate against the known schema of this RestBase 5840 * @param {Object} obj 5841 * The object to validate 5842 * @returns {Boolean} 5843 * True if the object fits the schema of this object. This usually 5844 * means all required keys or nested objects are present. 5845 * False otherwise. 5846 * @private 5847 */ 5848 _validate: function (obj) { 5849 var valid = (typeof obj === "object" && this.hasProperty(obj, this.getRestType())); 5850 if (!valid) 5851 { 5852 this._logger.error(this.getRestType() + " failed validation! Does your JS REST class return the correct string from getRestType()?"); 5853 } 5854 return valid; 5855 }, 5856 5857 /** 5858 * Utility method to fetch this RestBase from the server 5859 * @param {finesse.interfaces.RequestHandlers} handlers 5860 * An object containing the handlers for the request 5861 * @returns {finesse.restservices.RestBase} 5862 * This RestBase object to allow cascading 5863 * @private 5864 */ 5865 _doGET: function (handlers) { 5866 this.restRequest(this.getRestUrl(), handlers); 5867 return this; // Allow cascading 5868 }, 5869 5870 /** 5871 * Common update event handler used by the pubsub callback closure. 5872 * Processes the update event then notifies listeners. 5873 * @param {Object} scope 5874 * An object containing callbacks to handle the asynchronous get 5875 * @param {Object} update 5876 * An object containing callbacks to handle the asynchronous get 5877 * @private 5878 */ 5879 _updateEventHandler: function (scope, update) { 5880 if (scope._processUpdate(update)) { 5881 switch (update.object.Update.event) { 5882 case "POST": 5883 scope._addNotifier.notifyListeners(scope); 5884 break; 5885 case "PUT": 5886 scope._changeNotifier.notifyListeners(scope); 5887 break; 5888 case "DELETE": 5889 scope._deleteNotifier.notifyListeners(scope); 5890 break; 5891 } 5892 } 5893 }, 5894 5895 /** 5896 * Utility method to create a callback to be given to OpenAjax to invoke when a message 5897 * is published on the topic of our REST URL (also XEP-0060 node). 5898 * This needs to be its own defined method so that subclasses can have their own implementation. 5899 * @returns {Function} callback(update) 5900 * The callback to be invoked when an update event is received. This callback will 5901 * process the update and notify listeners. 5902 * @private 5903 */ 5904 _createPubsubCallback: function () { 5905 var _this = this; 5906 return function (update) { 5907 _this._updateEventHandler(_this, update); 5908 }; 5909 }, 5910 5911 /** 5912 * Subscribe to pubsub infra using the REST URL as the topic name. 5913 * @param {finesse.interfaces.RequestHandlers} handlers 5914 * An object containing the handlers for the request 5915 * @private 5916 */ 5917 subscribe: function (callbacks) { 5918 // Only need to do a subscription to client pubsub. No need to trigger 5919 // a subscription on the Finesse server due to implicit subscribe (at 5920 // least for now). 5921 var _this = this, 5922 topic = Topics.getTopic(this.getRestUrl()), 5923 handlers, 5924 successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true); 5925 5926 callbacks = callbacks || {}; 5927 5928 handlers = { 5929 /** @private */ 5930 success: function () { 5931 // Add item to the refresh list in ClientServices to refresh if 5932 // we recover due to our resilient connection. However, do 5933 // not add if doNotRefresh flag is set. 5934 if (!_this.doNotRefresh) { 5935 ClientServices.addToRefreshList(_this); 5936 } 5937 5938 if (typeof callbacks.success === "function") { 5939 callbacks.success(); 5940 } 5941 }, 5942 /** @private */ 5943 error: function (err) { 5944 if (successful) { 5945 ClientServices.unsubscribe(topic); 5946 } 5947 5948 if (typeof callbacks.error === "function") { 5949 callbacks.error(err); 5950 } 5951 } 5952 }; 5953 5954 // Request a node subscription only if this object requires explicit subscriptions 5955 if (this.explicitSubscription === true) { 5956 this._subscribeNode(handlers); 5957 } else { 5958 if (successful) { 5959 this._subid = "OpenAjaxOnly"; 5960 handlers.success(); 5961 } else { 5962 handlers.error(); 5963 } 5964 } 5965 5966 return this; 5967 }, 5968 5969 /** 5970 * Unsubscribe to pubsub infra using the REST URL as the topic name. 5971 * @param {finesse.interfaces.RequestHandlers} handlers 5972 * An object containing the handlers for the request 5973 * @private 5974 */ 5975 unsubscribe: function (callbacks) { 5976 // Only need to do a subscription to client pubsub. No need to trigger 5977 // a subscription on the Finesse server due to implicit subscribe (at 5978 // least for now). 5979 var _this = this, 5980 topic = Topics.getTopic(this.getRestUrl()), 5981 handlers; 5982 5983 // no longer keep track of object to refresh on reconnect 5984 ClientServices.removeFromRefreshList(_this); 5985 5986 callbacks = callbacks || {}; 5987 5988 handlers = { 5989 /** @private */ 5990 success: function () { 5991 if (typeof callbacks.success === "function") { 5992 callbacks.success(); 5993 } 5994 }, 5995 /** @private */ 5996 error: function (err) { 5997 if (typeof callbacks.error === "function") { 5998 callbacks.error(err); 5999 } 6000 } 6001 }; 6002 6003 if (this._subid) { 6004 ClientServices.unsubscribe(topic); 6005 // Request a node unsubscribe only if this object requires explicit subscriptions 6006 if (this.explicitSubscription === true) { 6007 this._unsubscribeNode(handlers); 6008 } else { 6009 this._subid = undefined; 6010 handlers.success(); 6011 } 6012 } else { 6013 handlers.success(); 6014 } 6015 6016 return this; 6017 }, 6018 6019 /** 6020 * Private utility to perform node subscribe requests for explicit subscriptions 6021 * @param {finesse.interfaces.RequestHandlers} handlers 6022 * An object containing the handlers for the request 6023 * @private 6024 */ 6025 _subscribeNode: function (callbacks) { 6026 var _this = this; 6027 6028 // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined 6029 callbacks = callbacks || {}; 6030 6031 ClientServices.subscribeNode(this.getXMPPNodePath(), function (subid, err) { 6032 if (err) { 6033 if (typeof callbacks.error === "function") { 6034 callbacks.error(err); 6035 } 6036 } else { 6037 // Store the subid on a successful subscribe 6038 _this._subid = subid; 6039 if (typeof callbacks.success === "function") { 6040 callbacks.success(); 6041 } 6042 } 6043 }); 6044 }, 6045 6046 /** 6047 * Private utility to perform node unsubscribe requests for explicit subscriptions 6048 * @param {finesse.interfaces.RequestHandlers} handlers 6049 * An object containing the handlers for the request 6050 * @private 6051 */ 6052 _unsubscribeNode: function (callbacks) { 6053 var _this = this; 6054 6055 // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined 6056 callbacks = callbacks || {}; 6057 6058 ClientServices.unsubscribeNode(this.getXMPPNodePath(), this._subid, function (err) { 6059 _this._subid = undefined; 6060 if (err) { 6061 if (typeof callbacks.error === "function") { 6062 callbacks.error(err); 6063 } 6064 } else { 6065 if (typeof callbacks.success === "function") { 6066 callbacks.success(); 6067 } 6068 } 6069 }); 6070 }, 6071 6072 /** 6073 * Validate and store the object into the internal data store. 6074 * @param {Object} object 6075 * The JavaScript object that should match of schema of this REST object. 6076 * @returns {Boolean} 6077 * True if the object was validated and stored successfully. 6078 * @private 6079 */ 6080 _processObject: function (object) { 6081 if (this._validate(object)) { 6082 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here? 6083 6084 // If loaded for the first time, call the load notifiers. 6085 if (!this._loaded) { 6086 this._loaded = true; 6087 this._loadNotifier.notifyListeners(this); 6088 } 6089 6090 return true; 6091 } 6092 return false; 6093 }, 6094 6095 /** 6096 * Normalize the object to mitigate the differences between the backend 6097 * and what this REST object should hold. For example, the backend sends 6098 * send an event with the root property name being lower case. In order to 6099 * match the GET, the property should be normalized to an upper case. 6100 * @param {Object} object 6101 * The object which should be normalized. 6102 * @returns {Object} 6103 * Return the normalized object. 6104 * @private 6105 */ 6106 _normalize: function (object) { 6107 var 6108 restType = this.getRestType(), 6109 // Get the REST object name with first character being lower case. 6110 objRestType = restType.charAt(0).toLowerCase() + restType.slice(1); 6111 6112 // Normalize payload to match REST object. The payload for an update 6113 // use a lower case object name as oppose to upper case. Only normalize 6114 // if necessary. 6115 if (!this.hasProperty(object, restType) && this.hasProperty(object, objRestType)) { 6116 //Since the object is going to be modified, clone the object so that 6117 //it doesn't affect others (due to OpenAjax publishing to other 6118 //subscriber. 6119 object = jQuery.extend(true, {}, object); 6120 6121 object[restType] = object[objRestType]; 6122 delete(object[objRestType]); 6123 } 6124 return object; 6125 }, 6126 6127 /** 6128 * Utility method to process the response of a successful get 6129 * @param {Object} rsp 6130 * The response of a successful get 6131 * @returns {Boolean} 6132 * True if the update was successfully processed (the response object 6133 * passed the schema validation) and updated the internal data cache, 6134 * false otherwise. 6135 * @private 6136 */ 6137 _processResponse: function (rsp) { 6138 try { 6139 if (this.keepRestResponse) { 6140 this._restResponse = rsp.content; 6141 } 6142 return this._processObject(rsp.object); 6143 } 6144 catch (err) { 6145 this._logger.error(this.getRestType() + ': ' + err); 6146 } 6147 return false; 6148 }, 6149 6150 /** 6151 * Utility method to process the update notification. 6152 * @param {Object} update 6153 * The payload of an update notification. 6154 * @returns {Boolean} 6155 * True if the update was successfully processed (the update object 6156 * passed the schema validation) and updated the internal data cache, 6157 * false otherwise. 6158 * @private 6159 */ 6160 _processUpdate: function (update) { 6161 try { 6162 var updateObj, requestId, fakeResponse, receivedError; 6163 6164 // The backend will send the data object with a lower case. To be 6165 // consistent with what should be represented in this object, the 6166 // object name should be upper case. This will normalize the object. 6167 updateObj = this._normalize(update.object.Update.data); 6168 6169 // Store the last event. 6170 this._lastUpdate = update.object; 6171 6172 requestId = this._lastUpdate.Update ? this._lastUpdate.Update.requestId : undefined; 6173 6174 if (requestId && this._pendingCallbacks[requestId]) { 6175 6176 /* 6177 * The passed success/error callbacks are expecting to be passed an AJAX response, so construct 6178 * a simulated/"fake" AJAX response object from the information in the received event. 6179 * The constructed object should conform to the contract for response objects specified 6180 * in _createAjaxHandler(). 6181 */ 6182 fakeResponse = {}; 6183 6184 //The contract says that rsp.content should contain the raw text of the response so we simulate that here. 6185 //For some reason json2xml has trouble with the native update object, so we serialize a clone of it by 6186 //doing a parse(stringify(update)). 6187 fakeResponse.content = this._util.json2xml(gadgets.json.parse(gadgets.json.stringify(update))); 6188 6189 fakeResponse.object = {}; 6190 6191 if (updateObj.apiErrors && updateObj.apiErrors.apiError) { //Error case 6192 6193 //TODO: The lowercase -> uppercase ApiErrors translation method below is undesirable, can it be improved? 6194 receivedError = updateObj.apiErrors.apiError; 6195 fakeResponse.object.ApiErrors = {}; 6196 fakeResponse.object.ApiErrors.ApiError = {}; 6197 fakeResponse.object.ApiErrors.ApiError.ErrorData = receivedError.errorData || undefined; 6198 fakeResponse.object.ApiErrors.ApiError.ErrorMessage = receivedError.errorMessage || undefined; 6199 fakeResponse.object.ApiErrors.ApiError.ErrorType = receivedError.errorType || undefined; 6200 6201 /* 6202 * Since this is the error case, supply the error callback with a '400 BAD REQUEST' status code. We don't know what the real 6203 * status code should be since the event we're constructing fakeResponse from doesn't include a status code. 6204 * This is just to conform to the contract for the error callback in _createAjaxHandler(). 6205 **/ 6206 fakeResponse.status = 400; 6207 6208 } else { //Success case 6209 6210 fakeResponse.object = this._lastUpdate; 6211 6212 /* 6213 * Since this is the success case, supply the success callback with a '200 OK' status code. We don't know what the real 6214 * status code should be since the event we're constructing fakeResponse from doesn't include a status code. 6215 * This is just to conform to the contract for the success callback in _createAjaxHandler(). 6216 **/ 6217 fakeResponse.status = 200; 6218 } 6219 6220 try { 6221 6222 if (fakeResponse.object.ApiErrors && this._pendingCallbacks[requestId].error) { 6223 this._pendingCallbacks[requestId].error(fakeResponse); 6224 } 6225 // HTTP 202 is handled as a success, besides, we cannot infer that a non-error is a success. 6226 /*else if (this._pendingCallbacks[requestId].success) { 6227 this._pendingCallbacks[requestId].success(fakeResponse); 6228 }*/ 6229 6230 } catch (callbackErr) { 6231 6232 this._logger.error(this.getRestType() + ": Caught error while firing callback: " + callbackErr); 6233 6234 } 6235 6236 //Clean up _pendingCallbacks now that we fired a callback corresponding to the received requestId. 6237 delete this._pendingCallbacks[requestId]; 6238 6239 } else { 6240 this._logger.log(this.getRestType() + ": Received the following event with an invalid or unknown requestId:"); 6241 this._logger.log(gadgets.json.stringify(update)); 6242 } 6243 6244 return this._processObject(updateObj); 6245 } 6246 catch (err) { 6247 this._logger.error(this.getRestType() + ': ' + err); 6248 } 6249 return false; 6250 }, 6251 6252 /** 6253 * Utility method to create ajax response handler closures around the 6254 * provided callbacks. Callbacks should be passed through from .ajax(). 6255 * makeRequest is responsible for garbage collecting these closures. 6256 * @param {finesse.interfaces.RequestHandlers} handlers 6257 * An object containing the handlers for the request 6258 * @private 6259 */ 6260 _createAjaxHandler: function (options) { 6261 //We should not need to check this again since it has already been done in .restRequest() 6262 //options = options || {}; 6263 6264 //Get a reference to the parent User object 6265 var _this = this; 6266 6267 return function (rsp) { 6268 6269 var requestId, error = false, rspObj; 6270 6271 if (options.success || options.error) { 6272 rspObj = { 6273 status: rsp.rc, 6274 content: rsp.text 6275 }; 6276 6277 if (!_this.doNotLog) { 6278 _this._logger.log(_this.getRestType() + ": requestId='" + options.uuid + "', Returned with status=" + rspObj.status + ", content='" + rspObj.content + "'"); 6279 } 6280 6281 //Some responses may not have a body. 6282 if (rsp.text && rsp.text.length > 0) { 6283 try { 6284 rspObj.object = _this._util.xml2js(rsp.text); 6285 } catch (e) { 6286 error = true; 6287 rspObj.error = { 6288 errorType: "parseError", 6289 errorMessage: "Could not serialize XML: " + e 6290 }; 6291 } 6292 } else { 6293 rspObj.object = {}; 6294 } 6295 6296 if (!error && rspObj.status >= 200 && rspObj.status < 300) { 6297 if (options.success) { 6298 options.success(rspObj); 6299 } 6300 } else { 6301 if (options.error) { 6302 options.error(rspObj); 6303 } 6304 } 6305 6306 /* 6307 * If a synchronous error happened after a non-GET request (usually a validation error), we 6308 * need to clean up the request's entry in _pendingCallbacks since no corresponding event 6309 * will arrive later. The corresponding requestId should be present in the response headers. 6310 * 6311 * It appears Shindig changes the header keys to lower case, hence 'requestid' instead of 6312 * 'requestId' below. 6313 **/ 6314 if (rspObj.status !== 202 && rsp.headers && rsp.headers.requestid) { 6315 requestId = rsp.headers.requestid[0]; 6316 if (_this._pendingCallbacks[requestId]) { 6317 delete _this._pendingCallbacks[requestId]; 6318 } 6319 } 6320 } 6321 }; 6322 }, 6323 6324 /** 6325 * Utility method to make an asynchronous request 6326 * @param {String} url 6327 * The unencoded URL to which the request is sent (will be encoded) 6328 * @param {Object} options 6329 * An object containing additional options for the request. 6330 * @param {Object} options.content 6331 * An object to send in the content body of the request. Will be 6332 * serialized into XML before sending. 6333 * @param {String} options.method 6334 * The type of request. Defaults to "GET" when none is specified. 6335 * @param {Function} options.success(rsp) 6336 * A callback function to be invoked for a successful request. 6337 * { 6338 * status: {Number} The HTTP status code returned 6339 * content: {String} Raw string of response 6340 * object: {Object} Parsed object of response 6341 * } 6342 * @param {Function} options.error(rsp) 6343 * A callback function to be invoked for an unsuccessful request. 6344 * { 6345 * status: {Number} The HTTP status code returned 6346 * content: {String} Raw string of response 6347 * object: {Object} Parsed object of response 6348 * error: {Object} Wrapped exception that was caught 6349 * error.errorType: {String} Type of error that was caught 6350 * error.errorMessage: {String} Message associated with error 6351 * } 6352 * @private 6353 */ 6354 restRequest: function (url, options) { 6355 6356 var params, encodedUrl; 6357 6358 params = {}; 6359 6360 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 6361 options = options || {}; 6362 options.success = this._util.validateHandler(options.success); 6363 options.error = this._util.validateHandler(options.error); 6364 6365 // Request Headers 6366 params[gadgets.io.RequestParameters.HEADERS] = {}; 6367 6368 // HTTP method is a passthrough to gadgets.io.makeRequest, makeRequest defaults to GET 6369 params[gadgets.io.RequestParameters.METHOD] = options.method; 6370 6371 //true if this should be a GET request, false otherwise 6372 if (!options.method || options.method === "GET") { 6373 //Disable caching for GETs 6374 if (url.indexOf("?") > -1) { 6375 url += "&"; 6376 } else { 6377 url += "?"; 6378 } 6379 url += "nocache=" + this._util.currentTimeMillis(); 6380 } else { 6381 /** 6382 * If not GET, generate a requestID and add it to the headers, then wrap 6383 * callbacks into an object and store it in _pendingCallbacks. 6384 * If we receive a synchronous error response instead of a 202 as expected, 6385 * the AJAX handler will clean up _pendingCallbacks. 6386 **/ 6387 /* 6388 * TODO: Clean up _pendingCallbacks if an entry persists after a certain amount of time has passed. 6389 * In the block below, can store the current time (new Date().getTime()) alongside the 6390 * callbacks in the new _pendingCallbacks entry. Then iterate through a copty of _pendingCallbacks, 6391 * deleting all entries inside _pendingCallbacks that are older than a certain threshold (2 minutes for example.) 6392 * This solves a potential memory leak issue if we never receive an event for a given stored requestId; 6393 * we don't want to store unfired callbacks forever. 6394 */ 6395 /** @private */ 6396 options.uuid = this._util.generateUUID(); 6397 params[gadgets.io.RequestParameters.HEADERS].requestId = options.uuid; 6398 //By default, Shindig strips nearly all of the response headers, but this parameter tells Shindig 6399 //to send the headers through unmodified; we need to be able to read the 'requestId' header if we 6400 //get a synchronous error as a result of a non-GET request. (See the bottom of _createAjaxHandler().) 6401 params[gadgets.io.RequestParameters.GET_FULL_HEADERS] = "true"; 6402 this._pendingCallbacks[options.uuid] = {}; 6403 this._pendingCallbacks[options.uuid].success = options.success; 6404 this._pendingCallbacks[options.uuid].error = options.error; 6405 } 6406 6407 //debugger; 6408 // NAT: IGNORE this until you hit save after changing assignments, then 6409 // pause here and set window.errorOnRequest to true, step past the next line, 6410 // and then set it to false. True value will throw an error when saving assignments. 6411 encodedUrl = encodeURI(url) + (window.errorOnRestRequest ? "ERROR" : ""); 6412 6413 if (!this.doNotLog) { 6414 this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', Making REST request: method=" + (options.method || "GET") + ", url='" + encodedUrl + "'"); 6415 } 6416 6417 // Content Body 6418 if (typeof options.content === "object") { 6419 // Content Type 6420 params[gadgets.io.RequestParameters.HEADERS]["Content-Type"] = "application/xml"; 6421 // Content 6422 params[gadgets.io.RequestParameters.POST_DATA] = this._util.js2xml(options.content); 6423 6424 if (!this.doNotLog) { 6425 this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', POST_DATA='" + params[gadgets.io.RequestParameters.POST_DATA] + "'"); 6426 } 6427 } 6428 6429 ClientServices.makeRequest(encodedUrl, this._createAjaxHandler(options), params); 6430 }, 6431 6432 /** 6433 * Retrieves a reference to a particular notifierType. 6434 * @param notifierType is a string which indicates the notifier to retrieve 6435 * ('load', 'change', 'add', 'delete', 'error') 6436 * @return {Notifier} 6437 * @private 6438 */ 6439 _getNotifierReference: function (notifierType) { 6440 var notifierReference = null; 6441 if (notifierType === 'load') { 6442 notifierReference = this._loadNotifier; 6443 } else if (notifierType === 'change') { 6444 notifierReference = this._changeNotifier; 6445 } else if (notifierType === 'add') { 6446 notifierReference = this._addNotifier; 6447 } else if (notifierType === 'delete') { 6448 notifierReference = this._deleteNotifier; 6449 } else if (notifierType === 'error') { 6450 notifierReference = this._errorNotifier; 6451 } else { 6452 throw new Error("_getNotifierReference(): Trying to get unknown notifier(notifierType=" + notifierType + ")"); 6453 } 6454 6455 return notifierReference; 6456 } 6457 }); 6458 6459 window.finesse = window.finesse || {}; 6460 window.finesse.restservices = window.finesse.restservices || {}; 6461 window.finesse.restservices.RestBase = RestBase; 6462 6463 return RestBase; 6464 })); 6465 6466 /** The following comment is to prevent jslint errors about 6467 * using variables before they are defined. 6468 */ 6469 /*global finesse*/ 6470 6471 /** 6472 * JavaScript base object that all REST collection objects should 6473 * inherit from because it encapsulates and provides the common functionality 6474 * that all REST objects need. 6475 * 6476 * @requires finesse.clientservices.ClientServices 6477 * @requires Class 6478 * @requires finesse.FinesseBase 6479 * @requires finesse.restservices.RestBase 6480 */ 6481 6482 /** 6483 * @class 6484 * JavaScript representation of a REST collection object. 6485 * 6486 * @constructor 6487 * @param {Function} callbacks.onCollectionAdd(this) 6488 * Callback to invoke upon successful item addition to the collection. 6489 * @param {Function} callbacks.onCollectionDelete(this) 6490 * Callback to invoke upon successful item deletion from the collection. 6491 * @borrows finesse.restservices.RestBase as finesse.restservices.RestCollectionBase 6492 */ 6493 /** @private */ 6494 (function (factory) { 6495 6496 6497 // Define as an AMD module if possible 6498 if ( typeof define === 'function' && define.amd ) 6499 { 6500 define('restservices/RestCollectionBase', ['restservices/RestBase', 6501 'utilities/Utilities', 6502 'restservices/Notifier'], factory ); 6503 } 6504 /* Define using browser globals otherwise 6505 * Prevent multiple instantiations if the script is loaded twice 6506 */ 6507 else 6508 { 6509 factory(finesse.restservices.RestBase, finesse.utilities.Utilities, finesse.restservices.Notifier); 6510 } 6511 }(function (RestBase, Utilities, Notifier) { 6512 var RestCollectionBase = RestBase.extend(/** @lends finesse.restservices.RestCollectionBase.prototype */{ 6513 6514 /** 6515 * Boolean function that specifies whether the collection handles subscribing 6516 * and propagation of events for the individual REST object items the 6517 * collection holds. False by default. Subclasses should override if true. 6518 * @private 6519 */ 6520 supportsRestItemSubscriptions: false, 6521 6522 /** 6523 * Gets the constructor the individual items that make of the collection. 6524 * For example, a Dialogs collection object will hold a list of Dialog items. 6525 * @throws Error because subtype must implement. 6526 * @private 6527 */ 6528 getRestItemClass: function () { 6529 throw new Error("getRestItemClass(): Not implemented in subtype."); 6530 }, 6531 6532 /** 6533 * Gets the REST type of the individual items that make of the collection. 6534 * For example, a Dialogs collection object will hold a list of Dialog items. 6535 * @throws Error because subtype must implement. 6536 * @private 6537 */ 6538 getRestItemType: function () { 6539 throw new Error("getRestItemType(): Not implemented in subtype."); 6540 }, 6541 6542 /** 6543 * The base REST URL in which items this object contains can be referenced. 6544 * @return {String} 6545 * The REST URI for items this object contains. 6546 * @private 6547 */ 6548 getRestItemBaseUrl: function () { 6549 var 6550 restUrl = "/finesse/api"; 6551 6552 //Append the REST type. 6553 restUrl += "/" + this.getRestItemType(); 6554 6555 return restUrl; 6556 }, 6557 6558 /* 6559 * Creates a new object from the given data 6560 * @param data - data object 6561 * @private 6562 */ 6563 _objectCreator: function (data) { 6564 var objectId = this._extractId(data), 6565 newRestObj = this._collection[objectId], 6566 _this = this; 6567 6568 //Prevent duplicate entries into collection. 6569 if (!newRestObj) { 6570 //Create a new REST object using the subtype defined by the 6571 //overridden method. 6572 newRestObj = new (this.getRestItemClass())({ 6573 doNotSubscribe: this.handlesItemSubscription, 6574 doNotRefresh: this.handlesItemRefresh, 6575 id: objectId, 6576 data: data, 6577 onLoad: function (newObj) { 6578 //Normalize and add REST object to collection datastore. 6579 _this._collection[objectId] = newObj; 6580 _this._collectionAddNotifier.notifyListeners(newObj); 6581 _this.length += 1; 6582 } 6583 }); 6584 } 6585 else { 6586 //If entry already exist in collection, process the new event, 6587 //and notify all change listeners since an existing object has 6588 //change. This could happen in the case when the Finesse server 6589 //cycles, and sends a snapshot of the user's calls. 6590 newRestObj._processObject(data); 6591 newRestObj._changeNotifier.notifyListeners(newRestObj); 6592 } 6593 }, 6594 6595 /* 6596 * Deletes and object and notifies its handlers 6597 * @param data - data object 6598 * @private 6599 */ 6600 _objectDeleter: function (data) { 6601 var objectId = this._extractId(data), 6602 object = this._collection[objectId]; 6603 if (object) { 6604 //Even though this is a delete, let's make sure the object we are passing has got good data 6605 object._processObject(data); 6606 //Notify listeners and delete from internal datastore. 6607 this._collectionDeleteNotifier.notifyListeners(object); 6608 delete this._collection[objectId]; 6609 this.length -= 1; 6610 } 6611 }, 6612 6613 /** 6614 * Creates an anonymous function for notifiying error listeners of a particular object 6615 * data. 6616 * @param obj - the objects whose error listeners to notify 6617 * @returns {Function} 6618 * Callback for notifying of errors 6619 * @private 6620 */ 6621 _createErrorNotifier: function (obj) { 6622 return function (err) { 6623 obj._errorNotifier.notifyListeners(err); 6624 }; 6625 }, 6626 6627 /** 6628 * Replaces the collection with a refreshed list using the passed in 6629 * data. 6630 * @param data - data object (usually this._data) 6631 * @private 6632 */ 6633 _buildRefreshedCollection: function (data) { 6634 var i, dataObject, object, objectId, dataArray, newIds = [], foundFlag; 6635 if (data && this.getProperty(data, this.getRestItemType()) !== null) { 6636 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType())); 6637 } else { 6638 dataArray = []; 6639 } 6640 6641 // iterate through each item in the new data and add to or update collection 6642 for (i = 0; i < dataArray.length; i += 1) { 6643 dataObject = {}; 6644 dataObject[this.getRestItemType()] = dataArray[i]; 6645 objectId = this._extractId(dataObject); 6646 6647 this._objectCreator(dataObject); 6648 newIds.push(objectId); 6649 6650 // resubscribe if the object requires an explicit subscription 6651 object = this._collection[objectId]; 6652 if (this.handlesItemRefresh && object.explicitSubscription) { 6653 object._subscribeNode({ 6654 error: this._createErrorNotifier(object) 6655 }); 6656 } 6657 } 6658 6659 // now clean up items (if any) that were removed 6660 for (objectId in this._collection) { 6661 if (this._collection.hasOwnProperty(objectId)) { 6662 foundFlag = false; 6663 for (i = newIds.length - 1; i >= 0; i -= 1) { 6664 if (newIds[i] === objectId) { 6665 foundFlag = true; 6666 break; 6667 } 6668 } 6669 // did not find in updated list, so delete it 6670 if (!foundFlag) { 6671 this._objectDeleter({'data': this._collection[objectId]._data}); 6672 } 6673 } 6674 } 6675 }, 6676 6677 /** 6678 * The actual refresh operation, refactored out so we don't have to repeat code 6679 * @private 6680 */ 6681 _RESTRefresh: function () { 6682 var _this = this; 6683 this._doGET({ 6684 success: function(rsp) { 6685 if (_this._processResponse(rsp)) { 6686 _this._buildRefreshedCollection(_this._data); 6687 } else { 6688 _this._errorNotifier.notifyListeners(_this); 6689 } 6690 }, 6691 error: function(rsp) { 6692 _this._errorNotifier.notifyListeners(rsp); 6693 } 6694 }); 6695 }, 6696 6697 /** 6698 * Force an update on this object. Since an asynchronous GET is performed, 6699 * it is necessary to have an onChange handler registered in order to be 6700 * notified when the response of this returns. 6701 * @returns {finesse.restservices.RestBaseCollection} 6702 * This RestBaseCollection object to allow cascading 6703 */ 6704 refresh: function() { 6705 var _this = this, isLoaded = this._loaded; 6706 6707 // resubscribe if the collection requires an explicit subscription 6708 if (this.explicitSubscription) { 6709 this._subscribeNode({ 6710 success: function () { 6711 _this._RESTRefresh(); 6712 }, 6713 error: function (err) { 6714 _this._errorNotifier.notifyListeners(err); 6715 } 6716 }); 6717 } else { 6718 this._RESTRefresh(); 6719 } 6720 6721 return this; // Allow cascading 6722 }, 6723 6724 /** 6725 * @private 6726 * The _addHandlerCb and _deleteHandlerCb require that data be passed in the 6727 * format of an array of {(Object Type): object} objects. For example, a 6728 * queues object would return [{Queue: queue1}, {Queue: queue2}, ...]. 6729 * @param skipOuterObject If {true} is passed in for this param, then the "data" 6730 * property is returned instead of an object with the 6731 * data appended. 6732 * @return {Array} 6733 */ 6734 extractCollectionData: function (skipOuterObject) { 6735 var restObjs, 6736 obj, 6737 result = [], 6738 _this = this; 6739 6740 if (this._data) 6741 { 6742 restObjs = this._data[this.getRestItemType()]; 6743 6744 if (restObjs) 6745 { 6746 // check if there are multiple objects to pass 6747 if (!$.isArray(restObjs)) 6748 { 6749 restObjs = [restObjs]; 6750 } 6751 6752 // if so, create an object for each and add to result array 6753 $.each(restObjs, function (id, object) { 6754 if (skipOuterObject === true) 6755 { 6756 obj = object; 6757 } 6758 else 6759 { 6760 obj = {}; 6761 obj[_this.getRestItemType()] = object; 6762 } 6763 result.push(obj); 6764 }); 6765 } 6766 6767 } 6768 6769 return result; 6770 }, 6771 6772 /** 6773 * For Finesse, collections are handled uniquely on a POST and 6774 * doesn't necessary follow REST conventions. A POST on a collection 6775 * doesn't mean that the collection has been created, it means that an 6776 * item has been added to the collection. This function will generate 6777 * a closure which will handle this logic appropriately. 6778 * @param {Object} scope 6779 * The scope of where the callback should be invoked. 6780 * @private 6781 */ 6782 _addHandlerCb: function (scope) { 6783 return function (restItem) { 6784 var data = restItem.extractCollectionData(); 6785 6786 $.each(data, function (id, object) { 6787 scope._objectCreator(object); 6788 }); 6789 }; 6790 }, 6791 6792 /** 6793 * For Finesse, collections are handled uniquely on a DELETE and 6794 * doesn't necessary follow REST conventions. A DELETE on a collection 6795 * doesn't mean that the collection has been deleted, it means that an 6796 * item has been deleted from the collection. This function will generate 6797 * a closure which will handle this logic appropriately. 6798 * @param {Object} scope 6799 * The scope of where the callback should be invoked. 6800 * @private 6801 */ 6802 _deleteHandlerCb: function (scope) { 6803 return function (restItem) { 6804 var data = restItem.extractCollectionData(); 6805 6806 $.each(data, function (id, obj) { 6807 scope._objectDeleter(obj); 6808 }); 6809 }; 6810 }, 6811 6812 /** 6813 * Utility method to process the update notification for Rest Items 6814 * that are children of the collection whose events are published to 6815 * the collection's node. 6816 * @param {Object} update 6817 * The payload of an update notification. 6818 * @returns {Boolean} 6819 * True if the update was successfully processed (the update object 6820 * passed the schema validation) and updated the internal data cache, 6821 * false otherwise. 6822 * @private 6823 */ 6824 _processRestItemUpdate: function (update) { 6825 var object, objectId, updateObj = update.object.Update; 6826 6827 //Extract the ID from the source if the Update was an error. 6828 if (updateObj.data.apiErrors) { 6829 objectId = Utilities.getId(updateObj.source); 6830 } 6831 //Otherwise extract from the data object itself. 6832 else { 6833 objectId = this._extractId(updateObj.data); 6834 } 6835 6836 object = this._collection[objectId]; 6837 if (object) { 6838 if (object._processUpdate(update)) { 6839 switch (updateObj.event) { 6840 case "POST": 6841 object._addNotifier.notifyListeners(object); 6842 break; 6843 case "PUT": 6844 object._changeNotifier.notifyListeners(object); 6845 break; 6846 case "DELETE": 6847 object._deleteNotifier.notifyListeners(object); 6848 break; 6849 } 6850 } 6851 } 6852 }, 6853 6854 /** 6855 * SUBCLASS IMPLEMENTATION (override): 6856 * For collections, this callback has the additional responsibility of passing events 6857 * of collection item updates to the item objects themselves. The collection needs to 6858 * do this because item updates are published to the collection's node. 6859 * @returns {Function} 6860 * The callback to be invoked when an update event is received 6861 * @private 6862 */ 6863 _createPubsubCallback: function () { 6864 var _this = this; 6865 return function (update) { 6866 //If the source of the update is our REST URL, this means the collection itself is modified 6867 if (update.object.Update.source === _this.getRestUrl()) { 6868 _this._updateEventHandler(_this, update); 6869 } else { 6870 //Otherwise, it is safe to assume that if we got an event on our topic, it must be a 6871 //rest item update of one of our children that was published on our node (OpenAjax topic) 6872 _this._processRestItemUpdate(update); 6873 } 6874 }; 6875 }, 6876 6877 /** 6878 * @class 6879 * This is the base collection object. 6880 * 6881 * @constructs 6882 * @augments finesse.restservices.RestBase 6883 * @see finesse.restservices.Contacts 6884 * @see finesse.restservices.Dialogs 6885 * @see finesse.restservices.PhoneBooks 6886 * @see finesse.restservices.Queues 6887 * @see finesse.restservices.WorkflowActions 6888 * @see finesse.restservices.Workflows 6889 * @see finesse.restservices.WrapUpReasons 6890 */ 6891 _fakeConstuctor: function () { 6892 /* This is here to hide the real init constructor from the public docs */ 6893 }, 6894 6895 /** 6896 * @private 6897 * @param {Object} options 6898 * An object with the following properties:<ul> 6899 * <li><b>id:</b> The id of the object being constructed</li> 6900 * <li><b>onCollectionAdd(this): (optional)</b> when an object is added to this collection</li> 6901 * <li><b>onCollectionDelete(this): (optional)</b> when an object is removed from this collection</li> 6902 * <li><b>onLoad(this): (optional)</b> when the collection is successfully loaded from the server</li> 6903 * <li><b>onChange(this): (optional)</b> when an update notification of the collection is received. 6904 * This does not include adding and deleting members of the collection</li> 6905 * <li><b>onAdd(this): (optional)</b> when a notification that the collection is created is received</li> 6906 * <li><b>onDelete(this): (optional)</b> when a notification that the collection is deleted is received</li> 6907 * <li><b>onError(rsp): (optional)</b> if loading of the collection fails, invoked with the error response object:<ul> 6908 * <li><b>status:</b> {Number} The HTTP status code returned</li> 6909 * <li><b>content:</b> {String} Raw string of response</li> 6910 * <li><b>object:</b> {Object} Parsed object of response</li> 6911 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 6912 * <li><b>errorType:</b> {String} Type of error that was caught</li> 6913 * <li><b>errorMessage:</b> {String} Message associated with error</li> 6914 * </ul></li> 6915 * </ul></li> 6916 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 6917 **/ 6918 init: function (options) { 6919 6920 options = options || {}; 6921 options.id = ""; 6922 6923 //Make internal datastore collection to hold a list of objects. 6924 this._collection = {}; 6925 this.length = 0; 6926 6927 //Collections will have additional callbacks that will be invoked when 6928 //an item has been added/deleted. 6929 this._collectionAddNotifier = new Notifier(); 6930 this._collectionDeleteNotifier = new Notifier(); 6931 6932 //Initialize the base class. 6933 this._super(options); 6934 6935 this.addHandler('collectionAdd', options.onCollectionAdd); 6936 this.addHandler('collectionDelete', options.onCollectionDelete); 6937 6938 //For Finesse, collections are handled uniquely on a POST/DELETE and 6939 //doesn't necessary follow REST conventions. A POST on a collection 6940 //doesn't mean that the collection has been created, it means that an 6941 //item has been added to the collection. A DELETE means that an item has 6942 //been removed from the collection. Due to this, we are attaching 6943 //special callbacks to the add/delete that will handle this logic. 6944 this.addHandler("add", this._addHandlerCb(this)); 6945 this.addHandler("delete", this._deleteHandlerCb(this)); 6946 }, 6947 6948 /** 6949 * Returns the collection. 6950 * @returns {Object} 6951 * The collection as an object 6952 */ 6953 getCollection: function () { 6954 //TODO: is this safe? or should we instead return protected functions such as .each(function)? 6955 return this._collection; 6956 }, 6957 6958 /** 6959 * Utility method to build the internal collection data structure (object) based on provided data 6960 * @param {Object} data 6961 * The data to build the internal collection from 6962 * @private 6963 */ 6964 _buildCollection: function (data) { 6965 var i, object, objectId, dataArray; 6966 if (data && this.getProperty(data, this.getRestItemType()) !== null) { 6967 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType())); 6968 for (i = 0; i < dataArray.length; i += 1) { 6969 6970 object = {}; 6971 object[this.getRestItemType()] = dataArray[i]; 6972 objectId = this._extractId(object); 6973 this._collection[objectId] = new (this.getRestItemClass())({ 6974 doNotSubscribe: this.handlesItemSubscription, 6975 doNotRefresh: this.handlesItemRefresh, 6976 id: objectId, 6977 data: object 6978 }); 6979 this.length += 1; 6980 } 6981 } 6982 }, 6983 6984 /** 6985 * Called to know whether to include an item in the _collection and _data. Return false to keep it, true to filter out (discard) it. 6986 * Override this in subclasses if you need only object with certain attribute values. 6987 * @param {Object} item Item to test. 6988 * @return {Boolean} False to keep, true to filter out (discard); 6989 */ 6990 _filterOutItem: function (item) { 6991 return false; 6992 }, 6993 6994 /** 6995 * Validate and store the object into the internal data store. 6996 * SUBCLASS IMPLEMENTATION (override): 6997 * Performs collection specific logic to _buildCollection internally based on provided data 6998 * @param {Object} object 6999 * The JavaScript object that should match of schema of this REST object. 7000 * @returns {Boolean} 7001 * True if the object was validated and stored successfully. 7002 * @private 7003 */ 7004 _processObject: function (object) { 7005 var i, 7006 restItemType = this.getRestItemType(), 7007 items; 7008 if (this._validate(object)) { 7009 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here? 7010 7011 // If a subclass has overriden _filterOutItem then we'll need to run through the items and remove them 7012 if (this._data) 7013 { 7014 items = this._data[restItemType]; 7015 7016 if (typeof(items) !== "undefined") 7017 { 7018 if (typeof(items.length) === "undefined") 7019 { 7020 // Single object 7021 if (this._filterOutItem(items)) 7022 { 7023 this._data[restItemType] = items = []; 7024 } 7025 7026 } 7027 else 7028 { 7029 // filter out objects 7030 for (i = items.length - 1; i !== -1; i = i - 1) 7031 { 7032 if (this._filterOutItem(items[i])) 7033 { 7034 items.splice(i, 1); 7035 } 7036 } 7037 } 7038 } 7039 } 7040 7041 // If loaded for the first time, call the load notifiers. 7042 if (!this._loaded) { 7043 this._buildCollection(this._data); 7044 this._loaded = true; 7045 this._loadNotifier.notifyListeners(this); 7046 } 7047 7048 return true; 7049 7050 } 7051 return false; 7052 }, 7053 7054 /** 7055 * Retrieves a reference to a particular notifierType. 7056 * @param {String} notifierType 7057 * Specifies the notifier to retrieve (load, change, error, add, delete) 7058 * @return {Notifier} The notifier object. 7059 */ 7060 _getNotifierReference: function (notifierType) { 7061 var notifierReference; 7062 7063 try { 7064 //Use the base method to get references for load/change/error. 7065 notifierReference = this._super(notifierType); 7066 } catch (err) { 7067 //Check for add/delete 7068 if (notifierType === "collectionAdd") { 7069 notifierReference = this._collectionAddNotifier; 7070 } else if (notifierType === "collectionDelete") { 7071 notifierReference = this._collectionDeleteNotifier; 7072 } else { 7073 //Rethrow exception from base class. 7074 throw err; 7075 } 7076 } 7077 return notifierReference; 7078 } 7079 }); 7080 7081 window.finesse = window.finesse || {}; 7082 window.finesse.restservices = window.finesse.restservices || {}; 7083 window.finesse.restservices.RestCollectionBase = RestCollectionBase; 7084 7085 return RestCollectionBase; 7086 })); 7087 7088 /** 7089 * JavaScript representation of the Finesse Dialog object. 7090 * 7091 * @requires finesse.clientservices.ClientServices 7092 * @requires Class 7093 * @requires finesse.FinesseBase 7094 * @requires finesse.restservices.RestBase 7095 */ 7096 7097 /** @private */ 7098 (function (factory) { 7099 7100 7101 // Define as an AMD module if possible 7102 if ( typeof define === 'function' && define.amd ) 7103 { 7104 define('restservices/Dialog', ['restservices/RestBase', 7105 'utilities/Utilities'], factory ); 7106 } 7107 /* Define using browser globals otherwise 7108 * Prevent multiple instantiations if the script is loaded twice 7109 */ 7110 else 7111 { 7112 factory(finesse.restservices.RestBase, finesse.utilities.Utilities); 7113 } 7114 }(function (RestBase, Utilities) { 7115 var Dialog = RestBase.extend(/** @lends finesse.restservices.Dialog.prototype */{ 7116 7117 /** 7118 * @class 7119 * A Dialog is an attempted connection between or among multiple participants, 7120 * for example, a regular phone call, a conference, or a silent monitor session. 7121 * 7122 * @augments finesse.restservices.RestBase 7123 * @constructs 7124 */ 7125 _fakeConstuctor: function () { 7126 /* This is here to hide the real init constructor from the public docs */ 7127 }, 7128 7129 /** 7130 * @private 7131 * 7132 * @param {Object} options 7133 * An object with the following properties:<ul> 7134 * <li><b>id:</b> The id of the object being constructed</li> 7135 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 7136 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 7137 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 7138 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 7139 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 7140 * <li><b>status:</b> {Number} The HTTP status code returned</li> 7141 * <li><b>content:</b> {String} Raw string of response</li> 7142 * <li><b>object:</b> {Object} Parsed object of response</li> 7143 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 7144 * <li><b>errorType:</b> {String} Type of error that was caught</li> 7145 * <li><b>errorMessage:</b> {String} Message associated with error</li> 7146 * </ul></li> 7147 * </ul></li> 7148 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 7149 **/ 7150 init: function (options) { 7151 this._super(options); 7152 }, 7153 7154 /** 7155 * @private 7156 * Gets the REST class for the current object - this is the Dialog class. 7157 * @returns {Object} The Dialog class. 7158 */ 7159 getRestClass: function () { 7160 return Dialog; 7161 }, 7162 7163 /** 7164 * @private 7165 * The constant for agent device. 7166 */ 7167 _agentDeviceType: "AGENT_DEVICE", 7168 7169 /** 7170 * @private 7171 * Gets the REST type for the current object - this is a "Dialog". 7172 * @returns {String} The Dialog string. 7173 */ 7174 getRestType: function () { 7175 return "Dialog"; 7176 }, 7177 7178 /** 7179 * @private 7180 * Override default to indicate that this object doesn't support making 7181 * requests. 7182 */ 7183 supportsRequests: false, 7184 7185 /** 7186 * @private 7187 * Override default to indicate that this object doesn't support subscriptions. 7188 */ 7189 supportsSubscriptions: false, 7190 7191 /** 7192 * Getter for the from address. 7193 * @returns {String} The from address. 7194 */ 7195 getFromAddress: function () { 7196 this.isLoaded(); 7197 return this.getData().fromAddress; 7198 }, 7199 7200 /** 7201 * Getter for the to address. 7202 * @returns {String} The to address. 7203 */ 7204 getToAddress: function () { 7205 this.isLoaded(); 7206 return this.getData().toAddress; 7207 }, 7208 7209 /** 7210 * Getter for the media type. 7211 * @returns {String} The media type. 7212 */ 7213 getMediaType: function () { 7214 this.isLoaded(); 7215 return this.getData().mediaType; 7216 }, 7217 7218 /** 7219 * @private 7220 * Getter for the uri. 7221 * @returns {String} The uri. 7222 */ 7223 getDialogUri: function () { 7224 this.isLoaded(); 7225 return this.getData().uri; 7226 }, 7227 7228 /** 7229 * Getter for the callType. 7230 * @deprecated Use getMediaProperties().callType instead. 7231 * @returns {String} The callType. 7232 */ 7233 getCallType: function () { 7234 this.isLoaded(); 7235 return this.getData().mediaProperties.callType; 7236 }, 7237 7238 /** 7239 * Getter for the DNIS. This is usually the actual number dialed. 7240 * @deprecated Use getMediaProperties().DNIS instead. 7241 * @returns {String} The callType. 7242 */ 7243 getDNIS: function () { 7244 this.isLoaded(); 7245 return this.getData().mediaProperties.DNIS; 7246 }, 7247 7248 /** 7249 * Getter for the Dialog state. 7250 * @returns {String} The Dialog state. 7251 */ 7252 getState: function () { 7253 this.isLoaded(); 7254 return this.getData().state; 7255 }, 7256 7257 /** 7258 * Retrieves a list of participants within the Dialog object. 7259 * @returns {Object} Array list of participants. 7260 */ 7261 getParticipants: function () { 7262 this.isLoaded(); 7263 var participants = this.getData().participants.Participant; 7264 //Due to the nature of the XML->JSO converter library, a single 7265 //element in the XML array will be considered to an object instead of 7266 //a real array. This will handle those cases to ensure that an array is 7267 //always returned. 7268 7269 return Utilities.getArray(participants); 7270 }, 7271 7272 /** 7273 * gets the participant timer counters 7274 * 7275 * @param {String} participantExt Extension of participant. 7276 * @returns {Object} which contains state, startTime, and stateChangeTime fields 7277 */ 7278 getParticipantTimerCounters : function (participantExt) { 7279 var part, participantTimerCounters = {}, idx, participants; 7280 7281 participants = this.getParticipants(); 7282 7283 7284 //Loop through all the participants and find the right participant (based on participantExt) 7285 for(idx=0;idx<participants.length;idx=idx+1) 7286 { 7287 part = participants[idx]; 7288 7289 if (part.mediaAddress === participantExt) 7290 { 7291 participantTimerCounters.startTime= part.startTime; 7292 participantTimerCounters.stateChangeTime= part.stateChangeTime; 7293 participantTimerCounters.state= part.state; 7294 break; 7295 } 7296 } 7297 7298 return participantTimerCounters; 7299 }, 7300 7301 /** 7302 * Determines the droppable participants. A droppable participant is a participant that is an agent extension. 7303 * (It is not a CTI Route Point, IVR Port, or the caller) 7304 * 7305 * @param {String} filterExtension used to remove a single extension from the list 7306 * @returns {Array} Participants which is an array of all participants which can be dropped. 7307 */ 7308 getDroppableParticipants: function (filterExtension) { 7309 this.isLoaded(); 7310 var droppableParticipants = [], participants, index, idx, filterExtensionToRemove = "", callStateOk, part; 7311 7312 participants = this.getParticipants(); 7313 7314 if (filterExtension) 7315 { 7316 filterExtensionToRemove = filterExtension; 7317 } 7318 7319 //Loop through all the participants to remove non-agents & remove filterExtension 7320 //We could have removed filterExtension using splice, but we have to iterate through 7321 //the list anyway. 7322 for(idx=0;idx<participants.length;idx=idx+1) 7323 { 7324 part = participants[idx]; 7325 7326 //Skip the filterExtension 7327 if (part.mediaAddress !== filterExtensionToRemove) 7328 { 7329 callStateOk = this._isParticipantStateDroppable(part); 7330 7331 //Remove non-agents & make sure callstate 7332 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType) 7333 { 7334 droppableParticipants.push(part); 7335 } 7336 } 7337 } 7338 7339 return Utilities.getArray(droppableParticipants); 7340 }, 7341 7342 _isParticipantStateDroppable : function (part) 7343 { 7344 var isParticipantStateDroppable = false; 7345 if (part.state === Dialog.ParticipantStates.ACTIVE || part.state === Dialog.ParticipantStates.ACCEPTED || part.state === Dialog.ParticipantStates.HELD) 7346 { 7347 isParticipantStateDroppable = true; 7348 } 7349 7350 return isParticipantStateDroppable; 7351 }, 7352 7353 /** 7354 * Is the participant droppable 7355 * 7356 * @param {String} participantExt Extension of participant. 7357 * @returns {Boolean} True is droppable. 7358 */ 7359 isParticipantDroppable : function (participantExt) { 7360 var droppableParticipants = null, isDroppable = false, idx, part, callStateOk; 7361 7362 droppableParticipants = this.getDroppableParticipants(); 7363 7364 if (droppableParticipants) 7365 { 7366 for(idx=0;idx<droppableParticipants.length;idx=idx+1) 7367 { 7368 part = droppableParticipants[idx]; 7369 7370 if (part.mediaAddress === participantExt) 7371 { 7372 callStateOk = this._isParticipantStateDroppable(part); 7373 7374 //Remove non-agents & make sure callstate 7375 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType) 7376 { 7377 isDroppable = true; 7378 break; 7379 } 7380 } 7381 } 7382 } 7383 7384 return isDroppable; 7385 }, 7386 7387 /** 7388 * Retrieves a list of media properties from the dialog object. 7389 * @returns {Object} Map of call variables; names mapped to values. 7390 * Variables may include the following:<ul> 7391 * <li>dialedNumber: The number dialed. 7392 * <li>callType: The type of call. Call types include:<ul> 7393 * <li>ACD_IN 7394 * <li>PREROUTE_ACD_IN 7395 * <li>PREROUTE_DIRECT_AGENT 7396 * <li>TRANSFER 7397 * <li>OTHER_IN 7398 * <li>OUT 7399 * <li>AGENT_INSIDE 7400 * <li>CONSULT 7401 * <li>CONFERENCE 7402 * <li>SUPERVISOR_MONITOR 7403 * <li>OUTBOUND 7404 * <li>OUTBOUND_PREVIEW</ul> 7405 * <li>DNIS: The DNIS provided. For routed calls, this is the route point. 7406 * <li>wrapUpReason: A description of the call. 7407 * <li>Call Variables, by name. The name indicates whether it is a call variable or ECC variable. 7408 * Call variable names start with callVariable#, where # is 1-10. ECC variable names (both scalar and array) are prepended with "user". 7409 * ECC variable arrays include an index enclosed within square brackets located at the end of the ECC array name. 7410 * <li>The following call variables provide additional details about an Outbound Option call:<ul> 7411 * <li>BACampaign 7412 * <li>BAAccountNumber 7413 * <li>BAResponse 7414 * <li>BAStatus<ul> 7415 * <li>PREDICTIVE_OUTBOUND: A predictive outbound call. 7416 * <li>PROGRESSIVE_OUTBOUND: A progressive outbound call. 7417 * <li>PREVIEW_OUTBOUND_RESERVATION: Agent is reserved for a preview outbound call. 7418 * <li>PREVIEW_OUTBOUND: Agent is on a preview outbound call.</ul> 7419 * <li>BADialedListID 7420 * <li>BATimeZone 7421 * <li>BABuddyName</ul></ul> 7422 * 7423 */ 7424 getMediaProperties: function () { 7425 var mpData, currentMediaPropertiesMap, thisMediaPropertiesJQuery; 7426 7427 this.isLoaded(); 7428 7429 // We have to convert to jQuery object to do a proper compare 7430 thisMediaPropertiesJQuery = jQuery(this.getData().mediaProperties); 7431 7432 if ((this._lastMediaPropertiesJQuery !== undefined) 7433 && (this._lastMediaPropertiesMap !== undefined) 7434 && (this._lastMediaPropertiesJQuery.is(thisMediaPropertiesJQuery))) { 7435 7436 return this._lastMediaPropertiesMap; 7437 } 7438 7439 currentMediaPropertiesMap = {}; 7440 7441 mpData = this.getData().mediaProperties; 7442 7443 if (mpData) { 7444 if (mpData.callvariables && mpData.callvariables.CallVariable) { 7445 jQuery.each(mpData.callvariables.CallVariable, function (i, callVariable) { 7446 currentMediaPropertiesMap[callVariable.name] = callVariable.value; 7447 }); 7448 } 7449 7450 jQuery.each(mpData, function (key, value) { 7451 if (key !== 'callvariables') { 7452 currentMediaPropertiesMap[key] = value; 7453 } 7454 }); 7455 } 7456 7457 this._lastMediaPropertiesMap = currentMediaPropertiesMap; 7458 this._lastMediaPropertiesJQuery = thisMediaPropertiesJQuery; 7459 7460 return this._lastMediaPropertiesMap; 7461 }, 7462 7463 /** 7464 * Retrieves information about the currently scheduled callback, if any. 7465 * @returns {Object} If no callback has been set, will return undefined. If 7466 * a callback has been set, it will return a map with one or more of the 7467 * following entries, depending on what values have been set. 7468 * callbackTime - the callback time, if it has been set. 7469 * callbackNumber - the callback number, if it has been set. 7470 */ 7471 getCallbackInfo: function() { 7472 this.isLoaded(); 7473 return this.getData().scheduledCallbackInfo; 7474 }, 7475 7476 /** 7477 * @private 7478 * Invoke a request to the server given a content body and handlers. 7479 * 7480 * @param {Object} contentBody 7481 * A JS object containing the body of the action request. 7482 * @param {finesse.interfaces.RequestHandlers} handlers 7483 * An object containing the handlers for the request 7484 */ 7485 _makeRequest: function (contentBody, handlers) { 7486 // Protect against null dereferencing of options allowing its 7487 // (nonexistent) keys to be read as undefined 7488 handlers = handlers || {}; 7489 7490 this.restRequest(this.getRestUrl(), { 7491 method: 'PUT', 7492 success: handlers.success, 7493 error: handlers.error, 7494 content: contentBody 7495 }); 7496 }, 7497 7498 /** 7499 * Invoke a consult call out to a destination. 7500 * 7501 * @param {String} mediaAddress 7502 * The media address of the user performing the consult call. 7503 * @param {String} toAddress 7504 * The destination address of the consult call. 7505 * @param {finesse.interfaces.RequestHandlers} handlers 7506 * An object containing the handlers for the request 7507 */ 7508 makeConsultCall: function (mediaAddress, toAddress, handlers) { 7509 this.isLoaded(); 7510 var contentBody = {}; 7511 contentBody[this.getRestType()] = { 7512 "targetMediaAddress": mediaAddress, 7513 "toAddress": toAddress, 7514 "requestedAction": Dialog.Actions.CONSULT_CALL 7515 }; 7516 this._makeRequest(contentBody, handlers); 7517 return this; // Allow cascading 7518 }, 7519 7520 /** 7521 * Invoke a single step transfer request. 7522 * 7523 * @param {String} mediaAddress 7524 * The media address of the user performing the single step transfer. 7525 * @param {String} toAddress 7526 * The destination address of the single step transfer. 7527 * @param {finesse.interfaces.RequestHandlers} handlers 7528 * An object containing the handlers for the request 7529 */ 7530 initiateDirectTransfer: function (mediaAddress, toAddress, handlers) { 7531 this.isLoaded(); 7532 var contentBody = {}; 7533 contentBody[this.getRestType()] = { 7534 "targetMediaAddress": mediaAddress, 7535 "toAddress": toAddress, 7536 "requestedAction": Dialog.Actions.TRANSFER_SST 7537 }; 7538 this._makeRequest(contentBody, handlers); 7539 return this; // Allow cascading 7540 }, 7541 7542 /** 7543 * Update this dialog's wrap-up reason. 7544 * 7545 * @param {String} wrapUpReason 7546 * The new wrap-up reason for this dialog 7547 * @param {finesse.interfaces.RequestHandlers} handlers 7548 * An object containing the handlers for the request 7549 */ 7550 updateWrapUpReason: function (wrapUpReason, options) 7551 { 7552 this.isLoaded(); 7553 var mediaProperties = 7554 { 7555 "wrapUpReason": wrapUpReason 7556 }; 7557 7558 options = options || {}; 7559 options.content = {}; 7560 options.content[this.getRestType()] = 7561 { 7562 "mediaProperties": mediaProperties, 7563 "requestedAction": Dialog.Actions.UPDATE_CALL_DATA 7564 }; 7565 options.method = "PUT"; 7566 this.restRequest(this.getRestUrl(), options); 7567 7568 return this; 7569 }, 7570 7571 /** 7572 * Invoke a request to server based on the action given. 7573 * @param {String} mediaAddress 7574 * The media address of the user performing the action. 7575 * @param {finesse.restservices.Dialog.Actions} action 7576 * The action string indicating the action to invoke on dialog. 7577 * @param {finesse.interfaces.RequestHandlers} handlers 7578 * An object containing the handlers for the request 7579 */ 7580 requestAction: function (mediaAddress, action, handlers) { 7581 this.isLoaded(); 7582 var contentBody = {}; 7583 contentBody[this.getRestType()] = { 7584 "targetMediaAddress": mediaAddress, 7585 "requestedAction": action 7586 }; 7587 this._makeRequest(contentBody, handlers); 7588 return this; // Allow cascading 7589 }, 7590 7591 /** 7592 * Wrapper around "requestAction" to request PARTICIPANT_DROP action. 7593 * 7594 * @param targetMediaAddress is the address to drop 7595 * @param {finesse.interfaces.RequestHandlers} handlers 7596 * An object containing the handlers for the request 7597 */ 7598 dropParticipant: function (targetMediaAddress, handlers) { 7599 this.requestAction(targetMediaAddress, Dialog.Actions.PARTICIPANT_DROP, handlers); 7600 }, 7601 7602 /** 7603 * Invoke a request to server to send DTMF digit tones. 7604 * @param {String} mediaAddress 7605 * @param {String} action 7606 * The action string indicating the action to invoke on dialog. 7607 * @param {finesse.interfaces.RequestHandlers} handlers 7608 * An object containing the handlers for the request 7609 */ 7610 sendDTMFRequest: function (mediaAddress, handlers, digit) { 7611 this.isLoaded(); 7612 var contentBody = {}; 7613 contentBody[this.getRestType()] = { 7614 "targetMediaAddress": mediaAddress, 7615 "requestedAction": "SEND_DTMF", 7616 "actionParams": { 7617 "ActionParam": { 7618 "name": "dtmfString", 7619 "value": digit 7620 } 7621 } 7622 }; 7623 this._makeRequest(contentBody, handlers); 7624 return this; // Allow cascading 7625 }, 7626 7627 /** 7628 * Invoke a request to server to set the time for a callback. 7629 * @param {String} mediaAddress 7630 * @param {String} callbackTime 7631 * The requested time for the callback, in YYYY-MM-DDTHH:MM format 7632 * (ex: 2013-12-24T23:59) 7633 * @param {finesse.interfaces.RequestHandlers} handlers 7634 * An object containing the handlers for the request 7635 */ 7636 updateCallbackTime: function (mediaAddress, callbackTime, handlers) { 7637 this.isLoaded(); 7638 var contentBody = {}; 7639 contentBody[this.getRestType()] = { 7640 "targetMediaAddress": mediaAddress, 7641 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK, 7642 "actionParams": { 7643 "ActionParam": { 7644 "name": "callbackTime", 7645 "value": callbackTime 7646 } 7647 } 7648 }; 7649 this._makeRequest(contentBody, handlers); 7650 return this; // Allow cascading 7651 }, 7652 7653 /** 7654 * Invoke a request to server to set the number for a callback. 7655 * @param {String} mediaAddress 7656 * @param {String} callbackNumber 7657 * The requested number to call for the callback 7658 * @param {finesse.interfaces.RequestHandlers} handlers 7659 * An object containing the handlers for the request 7660 */ 7661 updateCallbackNumber: function (mediaAddress, callbackNumber, handlers) { 7662 this.isLoaded(); 7663 var contentBody = {}; 7664 contentBody[this.getRestType()] = { 7665 "targetMediaAddress": mediaAddress, 7666 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK, 7667 "actionParams": { 7668 "ActionParam": { 7669 "name": "callbackNumber", 7670 "value": callbackNumber 7671 } 7672 } 7673 }; 7674 this._makeRequest(contentBody, handlers); 7675 return this; // Allow cascading 7676 }, 7677 7678 /** 7679 * Invoke a request to server to cancel a callback. 7680 * @param {String} mediaAddress 7681 * @param {finesse.interfaces.RequestHandlers} handlers 7682 * An object containing the handlers for the request 7683 */ 7684 cancelCallback: function (mediaAddress, handlers) { 7685 this.isLoaded(); 7686 var contentBody = {}; 7687 contentBody[this.getRestType()] = { 7688 "targetMediaAddress": mediaAddress, 7689 "requestedAction": Dialog.Actions.CANCEL_SCHEDULED_CALLBACK 7690 }; 7691 this._makeRequest(contentBody, handlers); 7692 return this; // Allow cascading 7693 }, 7694 7695 /** 7696 * Invoke a request to server to reclassify the call type. 7697 * @param {String} mediaAddress 7698 * @param {String} callbackNumber 7699 * The requested number to call for the callback 7700 * @param {finesse.interfaces.RequestHandlers} handlers 7701 * An object containing the handlers for the request 7702 */ 7703 reclassifyCall: function (mediaAddress, classification, handlers) { 7704 this.isLoaded(); 7705 var contentBody = {}; 7706 contentBody[this.getRestType()] = { 7707 "targetMediaAddress": mediaAddress, 7708 "requestedAction": Dialog.Actions.RECLASSIFY, 7709 "actionParams": { 7710 "ActionParam": { 7711 "name": "outboundClassification", 7712 "value": classification 7713 } 7714 } 7715 }; 7716 this._makeRequest(contentBody, handlers); 7717 return this; // Allow cascading 7718 } 7719 7720 7721 }); 7722 7723 Dialog.Actions = /** @lends finesse.restservices.Dialog.Actions.prototype */ { 7724 /** 7725 * Drops the Participant from the Dialog. 7726 */ 7727 DROP: "DROP", 7728 /** 7729 * Answers a Dialog. 7730 */ 7731 ANSWER: "ANSWER", 7732 /** 7733 * Holds the Dialog. 7734 */ 7735 HOLD: "HOLD", 7736 /** 7737 * Barges into a Call Dialog. 7738 */ 7739 BARGE_CALL: "BARGE_CALL", 7740 /** 7741 * Allow as Supervisor to Drop a Participant from the Dialog. 7742 */ 7743 PARTICIPANT_DROP: "PARTICIPANT_DROP", 7744 /** 7745 * Makes a new Call Dialog. 7746 */ 7747 MAKE_CALL: "MAKE_CALL", 7748 /** 7749 * Retrieves a Dialog that is on Hold. 7750 */ 7751 RETRIEVE: "RETRIEVE", 7752 /** 7753 * Sets the time or number for a callback. Can be 7754 * either a new callback, or updating an existing one. 7755 */ 7756 UPDATE_SCHEDULED_CALLBACK: "UPDATE_SCHEDULED_CALLBACK", 7757 /** 7758 * Cancels a callback. 7759 */ 7760 CANCEL_SCHEDULED_CALLBACK: "CANCEL_SCHEDULED_CALLBACK", 7761 /** 7762 * Initiates a Consult Call. 7763 */ 7764 CONSULT_CALL: "CONSULT_CALL", 7765 /** 7766 * Initiates a Transfer of a Dialog. 7767 */ 7768 TRANSFER: "TRANSFER", 7769 /** 7770 * Initiates a Single-Step Transfer of a Dialog. 7771 */ 7772 TRANSFER_SST: "TRANSFER_SST", 7773 /** 7774 * Initiates a Conference of a Dialog. 7775 */ 7776 CONFERENCE: "CONFERENCE", 7777 /** 7778 * Changes classification for a call 7779 */ 7780 RECLASSIFY: "RECLASSIFY", 7781 /** 7782 * Updates data on a Call Dialog. 7783 */ 7784 UPDATE_CALL_DATA: "UPDATE_CALL_DATA", 7785 /** 7786 * Initiates a Recording on a Call Dialog. 7787 */ 7788 START_RECORDING : "START_RECORDING", 7789 /** 7790 * Sends DTMF (dialed digits) to a Call Dialog. 7791 */ 7792 DTMF : "SEND_DTMF", 7793 /** 7794 * Accepts a Dialog that is being Previewed. 7795 */ 7796 ACCEPT: "ACCEPT", 7797 /** 7798 * Rejects a Dialog. 7799 */ 7800 REJECT: "REJECT", 7801 /** 7802 * Closes a Dialog. 7803 */ 7804 CLOSE : "CLOSE", 7805 /** 7806 * @class Set of action constants for a Dialog. These should be used for 7807 * {@link finesse.restservices.Dialog#requestAction}. 7808 * @constructs 7809 */ 7810 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 7811 }; 7812 7813 Dialog.States = /** @lends finesse.restservices.Dialog.States.prototype */ { 7814 /** 7815 * Indicates that the call is ringing at a device. 7816 */ 7817 ALERTING: "ALERTING", 7818 /** 7819 * Indicates that the phone is off the hook at a device. 7820 */ 7821 INITIATING: "INITIATING", 7822 /** 7823 * Indicates that the dialog has a least one active participant. 7824 */ 7825 ACTIVE: "ACTIVE", 7826 /** 7827 * Indicates that the dialog has no active participants. 7828 */ 7829 DROPPED: "DROPPED", 7830 /** 7831 * Indicates that the phone is dialing at the device. 7832 */ 7833 INITIATED: "INITIATED", 7834 /** 7835 * Indicates that the dialog has failed. 7836 * @see Dialog.ReasonStates 7837 */ 7838 FAILED: "FAILED", 7839 /** 7840 * Indicates that the user has accepted an OUTBOUND_PREVIEW dialog. 7841 */ 7842 ACCEPTED: "ACCEPTED", 7843 /** 7844 * @class Possible Dialog State constants. 7845 * The State flow of a typical in-bound Dialog is as follows: INITIATING, INITIATED, ALERTING, ACTIVE, DROPPED. 7846 * @constructs 7847 */ 7848 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 7849 }; 7850 7851 Dialog.ParticipantStates = /** @lends finesse.restservices.Dialog.ParticipantStates.prototype */ { 7852 /** 7853 * Indicates that an incoming call is ringing on the device. 7854 */ 7855 ALERTING: "ALERTING", 7856 /** 7857 * Indicates that an outgoing call, not yet active, exists on the device. 7858 */ 7859 INITIATING: "INITIATING", 7860 /** 7861 * Indicates that the participant is active on the call. 7862 */ 7863 ACTIVE: "ACTIVE", 7864 /** 7865 * Indicates that the participant has dropped from the call. 7866 */ 7867 DROPPED: "DROPPED", 7868 /** 7869 * Indicates that the participant has held their connection to the call. 7870 */ 7871 HELD: "HELD", 7872 /** 7873 * Indicates that the phone is dialing at a device. 7874 */ 7875 INITIATED: "INITIATED", 7876 /** 7877 * Indicates that the call failed. 7878 * @see Dialog.ReasonStates 7879 */ 7880 FAILED: "FAILED", 7881 /** 7882 * Indicates that the participant is not in active state on the call, but is wrapping up after the participant has dropped from the call. 7883 */ 7884 WRAP_UP: "WRAP_UP", 7885 /** 7886 * Indicates that the participant has accepted the dialog. This state is applicable to OUTBOUND_PREVIEW dialogs. 7887 */ 7888 ACCEPTED: "ACCEPTED", 7889 /** 7890 * @class Possible Dialog Participant State constants. 7891 * @constructs 7892 */ 7893 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 7894 }; 7895 7896 Dialog.ReasonStates = /** @lends finesse.restservices.Dialog.ReasonStates.prototype */ { 7897 /** 7898 * Dialog was Busy. This will typically be for a Failed Dialog. 7899 */ 7900 BUSY: "BUSY", 7901 /** 7902 * Dialog reached a Bad Destination. This will typically be for a Failed Dialog. 7903 */ 7904 BAD_DESTINATION: "BAD_DESTINATION", 7905 /** 7906 * All Other Reasons. This will typically be for a Failed Dialog. 7907 */ 7908 OTHER: "OTHER", 7909 /** 7910 * The Device Resource for the Dialog was not available. 7911 */ 7912 DEVICE_RESOURCE_NOT_AVAILABLE : "DEVICE_RESOURCE_NOT_AVAILABLE", 7913 /** 7914 * @class Possible dialog state reasons code constants. 7915 * @constructs 7916 */ 7917 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 7918 }; 7919 7920 window.finesse = window.finesse || {}; 7921 window.finesse.restservices = window.finesse.restservices || {}; 7922 window.finesse.restservices.Dialog = Dialog; 7923 7924 7925 return Dialog; 7926 })); 7927 7928 /** 7929 * JavaScript representation of the Finesse Dialogs collection 7930 * object which contains a list of Dialog objects. 7931 * 7932 * @requires finesse.clientservices.ClientServices 7933 * @requires Class 7934 * @requires finesse.FinesseBase 7935 * @requires finesse.restservices.RestBase 7936 * @requires finesse.restservices.Dialog 7937 */ 7938 /** @private */ 7939 (function (factory) { 7940 7941 7942 // Define as an AMD module if possible 7943 if ( typeof define === 'function' && define.amd ) 7944 { 7945 define('restservices/Dialogs', ['restservices/RestCollectionBase', 7946 'restservices/Dialog'], factory ); 7947 } 7948 /* Define using browser globals otherwise 7949 * Prevent multiple instantiations if the script is loaded twice 7950 */ 7951 else 7952 { 7953 factory(finesse.restservices.RestCollectionBase, finesse.restservices.Dialog); 7954 } 7955 }(function (RestCollectionBase, Dialog) { 7956 var Dialogs = RestCollectionBase.extend(/** @lends finesse.restservices.Dialogs.prototype */{ 7957 7958 /** 7959 * @class 7960 * JavaScript representation of a Dialogs collection object. Also exposes 7961 * methods to operate on the object against the server. 7962 * @augments finesse.restservices.RestCollectionBase 7963 * @constructs 7964 * @see finesse.restservices.Dialog 7965 * @example 7966 * _dialogs = _user.getDialogs( { 7967 * onCollectionAdd : _handleDialogAdd, 7968 * onCollectionDelete : _handleDialogDelete, 7969 * onLoad : _handleDialogsLoaded 7970 * }); 7971 * 7972 * _dialogCollection = _dialogs.getCollection(); 7973 * for (var dialogId in _dialogCollection) { 7974 * if (_dialogCollection.hasOwnProperty(dialogId)) { 7975 * _dialog = _dialogCollection[dialogId]; 7976 * etc... 7977 * } 7978 * } 7979 */ 7980 _fakeConstuctor: function () { 7981 /* This is here to hide the real init constructor from the public docs */ 7982 }, 7983 7984 /** 7985 * @private 7986 * @param {Object} options 7987 * An object with the following properties:<ul> 7988 * <li><b>id:</b> The id of the object being constructed</li> 7989 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 7990 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 7991 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 7992 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 7993 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 7994 * <li><b>status:</b> {Number} The HTTP status code returned</li> 7995 * <li><b>content:</b> {String} Raw string of response</li> 7996 * <li><b>object:</b> {Object} Parsed object of response</li> 7997 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 7998 * <li><b>errorType:</b> {String} Type of error that was caught</li> 7999 * <li><b>errorMessage:</b> {String} Message associated with error</li> 8000 * </ul></li> 8001 * </ul></li> 8002 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 8003 **/ 8004 init: function (options) { 8005 this._super(options); 8006 }, 8007 8008 /** 8009 * @private 8010 * Gets the REST class for the current object - this is the Dialogs class. 8011 */ 8012 getRestClass: function () { 8013 return Dialogs; 8014 }, 8015 8016 /** 8017 * @private 8018 * Gets the REST class for the objects that make up the collection. - this 8019 * is the Dialog class. 8020 */ 8021 getRestItemClass: function () { 8022 return Dialog; 8023 }, 8024 8025 /** 8026 * @private 8027 * Gets the REST type for the current object - this is a "Dialogs". 8028 */ 8029 getRestType: function () { 8030 return "Dialogs"; 8031 }, 8032 8033 /** 8034 * @private 8035 * Gets the REST type for the objects that make up the collection - this is "Dialogs". 8036 */ 8037 getRestItemType: function () { 8038 return "Dialog"; 8039 }, 8040 8041 /** 8042 * @private 8043 * Override default to indicates that the collection doesn't support making 8044 * requests. 8045 */ 8046 supportsRequests: true, 8047 8048 /** 8049 * @private 8050 * Override default to indicates that the collection subscribes to its objects. 8051 */ 8052 supportsRestItemSubscriptions: true, 8053 8054 /** 8055 * @private 8056 * Create a new Dialog in this collection 8057 * 8058 * @param {String} toAddress 8059 * The to address of the new Dialog 8060 * @param {String} fromAddress 8061 * The from address of the new Dialog 8062 * @param {finesse.interfaces.RequestHandlers} handlers 8063 * An object containing the (optional) handlers for the request. 8064 * @return {finesse.restservices.Dialogs} 8065 * This Dialogs object, to allow cascading. 8066 */ 8067 createNewCallDialog: function (toAddress, fromAddress, handlers) 8068 { 8069 var contentBody = {}; 8070 contentBody[this.getRestItemType()] = { 8071 "requestedAction": "MAKE_CALL", 8072 "toAddress": toAddress, 8073 "fromAddress": fromAddress 8074 }; 8075 8076 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 8077 handlers = handlers || {}; 8078 8079 this.restRequest(this.getRestUrl(), { 8080 method: 'POST', 8081 success: handlers.success, 8082 error: handlers.error, 8083 content: contentBody 8084 }); 8085 return this; // Allow cascading 8086 }, 8087 8088 /** 8089 * @private 8090 * Create a new Dialog in this collection as a result of a requested action 8091 * 8092 * @param {String} toAddress 8093 * The to address of the new Dialog 8094 * @param {String} fromAddress 8095 * The from address of the new Dialog 8096 * @param {finesse.restservices.Dialog.Actions} actionType 8097 * The associated action to request for creating this new dialog 8098 * @param {finesse.interfaces.RequestHandlers} handlers 8099 * An object containing the (optional) handlers for the request. 8100 * @return {finesse.restservices.Dialogs} 8101 * This Dialogs object, to allow cascading. 8102 */ 8103 createNewSuperviseCallDialog: function (toAddress, fromAddress, actionType, handlers) 8104 { 8105 var contentBody = {}; 8106 this._isLoaded = true; 8107 8108 contentBody[this.getRestItemType()] = { 8109 "requestedAction": actionType, 8110 "toAddress": toAddress, 8111 "fromAddress": fromAddress 8112 }; 8113 8114 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 8115 handlers = handlers || {}; 8116 8117 this.restRequest(this.getRestUrl(), { 8118 method: 'POST', 8119 success: handlers.success, 8120 error: handlers.error, 8121 content: contentBody 8122 }); 8123 return this; // Allow cascading 8124 }, 8125 8126 /** 8127 * @private 8128 * Create a new Dialog in this collection as a result of a requested action 8129 * @param {String} fromAddress 8130 * The from address of the new Dialog 8131 * @param {String} toAddress 8132 * The to address of the new Dialog 8133 * @param {finesse.restservices.Dialog.Actions} actionType 8134 * The associated action to request for creating this new dialog 8135 * @param {String} dialogUri 8136 * The associated uri of SUPERVISOR_MONITOR call 8137 * @param {finesse.interfaces.RequestHandlers} handlers 8138 * An object containing the (optional) handlers for the request. 8139 * @return {finesse.restservices.Dialogs} 8140 * This Dialogs object, to allow cascading. 8141 */ 8142 createNewBargeCall: function (fromAddress, toAddress, actionType, dialogURI, handlers) { 8143 this.isLoaded(); 8144 8145 var contentBody = {}; 8146 contentBody[this.getRestItemType()] = { 8147 "fromAddress": fromAddress, 8148 "toAddress": toAddress, 8149 "requestedAction": actionType, 8150 "associatedDialogUri": dialogURI 8151 8152 }; 8153 // (nonexistent) keys to be read as undefined 8154 handlers = handlers || {}; 8155 this.restRequest(this.getRestUrl(), { 8156 method: 'POST', 8157 success: handlers.success, 8158 error: handlers.error, 8159 content: contentBody 8160 }); 8161 return this; // Allow cascading 8162 }, 8163 8164 /** 8165 * Utility method to get the number of dialogs in this collection. 8166 * 'excludeSilentMonitor' flag is provided as an option to exclude calls with type 8167 * 'SUPERVISOR_MONITOR' from the count. 8168 * @param {Boolean} excludeSilentMonitor If true, calls with type of 'SUPERVISOR_MONITOR' will be excluded from the count. 8169 * @return {Number} The number of dialogs in this collection. 8170 */ 8171 getDialogCount: function (excludeSilentMonitor) { 8172 this.isLoaded(); 8173 8174 var dialogId, count = 0; 8175 if (excludeSilentMonitor) { 8176 for (dialogId in this._collection) { 8177 if (this._collection.hasOwnProperty(dialogId)) { 8178 if (this._collection[dialogId].getCallType() !== 'SUPERVISOR_MONITOR') { 8179 count += 1; 8180 } 8181 } 8182 } 8183 8184 return count; 8185 } else { 8186 return this.length; 8187 } 8188 } 8189 8190 }); 8191 8192 window.finesse = window.finesse || {}; 8193 window.finesse.restservices = window.finesse.restservices || {}; 8194 window.finesse.restservices.Dialogs = Dialogs; 8195 8196 return Dialogs; 8197 })); 8198 8199 /** 8200 * JavaScript representation of the Finesse ClientLog object 8201 * 8202 * @requires finesse.clientservices.ClientServices 8203 * @requires Class 8204 * @requires finesse.FinesseBase 8205 * @requires finesse.restservices.RestBase 8206 */ 8207 8208 /** The following comment is to prevent jslint errors about 8209 * using variables before they are defined. 8210 */ 8211 /** @private */ 8212 /*global finesse*/ 8213 8214 (function (factory) { 8215 8216 8217 // Define as an AMD module if possible 8218 if ( typeof define === 'function' && define.amd ) 8219 { 8220 define('restservices/ClientLog',["restservices/RestBase"], factory ); 8221 } 8222 8223 /* Define using browser globals otherwise 8224 * Prevent multiple instantiations if the script is loaded twice 8225 */ 8226 else 8227 { 8228 factory(finesse.restservices.RestBase); 8229 } 8230 8231 }(function (RestBase) { 8232 8233 var ClientLog = RestBase.extend(/** @lends finesse.restservices.ClientLog.prototype */{ 8234 /** 8235 * @private 8236 * Returns whether this object supports transport logs 8237 */ 8238 doNotLog : true, 8239 8240 doNotRefresh: true, 8241 8242 explicitSubscription : true, 8243 8244 /** 8245 * @class 8246 * @private 8247 * JavaScript representation of a ClientLog object. Also exposes methods to operate 8248 * on the object against the server. 8249 * 8250 * @param {Object} options 8251 * An object with the following properties:<ul> 8252 * <li><b>id:</b> The id of the object being constructed</li> 8253 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 8254 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 8255 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 8256 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 8257 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 8258 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8259 * <li><b>content:</b> {String} Raw string of response</li> 8260 * <li><b>object:</b> {Object} Parsed object of response</li> 8261 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 8262 * <li><b>errorType:</b> {String} Type of error that was caught</li> 8263 * <li><b>errorMessage:</b> {String} Message associated with error</li> 8264 * </ul></li> 8265 * </ul></li> 8266 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 8267 * @constructs 8268 * @augments finesse.restservices.RestBase 8269 **/ 8270 init: function (options) { 8271 this._super({ 8272 id: "", 8273 data: {clientLog : null}, 8274 onAdd: options.onAdd, 8275 onChange: options.onChange, 8276 onLoad: options.onLoad, 8277 onError: options.onError, 8278 parentObj: options.parentObj 8279 }); 8280 }, 8281 8282 /** 8283 * @private 8284 * Gets the REST class for the current object - this is the ClientLog object. 8285 */ 8286 getRestClass: function () { 8287 return ClientLog; 8288 }, 8289 8290 /** 8291 * @private 8292 * Gets the REST type for the current object - this is a "ClientLog". 8293 */ 8294 getRestType: function () 8295 { 8296 return "ClientLog"; 8297 }, 8298 8299 /** 8300 * @private 8301 * Gets the node path for the current object 8302 * @returns {String} The node path 8303 */ 8304 getXMPPNodePath: function () { 8305 return this.getRestUrl(); 8306 }, 8307 8308 /** 8309 * @private 8310 * Invoke a request to the server given a content body and handlers. 8311 * 8312 * @param {Object} contentBody 8313 * A JS object containing the body of the action request. 8314 * @param {Object} handlers 8315 * An object containing the following (optional) handlers for the request:<ul> 8316 * <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following 8317 * response object as its only parameter:<ul> 8318 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8319 * <li><b>content:</b> {String} Raw string of response</li> 8320 * <li><b>object:</b> {Object} Parsed object of response</li></ul> 8321 * <li>A error callback function for an unsuccessful request to be invoked with the 8322 * error response object as its only parameter:<ul> 8323 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8324 * <li><b>content:</b> {String} Raw string of response</li> 8325 * <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li> 8326 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 8327 * <li><b>errorType:</b> {String} Type of error that was caught</li> 8328 * <li><b>errorMessage:</b> {String} Message associated with error</li> 8329 * </ul></li> 8330 * </ul> 8331 */ 8332 sendLogs: function (contentBody, handlers) { 8333 // Protect against null dereferencing of options allowing its 8334 // (nonexistent) keys to be read as undefined 8335 handlers = handlers || {}; 8336 8337 this.restRequest(this.getRestUrl(), { 8338 method: 'POST', 8339 //success: handlers.success, 8340 error: handlers.error, 8341 content: contentBody 8342 }); 8343 } 8344 }); 8345 8346 window.finesse = window.finesse || {}; 8347 window.finesse.restservices = window.finesse.restservices || {}; 8348 window.finesse.restservices.ClientLog = ClientLog; 8349 8350 return ClientLog; 8351 })); 8352 8353 /** 8354 * JavaScript representation of the Finesse Queue object 8355 * @requires finesse.clientservices.ClientServices 8356 * @requires Class 8357 * @requires finesse.FinesseBase 8358 * @requires finesse.restservices.RestBase 8359 */ 8360 8361 /** @private */ 8362 (function (factory) { 8363 8364 8365 // Define as an AMD module if possible 8366 if ( typeof define === 'function' && define.amd ) 8367 { 8368 define('restservices/Queue', ['restservices/RestBase', 8369 'utilities/Utilities'], factory ); 8370 } 8371 /* Define using browser globals otherwise 8372 * Prevent multiple instantiations if the script is loaded twice 8373 */ 8374 else 8375 { 8376 factory(finesse.restservices.RestBase, finesse.utilities.Utilities); 8377 } 8378 }(function (RestBase, Utilities) { 8379 var Queue = RestBase.extend(/** @lends finesse.restservices.Queue.prototype */{ 8380 8381 /** 8382 * @class 8383 * A Queue is a list of Contacts available to a User for quick dial. 8384 * 8385 * @augments finesse.restservices.RestBase 8386 * @constructs 8387 */ 8388 _fakeConstuctor: function () { 8389 /* This is here to hide the real init constructor from the public docs */ 8390 }, 8391 8392 /** 8393 * @private 8394 * JavaScript representation of a Queue object. Also exposes methods to operate 8395 * on the object against the server. 8396 * 8397 * @constructor 8398 * @param {String} id 8399 * Not required... 8400 * @param {Object} callbacks 8401 * An object containing callbacks for instantiation and runtime 8402 * @param {Function} callbacks.onLoad(this) 8403 * Callback to invoke upon successful instantiation 8404 * @param {Function} callbacks.onLoadError(rsp) 8405 * Callback to invoke on instantiation REST request error 8406 * as passed by finesse.clientservices.ClientServices.ajax() 8407 * { 8408 * status: {Number} The HTTP status code returned 8409 * content: {String} Raw string of response 8410 * object: {Object} Parsed object of response 8411 * error: {Object} Wrapped exception that was caught 8412 * error.errorType: {String} Type of error that was caught 8413 * error.errorMessage: {String} Message associated with error 8414 * } 8415 * @param {Function} callbacks.onChange(this) 8416 * Callback to invoke upon successful update 8417 * @param {Function} callbacks.onError(rsp) 8418 * Callback to invoke on update error (refresh or event) 8419 * as passed by finesse.clientservices.ClientServices.ajax() 8420 * { 8421 * status: {Number} The HTTP status code returned 8422 * content: {String} Raw string of response 8423 * object: {Object} Parsed object of response 8424 * error: {Object} Wrapped exception that was caught 8425 * error.errorType: {String} Type of error that was caught 8426 * error.errorMessage: {String} Message associated with error 8427 * } 8428 * 8429 */ 8430 init: function (id, callbacks, restObj) { 8431 this._super(id, callbacks, restObj); 8432 }, 8433 8434 /** 8435 * @private 8436 * Gets the REST class for the current object - this is the Queue object. 8437 */ 8438 getRestClass: function () { 8439 return Queue; 8440 }, 8441 8442 /** 8443 * @private 8444 * Gets the REST type for the current object - this is a "Queue". 8445 */ 8446 getRestType: function () { 8447 return "Queue"; 8448 }, 8449 8450 /** 8451 * @private 8452 * Returns whether this object supports subscriptions 8453 */ 8454 supportsSubscriptions: function () { 8455 return true; 8456 }, 8457 8458 /** 8459 * @private 8460 * Specifies whether this object's subscriptions need to be explicitly requested 8461 */ 8462 explicitSubscription: true, 8463 8464 /** 8465 * @private 8466 * Gets the node path for the current object - this is the team Users node 8467 * @returns {String} The node path 8468 */ 8469 getXMPPNodePath: function () { 8470 return this.getRestUrl(); 8471 }, 8472 8473 /** 8474 * Getter for the queue id 8475 * @returns {String} 8476 * The id of the Queue 8477 */ 8478 getId: function () { 8479 this.isLoaded(); 8480 return this._id; 8481 }, 8482 8483 /** 8484 * Getter for the queue name 8485 * @returns {String} 8486 * The name of the Queue 8487 */ 8488 getName: function () { 8489 this.isLoaded(); 8490 return this.getData().name; 8491 }, 8492 8493 /** 8494 * Getter for the queue statistics. 8495 * Supported statistics include:<br> 8496 * - callsInQueue<br> 8497 * - startTimeOfLongestCallInQueue<br> 8498 * <br> 8499 * These statistics can be accessed via dot notation:<br> 8500 * i.e.: getStatistics().callsInQueue 8501 * @returns {Object} 8502 * The Object with different statistics as properties. 8503 */ 8504 getStatistics: function () { 8505 this.isLoaded(); 8506 return this.getData().statistics; 8507 }, 8508 8509 /** 8510 * Parses a uriString to retrieve the id portion 8511 * @param {String} uriString 8512 * @return {String} id 8513 */ 8514 _parseIdFromUriString : function (uriString) { 8515 return Utilities.getId(uriString); 8516 } 8517 8518 }); 8519 8520 window.finesse = window.finesse || {}; 8521 window.finesse.restservices = window.finesse.restservices || {}; 8522 window.finesse.restservices.Queue = Queue; 8523 8524 return Queue; 8525 })); 8526 8527 /** 8528 * JavaScript representation of the Finesse Queues collection 8529 * object which contains a list of Queue objects. 8530 * @requires finesse.clientservices.ClientServices 8531 * @requires Class 8532 * @requires finesse.FinesseBase 8533 * @requires finesse.restservices.RestBase 8534 * @requires finesse.restservices.RestCollectionBase 8535 */ 8536 8537 /** 8538 * @class 8539 * JavaScript representation of a Queues collection object. 8540 * 8541 * @constructor 8542 * @borrows finesse.restservices.RestCollectionBase as finesse.restservices.Queues 8543 */ 8544 8545 /** @private */ 8546 (function (factory) { 8547 8548 8549 // Define as an AMD module if possible 8550 if ( typeof define === 'function' && define.amd ) 8551 { 8552 define('restservices/Queues', ['restservices/RestCollectionBase', 8553 'restservices/Queue'], factory ); 8554 } 8555 /* Define using browser globals otherwise 8556 * Prevent multiple instantiations if the script is loaded twice 8557 */ 8558 else 8559 { 8560 factory(finesse.restservices.RestCollectionBase, finesse.restservices.Queue); 8561 } 8562 }(function (RestCollectionBase, Queue) { 8563 var Queues = RestCollectionBase.extend(/** @lends finesse.restservices.Queues.prototype */{ 8564 8565 /** 8566 * @class 8567 * JavaScript representation of a Queues collection object. 8568 * @augments finesse.restservices.RestCollectionBase 8569 * @constructs 8570 * @see finesse.restservices.Queue 8571 * @example 8572 * _queues = _user.getQueues( { 8573 * onCollectionAdd : _handleQueueAdd, 8574 * onCollectionDelete : _handleQueueDelete, 8575 * onLoad : _handleQueuesLoaded 8576 * }); 8577 * 8578 * _queueCollection = _queues.getCollection(); 8579 * for (var queueId in _queueCollection) { 8580 * if (_queueCollection.hasOwnProperty(queueId)) { 8581 * _queue = _queueCollection[queueId]; 8582 * etc... 8583 * } 8584 * } 8585 */ 8586 _fakeConstuctor: function () { 8587 /* This is here to hide the real init constructor from the public docs */ 8588 }, 8589 8590 /** 8591 * @private 8592 * JavaScript representation of a Queues object. Also exposes 8593 * methods to operate on the object against the server. 8594 * 8595 * @param {Object} options 8596 * An object with the following properties:<ul> 8597 * <li><b>id:</b> The id of the object being constructed</li> 8598 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 8599 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 8600 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 8601 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 8602 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 8603 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8604 * <li><b>content:</b> {String} Raw string of response</li> 8605 * <li><b>object:</b> {Object} Parsed object of response</li> 8606 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 8607 * <li><b>errorType:</b> {String} Type of error that was caught</li> 8608 * <li><b>errorMessage:</b> {String} Message associated with error</li> 8609 * </ul></li> 8610 * </ul></li> 8611 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 8612 **/ 8613 init: function (options) { 8614 this._super(options); 8615 }, 8616 8617 /** 8618 * @private 8619 * Gets xmpp node path. 8620 */ 8621 getXMPPNodePath: function () { 8622 return this.getRestUrl(); 8623 }, 8624 8625 /** 8626 * @private 8627 * Gets the REST class for the current object - this is the Queues class. 8628 */ 8629 getRestClass: function () { 8630 return Queues; 8631 }, 8632 8633 /** 8634 * @private 8635 * Gets the REST class for the objects that make up the collection. - this 8636 * is the Queue class. 8637 */ 8638 getRestItemClass: function () { 8639 return Queue; 8640 }, 8641 8642 /** 8643 * @private 8644 * Gets the REST type for the current object - this is a "Queues". 8645 */ 8646 getRestType: function () { 8647 return "Queues"; 8648 }, 8649 8650 /** 8651 * @private 8652 * Gets the REST type for the objects that make up the collection - this is "Queue". 8653 */ 8654 getRestItemType: function () { 8655 return "Queue"; 8656 }, 8657 8658 explicitSubscription: true, 8659 8660 handlesItemRefresh: true 8661 }); 8662 8663 window.finesse = window.finesse || {}; 8664 window.finesse.restservices = window.finesse.restservices || {}; 8665 window.finesse.restservices.Queues = Queues; 8666 8667 return Queues; 8668 })); 8669 8670 /** 8671 * JavaScript representation of the Finesse WrapUpReason object. 8672 * 8673 * @requires finesse.clientservices.ClientServices 8674 * @requires Class 8675 * @requires finesse.FinesseBase 8676 * @requires finesse.restservices.RestBase 8677 */ 8678 8679 /** @private */ 8680 (function (factory) { 8681 8682 8683 // Define as an AMD module if possible 8684 if ( typeof define === 'function' && define.amd ) 8685 { 8686 define('restservices/WrapUpReason',['restservices/RestBase'], factory); 8687 } 8688 /* Define using browser globals otherwise 8689 * Prevent multiple instantiations if the script is loaded twice 8690 */ 8691 else 8692 { 8693 factory(finesse.restservices.RestBase); 8694 } 8695 }(function (RestBase) { 8696 8697 var WrapUpReason = RestBase.extend(/** @lends finesse.restservices.WrapUpReason.prototype */{ 8698 8699 /** 8700 * @class 8701 * A WrapUpReason is a code and description identifying a particular reason that a 8702 * User is in WORK (WrapUp) mode. 8703 * 8704 * @augments finesse.restservices.RestBase 8705 * @see finesse.restservices.User 8706 * @see finesse.restservices.User.States#WORK 8707 * @constructs 8708 */ 8709 _fakeConstuctor: function () { 8710 /* This is here to hide the real init constructor from the public docs */ 8711 }, 8712 8713 /** 8714 * @private 8715 * JavaScript representation of a WrapUpReason object. Also exposes 8716 * methods to operate on the object against the server. 8717 * 8718 * @param {Object} options 8719 * An object with the following properties:<ul> 8720 * <li><b>id:</b> The id of the object being constructed</li> 8721 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 8722 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 8723 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 8724 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 8725 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 8726 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8727 * <li><b>content:</b> {String} Raw string of response</li> 8728 * <li><b>object:</b> {Object} Parsed object of response</li> 8729 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 8730 * <li><b>errorType:</b> {String} Type of error that was caught</li> 8731 * <li><b>errorMessage:</b> {String} Message associated with error</li> 8732 * </ul></li> 8733 * </ul></li> 8734 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 8735 **/ 8736 init: function (options) { 8737 this._super(options); 8738 }, 8739 8740 /** 8741 * @private 8742 * Gets the REST class for the current object - this is the WrapUpReason class. 8743 * @returns {Object} The WrapUpReason class. 8744 */ 8745 getRestClass: function () { 8746 return WrapUpReason; 8747 }, 8748 8749 /** 8750 * @private 8751 * Gets the REST type for the current object - this is a "WrapUpReason". 8752 * @returns {String} The WrapUpReason string. 8753 */ 8754 getRestType: function () { 8755 return "WrapUpReason"; 8756 }, 8757 8758 /** 8759 * @private 8760 * Gets the REST type for the current object - this is a "WrapUpReasons". 8761 * @returns {String} The WrapUpReasons string. 8762 */ 8763 getParentRestType: function () { 8764 return "WrapUpReasons"; 8765 }, 8766 8767 /** 8768 * @private 8769 * Override default to indicate that this object doesn't support making 8770 * requests. 8771 */ 8772 supportsRequests: false, 8773 8774 /** 8775 * @private 8776 * Override default to indicate that this object doesn't support subscriptions. 8777 */ 8778 supportsSubscriptions: false, 8779 8780 /** 8781 * Getter for the label. 8782 * @returns {String} The label. 8783 */ 8784 getLabel: function () { 8785 this.isLoaded(); 8786 return this.getData().label; 8787 }, 8788 8789 /** 8790 * @private 8791 * Getter for the forAll flag. 8792 * @returns {Boolean} True if global. 8793 */ 8794 getForAll: function () { 8795 this.isLoaded(); 8796 return this.getData().forAll; 8797 }, 8798 8799 /** 8800 * @private 8801 * Getter for the Uri value. 8802 * @returns {String} The Uri. 8803 */ 8804 getUri: function () { 8805 this.isLoaded(); 8806 return this.getData().uri; 8807 } 8808 }); 8809 8810 window.finesse = window.finesse || {}; 8811 window.finesse.restservices = window.finesse.restservices || {}; 8812 window.finesse.restservices.WrapUpReason = WrapUpReason; 8813 8814 return WrapUpReason; 8815 })); 8816 8817 /** 8818 * JavaScript representation of the Finesse WrapUpReasons collection 8819 * object which contains a list of WrapUpReason objects. 8820 * 8821 * @requires finesse.clientservices.ClientServices 8822 * @requires Class 8823 * @requires finesse.FinesseBase 8824 * @requires finesse.restservices.RestBase 8825 * @requires finesse.restservices.Dialog 8826 * @requires finesse.restservices.RestCollectionBase 8827 */ 8828 8829 /** @private */ 8830 (function (factory) { 8831 8832 8833 // Define as an AMD module if possible 8834 if ( typeof define === 'function' && define.amd ) 8835 { 8836 define('restservices/WrapUpReasons', ['restservices/RestCollectionBase', 8837 'restservices/WrapUpReason'], factory ); 8838 } 8839 /* Define using browser globals otherwise 8840 * Prevent multiple instantiations if the script is loaded twice 8841 */ 8842 else 8843 { 8844 factory(finesse.restservices.RestCollectionBase, finesse.restservices.WrapUpReason); 8845 } 8846 }(function (RestCollectionBase, WrapUpReason) { 8847 8848 var WrapUpReasons = RestCollectionBase.extend(/** @lends finesse.restservices.WrapUpReasons.prototype */{ 8849 8850 /** 8851 * @class 8852 * JavaScript representation of a WrapUpReasons collection object. 8853 * @augments finesse.restservices.RestCollectionBase 8854 * @constructs 8855 * @see finesse.restservices.WrapUpReason 8856 * @example 8857 * _wrapUpReasons = _user.getWrapUpReasons ( { 8858 * onCollectionAdd : _handleWrapUpReasonAdd, 8859 * onCollectionDelete : _handleWrapUpReasonDelete, 8860 * onLoad : _handleWrapUpReasonsLoaded 8861 * }); 8862 * 8863 * _wrapUpReasonCollection = _wrapUpReasons.getCollection(); 8864 * for (var wrapUpReasonId in _wrapUpReasonCollection) { 8865 * if (_wrapUpReasonCollection.hasOwnProperty(wrapUpReasonId)) { 8866 * _wrapUpReason = _wrapUpReasonCollection[wrapUpReasonId]; 8867 * etc... 8868 * } 8869 * } 8870 */ 8871 _fakeConstuctor: function () { 8872 /* This is here to hide the real init constructor from the public docs */ 8873 }, 8874 8875 /** 8876 * @private 8877 * JavaScript representation of a WrapUpReasons collection object. Also exposes 8878 * methods to operate on the object against the server. 8879 * 8880 * @param {Object} options 8881 * An object with the following properties:<ul> 8882 * <li><b>id:</b> The id of the object being constructed</li> 8883 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 8884 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 8885 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 8886 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 8887 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 8888 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8889 * <li><b>content:</b> {String} Raw string of response</li> 8890 * <li><b>object:</b> {Object} Parsed object of response</li> 8891 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 8892 * <li><b>errorType:</b> {String} Type of error that was caught</li> 8893 * <li><b>errorMessage:</b> {String} Message associated with error</li> 8894 * </ul></li> 8895 * </ul></li> 8896 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 8897 **/ 8898 init: function (options) { 8899 this._super(options); 8900 }, 8901 8902 /** 8903 * @private 8904 * Gets the REST class for the current object - this is the WrapUpReasons class. 8905 */ 8906 getRestClass: function () { 8907 return WrapUpReasons; 8908 }, 8909 8910 /** 8911 * @private 8912 * Gets the REST class for the objects that make up the collection. - this 8913 * is the WrapUpReason class. 8914 */ 8915 getRestItemClass: function () { 8916 return WrapUpReason; 8917 }, 8918 8919 /** 8920 * @private 8921 * Gets the REST type for the current object - this is a "WrapUpReasons". 8922 */ 8923 getRestType: function () { 8924 return "WrapUpReasons"; 8925 }, 8926 8927 /** 8928 * @private 8929 * Gets the REST type for the objects that make up the collection - this is "WrapUpReason". 8930 */ 8931 getRestItemType: function () { 8932 return "WrapUpReason"; 8933 }, 8934 8935 /** 8936 * @private 8937 * Override default to indicates that the collection supports making 8938 * requests. 8939 */ 8940 supportsRequests: true, 8941 8942 /** 8943 * @private 8944 * Override default to indicate that this object doesn't support subscriptions. 8945 */ 8946 supportsRestItemSubscriptions: false, 8947 8948 /** 8949 * @private 8950 * Retrieve the Wrap-Up Reason Codes. This call will re-query the server and refresh the collection. 8951 * 8952 * @returns {finesse.restservices.WrapUpReasons} 8953 * This ReadyReasonCodes object to allow cascading. 8954 */ 8955 get: function () { 8956 // set loaded to false so it will rebuild the collection after the get 8957 this._loaded = false; 8958 // reset collection 8959 this._collection = {}; 8960 // perform get 8961 this._synchronize(); 8962 return this; 8963 } 8964 8965 }); 8966 8967 window.finesse = window.finesse || {}; 8968 window.finesse.restservices = window.finesse.restservices || {}; 8969 window.finesse.restservices.WrapUpReasons = WrapUpReasons; 8970 8971 return WrapUpReasons; 8972 })); 8973 8974 /** 8975 * JavaScript representation of the Finesse Contact object. 8976 * @requires finesse.clientservices.ClientServices 8977 * @requires Class 8978 * @requires finesse.FinesseBase 8979 * @requires finesse.restservices.RestBase 8980 */ 8981 /** @private */ 8982 (function (factory) { 8983 8984 8985 // Define as an AMD module if possible 8986 if ( typeof define === 'function' && define.amd ) 8987 { 8988 define('restservices/Contact',['restservices/RestBase'], factory); 8989 } 8990 /* Define using browser globals otherwise 8991 * Prevent multiple instantiations if the script is loaded twice 8992 */ 8993 else 8994 { 8995 factory(finesse.restservices.RestBase); 8996 } 8997 }(function (RestBase) { 8998 var Contact = RestBase.extend(/** @lends finesse.restservices.Contact.prototype */{ 8999 9000 /** 9001 * @class 9002 * A Contact is a single entry in a PhoneBook, consisting of a First and Last Name, 9003 * a Phone Number, and a Description. 9004 * 9005 * @augments finesse.restservices.RestBase 9006 * @see finesse.restservices.PhoneBook 9007 * @constructs 9008 */ 9009 _fakeConstuctor: function () { 9010 /* This is here to hide the real init constructor from the public docs */ 9011 }, 9012 9013 /** 9014 * @private 9015 * @param {Object} options 9016 * An object with the following properties:<ul> 9017 * <li><b>id:</b> The id of the object being constructed</li> 9018 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 9019 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 9020 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 9021 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 9022 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 9023 * <li><b>status:</b> {Number} The HTTP status code returned</li> 9024 * <li><b>content:</b> {String} Raw string of response</li> 9025 * <li><b>object:</b> {Object} Parsed object of response</li> 9026 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 9027 * <li><b>errorType:</b> {String} Type of error that was caught</li> 9028 * <li><b>errorMessage:</b> {String} Message associated with error</li> 9029 * </ul></li> 9030 * </ul></li> 9031 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 9032 **/ 9033 init: function (options) { 9034 this._super(options); 9035 }, 9036 9037 /** 9038 * @private 9039 * Gets the REST class for the current object - this is the Contact class. 9040 * @returns {Object} The Contact class. 9041 */ 9042 getRestClass: function () { 9043 return Contact; 9044 }, 9045 9046 /** 9047 * @private 9048 * Gets the REST type for the current object - this is a "Contact". 9049 * @returns {String} The Contact string. 9050 */ 9051 getRestType: function () { 9052 return "Contact"; 9053 }, 9054 9055 /** 9056 * @private 9057 * Override default to indicate that this object doesn't support making 9058 * requests. 9059 */ 9060 supportsRequests: false, 9061 9062 /** 9063 * @private 9064 * Override default to indicate that this object doesn't support subscriptions. 9065 */ 9066 supportsSubscriptions: false, 9067 9068 /** 9069 * Getter for the firstName. 9070 * @returns {String} The firstName. 9071 */ 9072 getFirstName: function () { 9073 this.isLoaded(); 9074 return this.getData().firstName; 9075 }, 9076 9077 /** 9078 * Getter for the lastName. 9079 * @returns {String} The lastName. 9080 */ 9081 getLastName: function () { 9082 this.isLoaded(); 9083 return this.getData().lastName; 9084 }, 9085 9086 /** 9087 * Getter for the phoneNumber. 9088 * @returns {String} The phoneNumber. 9089 */ 9090 getPhoneNumber: function () { 9091 this.isLoaded(); 9092 return this.getData().phoneNumber; 9093 }, 9094 9095 /** 9096 * Getter for the description. 9097 * @returns {String} The description. 9098 */ 9099 getDescription: function () { 9100 this.isLoaded(); 9101 return this.getData().description; 9102 }, 9103 9104 /** @private */ 9105 createPutSuccessHandler: function(contact, contentBody, successHandler){ 9106 return function (rsp) { 9107 // Update internal structure based on response. Here we 9108 // inject the contentBody from the PUT request into the 9109 // rsp.object element to mimic a GET as a way to take 9110 // advantage of the existing _processResponse method. 9111 rsp.object = contentBody; 9112 contact._processResponse(rsp); 9113 9114 //Remove the injected Contact object before cascading response 9115 rsp.object = {}; 9116 9117 //cascade response back to consumer's response handler 9118 successHandler(rsp); 9119 }; 9120 }, 9121 9122 /** @private */ 9123 createPostSuccessHandler: function (contact, contentBody, successHandler) { 9124 return function (rsp) { 9125 rsp.object = contentBody; 9126 contact._processResponse(rsp); 9127 9128 //Remove the injected Contact object before cascading response 9129 rsp.object = {}; 9130 9131 //cascade response back to consumer's response handler 9132 successHandler(rsp); 9133 }; 9134 }, 9135 9136 /** 9137 * Add 9138 * @private 9139 */ 9140 add: function (newValues, handlers) { 9141 // this.isLoaded(); 9142 var contentBody = {}; 9143 9144 contentBody[this.getRestType()] = { 9145 "firstName": newValues.firstName, 9146 "lastName": newValues.lastName, 9147 "phoneNumber": newValues.phoneNumber, 9148 "description": newValues.description 9149 }; 9150 9151 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9152 handlers = handlers || {}; 9153 9154 this.restRequest(this.getRestUrl(), { 9155 method: 'POST', 9156 success: this.createPostSuccessHandler(this, contentBody, handlers.success), 9157 error: handlers.error, 9158 content: contentBody 9159 }); 9160 9161 return this; // Allow cascading 9162 }, 9163 9164 /** 9165 * Update 9166 * @private 9167 */ 9168 update: function (newValues, handlers) { 9169 this.isLoaded(); 9170 var contentBody = {}; 9171 9172 contentBody[this.getRestType()] = { 9173 "uri": this.getId(), 9174 "firstName": newValues.firstName, 9175 "lastName": newValues.lastName, 9176 "phoneNumber": newValues.phoneNumber, 9177 "description": newValues.description 9178 }; 9179 9180 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9181 handlers = handlers || {}; 9182 9183 this.restRequest(this.getRestUrl(), { 9184 method: 'PUT', 9185 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 9186 error: handlers.error, 9187 content: contentBody 9188 }); 9189 9190 return this; // Allow cascading 9191 }, 9192 9193 9194 /** 9195 * Delete 9196 * @private 9197 */ 9198 "delete": function ( handlers) { 9199 this.isLoaded(); 9200 9201 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9202 handlers = handlers || {}; 9203 9204 this.restRequest(this.getRestUrl(), { 9205 method: 'DELETE', 9206 success: this.createPutSuccessHandler(this, {}, handlers.success), 9207 error: handlers.error, 9208 content: undefined 9209 }); 9210 9211 return this; // Allow cascading 9212 } 9213 }); 9214 9215 window.finesse = window.finesse || {}; 9216 window.finesse.restservices = window.finesse.restservices || {}; 9217 window.finesse.restservices.Contact = Contact; 9218 9219 return Contact; 9220 })); 9221 9222 /** 9223 * JavaScript representation of the Finesse Contacts collection 9224 * object which contains a list of Contact objects. 9225 * 9226 * @requires finesse.clientservices.ClientServices 9227 * @requires Class 9228 * @requires finesse.FinesseBase 9229 * @requires finesse.restservices.RestBase 9230 * @requires finesse.restservices.Dialog 9231 * @requires finesse.restservices.RestCollectionBase 9232 */ 9233 /** @private */ 9234 (function (factory) { 9235 9236 9237 // Define as an AMD module if possible 9238 if ( typeof define === 'function' && define.amd ) 9239 { 9240 define('restservices/Contacts', ['restservices/RestCollectionBase', 9241 'restservices/Contact'], factory ); 9242 } 9243 /* Define using browser globals otherwise 9244 * Prevent multiple instantiations if the script is loaded twice 9245 */ 9246 else 9247 { 9248 factory(finesse.restservices.RestCollectionBase, finesse.restservices.Contact); 9249 } 9250 }(function (RestCollectionBase, Contact) { 9251 var Contacts = RestCollectionBase.extend(/** @lends finesse.restservices.Contacts.prototype */{ 9252 9253 /** 9254 * @class 9255 * JavaScript representation of a Contacts collection object. Also exposes 9256 * methods to operate on the object against the server. 9257 * @augments finesse.restservices.RestCollectionBase 9258 * @constructs 9259 * @see finesse.restservices.Contact 9260 * @see finesse.restservices.PhoneBook 9261 * @example 9262 * _contacts = _phonebook.getContacts( { 9263 * onCollectionAdd : _handleContactAdd, 9264 * onCollectionDelete : _handleContactDelete, 9265 * onLoad : _handleContactsLoaded 9266 * }); 9267 * 9268 * _contactCollection = _contacts.getCollection(); 9269 * for (var contactId in _contactCollection) { 9270 * if (_contactCollection.hasOwnProperty(contactId)) { 9271 * _contact = _contactCollection[contactId]; 9272 * etc... 9273 * } 9274 * } 9275 */ 9276 _fakeConstuctor: function () { 9277 /* This is here to hide the real init constructor from the public docs */ 9278 }, 9279 9280 /** 9281 * @private 9282 * JavaScript representation of a Contacts collection object. Also exposes 9283 * methods to operate on the object against the server. 9284 * 9285 * @param {Object} options 9286 * An object with the following properties:<ul> 9287 * <li><b>id:</b> The id of the object being constructed</li> 9288 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 9289 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 9290 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 9291 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 9292 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 9293 * <li><b>status:</b> {Number} The HTTP status code returned</li> 9294 * <li><b>content:</b> {String} Raw string of response</li> 9295 * <li><b>object:</b> {Object} Parsed object of response</li> 9296 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 9297 * <li><b>errorType:</b> {String} Type of error that was caught</li> 9298 * <li><b>errorMessage:</b> {String} Message associated with error</li> 9299 * </ul></li> 9300 * </ul></li> 9301 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 9302 **/ 9303 init: function (options) { 9304 this._super(options); 9305 }, 9306 9307 /** 9308 * @private 9309 * Gets the REST class for the current object - this is the Contacts class. 9310 */ 9311 getRestClass: function () { 9312 return Contacts; 9313 }, 9314 9315 /** 9316 * @private 9317 * Gets the REST class for the objects that make up the collection. - this 9318 * is the Contact class. 9319 */ 9320 getRestItemClass: function () { 9321 return Contact; 9322 }, 9323 9324 /** 9325 * @private 9326 * Gets the REST type for the current object - this is a "Contacts". 9327 */ 9328 getRestType: function () { 9329 return "Contacts"; 9330 }, 9331 9332 /** 9333 * @private 9334 * Gets the REST type for the objects that make up the collection - this is "Contacts". 9335 */ 9336 getRestItemType: function () { 9337 return "Contact"; 9338 }, 9339 9340 /** 9341 * @private 9342 * Override default to indicates that the collection supports making 9343 * requests. 9344 */ 9345 supportsRequests: true, 9346 9347 /** 9348 * @private 9349 * Override default to indicates that the collection subscribes to its objects. 9350 */ 9351 supportsRestItemSubscriptions: false, 9352 9353 /** 9354 * @private 9355 * Retrieve the Contacts. This call will re-query the server and refresh the collection. 9356 * 9357 * @returns {finesse.restservices.Contacts} 9358 * This Contacts object, to allow cascading. 9359 */ 9360 get: function () { 9361 // set loaded to false so it will rebuild the collection after the get 9362 this._loaded = false; 9363 // reset collection 9364 this._collection = {}; 9365 // perform get 9366 this._synchronize(); 9367 return this; 9368 } 9369 9370 }); 9371 9372 window.finesse = window.finesse || {}; 9373 window.finesse.restservices = window.finesse.restservices || {}; 9374 window.finesse.restservices.Contacts = Contacts; 9375 9376 9377 return Contacts; 9378 })); 9379 9380 /** 9381 * JavaScript representation of the Finesse PhoneBook object. 9382 * 9383 * @requires finesse.clientservices.ClientServices 9384 * @requires Class 9385 * @requires finesse.FinesseBase 9386 * @requires finesse.restservices.RestBase 9387 */ 9388 9389 /** @private */ 9390 (function (factory) { 9391 9392 9393 // Define as an AMD module if possible 9394 if ( typeof define === 'function' && define.amd ) 9395 { 9396 define('restservices/PhoneBook',['restservices/RestBase', 9397 'restservices/Contacts'], factory); 9398 } 9399 /* Define using browser globals otherwise 9400 * Prevent multiple instantiations if the script is loaded twice 9401 */ 9402 else 9403 { 9404 factory(finesse.restservices.RestBase, 9405 finesse.restservices.Contacts); 9406 } 9407 }(function (RestBase, Contacts) { 9408 var PhoneBook = RestBase.extend(/** @lends finesse.restservices.PhoneBook.prototype */{ 9409 9410 _contacts: null, 9411 9412 /** 9413 * @class 9414 * A PhoneBook is a list of Contacts available to a User for quick dial. 9415 * 9416 * @augments finesse.restservices.RestBase 9417 * @see finesse.restservices.Contacts 9418 * @constructs 9419 */ 9420 _fakeConstuctor: function () { 9421 /* This is here to hide the real init constructor from the public docs */ 9422 }, 9423 9424 /** 9425 * @private 9426 * JavaScript representation of a PhoneBook object. Also exposes 9427 * methods to operate on the object against the server. 9428 * 9429 * @param {Object} options 9430 * An object with the following properties:<ul> 9431 * <li><b>id:</b> The id of the object being constructed</li> 9432 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 9433 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 9434 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 9435 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 9436 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 9437 * <li><b>status:</b> {Number} The HTTP status code returned</li> 9438 * <li><b>content:</b> {String} Raw string of response</li> 9439 * <li><b>object:</b> {Object} Parsed object of response</li> 9440 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 9441 * <li><b>errorType:</b> {String} Type of error that was caught</li> 9442 * <li><b>errorMessage:</b> {String} Message associated with error</li> 9443 * </ul></li> 9444 * </ul></li> 9445 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 9446 **/ 9447 init: function (options) { 9448 this._super(options); 9449 }, 9450 9451 /** 9452 * @private 9453 * Gets the REST class for the current object - this is the PhoneBook class. 9454 * @returns {Object} The PhoneBook class. 9455 */ 9456 getRestClass: function () { 9457 return PhoneBook; 9458 }, 9459 9460 /** 9461 * @private 9462 * Gets the REST type for the current object - this is a "PhoneBook". 9463 * @returns {String} The PhoneBook string. 9464 */ 9465 getRestType: function () { 9466 return "PhoneBook"; 9467 }, 9468 9469 /** 9470 * @private 9471 * Override default to indicate that this object doesn't support making 9472 * requests. 9473 */ 9474 supportsRequests: false, 9475 9476 /** 9477 * @private 9478 * Override default to indicate that this object doesn't support subscriptions. 9479 */ 9480 supportsSubscriptions: false, 9481 9482 /** 9483 * Getter for the name of the Phone Book. 9484 * @returns {String} The name. 9485 */ 9486 getName: function () { 9487 this.isLoaded(); 9488 return this.getData().name; 9489 }, 9490 9491 /** 9492 * Getter for the type flag. 9493 * @returns {String} The type. 9494 */ 9495 getType: function () { 9496 this.isLoaded(); 9497 return this.getData().type; 9498 }, 9499 9500 /** 9501 * @private 9502 * Getter for the Uri value. 9503 * @returns {String} The Uri. 9504 */ 9505 getUri: function () { 9506 this.isLoaded(); 9507 return this.getData().uri; 9508 }, 9509 9510 /** 9511 * Getter for a Contacts collection object that is associated with PhoneBook. 9512 * @param {finesse.interfaces.RequestHandlers} handlers 9513 * An object containing the handlers for the request 9514 * @returns {finesse.restservices.Contacts} 9515 * A Contacts collection object. 9516 */ 9517 getContacts: function (callbacks) { 9518 var options = callbacks || {}; 9519 options.parentObj = this; 9520 this.isLoaded(); 9521 9522 if (this._contacts === null) { 9523 this._contacts = new Contacts(options); 9524 } 9525 9526 return this._contacts; 9527 }, 9528 9529 /** 9530 * Getter for <contacts> node within PhoneBook - sometimes it's just a URI, sometimes it is a Contacts collection 9531 * @returns {String} uri to contacts 9532 * or {finesse.restservices.Contacts} collection 9533 */ 9534 getEmbeddedContacts: function(){ 9535 this.isLoaded(); 9536 return this.getData().contacts; 9537 }, 9538 9539 /** @private */ 9540 createPutSuccessHandler: function(phonebook, contentBody, successHandler){ 9541 return function (rsp) { 9542 // Update internal structure based on response. Here we 9543 // inject the contentBody from the PUT request into the 9544 // rsp.object element to mimic a GET as a way to take 9545 // advantage of the existing _processResponse method. 9546 rsp.object = contentBody; 9547 phonebook._processResponse(rsp); 9548 9549 //Remove the injected PhoneBook object before cascading response 9550 rsp.object = {}; 9551 9552 //cascade response back to consumer's response handler 9553 successHandler(rsp); 9554 }; 9555 }, 9556 9557 /** @private */ 9558 createPostSuccessHandler: function (phonebook, contentBody, successHandler) { 9559 return function (rsp) { 9560 rsp.object = contentBody; 9561 phonebook._processResponse(rsp); 9562 9563 //Remove the injected PhoneBook object before cascading response 9564 rsp.object = {}; 9565 9566 //cascade response back to consumer's response handler 9567 successHandler(rsp); 9568 }; 9569 }, 9570 9571 /** 9572 * @private 9573 * Add a PhoneBook. 9574 * @param {Object} newValues 9575 * @param {String} newValues.name Name of PhoneBook 9576 * @param {String} newValues.type Type of PhoneBook 9577 * @param {finesse.interfaces.RequestHandlers} handlers 9578 * An object containing the handlers for the request 9579 * @returns {finesse.restservices.PhoneBook} 9580 * This PhoneBook object, to allow cascading 9581 */ 9582 add: function (newValues, handlers) { 9583 // this.isLoaded(); 9584 var contentBody = {}; 9585 9586 contentBody[this.getRestType()] = { 9587 "name": newValues.name, 9588 "type": newValues.type 9589 }; 9590 9591 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9592 handlers = handlers || {}; 9593 9594 this.restRequest(this.getRestUrl(), { 9595 method: 'POST', 9596 success: this.createPostSuccessHandler(this, contentBody, handlers.success), 9597 error: handlers.error, 9598 content: contentBody 9599 }); 9600 9601 return this; // Allow cascading 9602 }, 9603 9604 /** 9605 * @private 9606 * Update a PhoneBook. 9607 * @param {Object} newValues 9608 * @param {String} newValues.name Name of PhoneBook 9609 * @param {String} newValues.type Type of PhoneBook 9610 * @param {finesse.interfaces.RequestHandlers} handlers 9611 * An object containing the handlers for the request 9612 * @returns {finesse.restservices.PhoneBook} 9613 * This PhoneBook object, to allow cascading 9614 */ 9615 update: function (newValues, handlers) { 9616 this.isLoaded(); 9617 var contentBody = {}; 9618 9619 contentBody[this.getRestType()] = { 9620 "uri": this.getId(), 9621 "name": newValues.name, 9622 "type": newValues.type 9623 }; 9624 9625 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9626 handlers = handlers || {}; 9627 9628 this.restRequest(this.getRestUrl(), { 9629 method: 'PUT', 9630 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 9631 error: handlers.error, 9632 content: contentBody 9633 }); 9634 9635 return this; // Allow cascading 9636 }, 9637 9638 9639 /** 9640 * Delete a PhoneBook. 9641 * @param {finesse.interfaces.RequestHandlers} handlers 9642 * An object containing the handlers for the request 9643 * @returns {finesse.restservices.PhoneBook} 9644 * This PhoneBook object, to allow cascading 9645 */ 9646 "delete": function ( handlers) { 9647 this.isLoaded(); 9648 9649 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9650 handlers = handlers || {}; 9651 9652 this.restRequest(this.getRestUrl(), { 9653 method: 'DELETE', 9654 success: this.createPutSuccessHandler(this, {}, handlers.success), 9655 error: handlers.error, 9656 content: undefined 9657 }); 9658 9659 return this; // Allow cascading 9660 } 9661 9662 9663 9664 }); 9665 9666 window.finesse = window.finesse || {}; 9667 window.finesse.restservices = window.finesse.restservices || {}; 9668 window.finesse.restservices.PhoneBook = PhoneBook; 9669 9670 return PhoneBook; 9671 })); 9672 9673 /** 9674 * JavaScript representation of the Finesse PhoneBooks collection 9675 * object which contains a list of PhoneBook objects. 9676 * 9677 * @requires finesse.clientservices.ClientServices 9678 * @requires Class 9679 * @requires finesse.FinesseBase 9680 * @requires finesse.restservices.RestBase 9681 * @requires finesse.restservices.Dialog 9682 * @requires finesse.restservices.RestCollectionBase 9683 */ 9684 /** @private */ 9685 (function (factory) { 9686 9687 9688 // Define as an AMD module if possible 9689 if ( typeof define === 'function' && define.amd ) 9690 { 9691 define('restservices/PhoneBooks', ['restservices/RestCollectionBase', 9692 'restservices/PhoneBook'], factory ); 9693 } 9694 /* Define using browser globals otherwise 9695 * Prevent multiple instantiations if the script is loaded twice 9696 */ 9697 else 9698 { 9699 factory(finesse.restservices.RestCollectionBase, finesse.restservices.PhoneBook); 9700 } 9701 }(function (RestCollectionBase, PhoneBook) { 9702 var PhoneBooks = RestCollectionBase.extend(/** @lends finesse.restservices.PhoneBooks.prototype */{ 9703 9704 /** 9705 * @class 9706 * JavaScript representation of a PhoneBooks collection object. 9707 * @augments finesse.restservices.RestCollectionBase 9708 * @constructs 9709 * @see finesse.restservices.PhoneBook 9710 * @see finesse.restservices.Contacts 9711 * @see finesse.restservices.Contact 9712 * @example 9713 * _phoneBooks = _user.getPhoneBooks( { 9714 * onCollectionAdd : _handlePhoneBookAdd, 9715 * onCollectionDelete : _handlePhoneBookDelete, 9716 * onLoad : _handlePhoneBooksLoaded 9717 * }); 9718 * 9719 * _phoneBookCollection = _phoneBooks.getCollection(); 9720 * for (var phoneBookId in _phoneBookCollection) { 9721 * if (_phoneBookCollection.hasOwnProperty(phoneBookId)) { 9722 * _phoneBook = _phoneBookCollection[phoneBookId]; 9723 * etc... 9724 * } 9725 * } 9726 */ 9727 _fakeConstuctor: function () { 9728 /* This is here to hide the real init constructor from the public docs */ 9729 }, 9730 9731 /** 9732 * @private 9733 * 9734 * @param {Object} options 9735 * An object with the following properties:<ul> 9736 * <li><b>id:</b> The id of the object being constructed</li> 9737 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 9738 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 9739 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 9740 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 9741 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 9742 * <li><b>status:</b> {Number} The HTTP status code returned</li> 9743 * <li><b>content:</b> {String} Raw string of response</li> 9744 * <li><b>object:</b> {Object} Parsed object of response</li> 9745 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 9746 * <li><b>errorType:</b> {String} Type of error that was caught</li> 9747 * <li><b>errorMessage:</b> {String} Message associated with error</li> 9748 * </ul></li> 9749 * </ul></li> 9750 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 9751 **/ 9752 init: function (options) { 9753 this._super(options); 9754 }, 9755 9756 /** 9757 * @private 9758 * Gets the REST class for the current object - this is the PhoneBooks class. 9759 */ 9760 getRestClass: function () { 9761 return PhoneBooks; 9762 }, 9763 9764 /** 9765 * @private 9766 * Gets the REST class for the objects that make up the collection. - this 9767 * is the PhoneBook class. 9768 */ 9769 getRestItemClass: function () { 9770 return PhoneBook; 9771 }, 9772 9773 /** 9774 * @private 9775 * Gets the REST type for the current object - this is a "PhoneBooks". 9776 */ 9777 getRestType: function () { 9778 return "PhoneBooks"; 9779 }, 9780 9781 /** 9782 * @private 9783 * Gets the REST type for the objects that make up the collection - this is "PhoneBooks". 9784 */ 9785 getRestItemType: function () { 9786 return "PhoneBook"; 9787 }, 9788 9789 /** 9790 * @private 9791 * Override default to indicates that the collection supports making 9792 * requests. 9793 */ 9794 supportsRequests: true, 9795 9796 /** 9797 * @private 9798 * Override default to indicates that the collection subscribes to its objects. 9799 */ 9800 supportsRestItemSubscriptions: false, 9801 9802 /** 9803 * @private 9804 * Retrieve the PhoneBooks. This call will re-query the server and refresh the collection. 9805 * 9806 * @returns {finesse.restservices.PhoneBooks} 9807 * This PhoneBooks object, to allow cascading. 9808 */ 9809 get: function () { 9810 // set loaded to false so it will rebuild the collection after the get 9811 this._loaded = false; 9812 // reset collection 9813 this._collection = {}; 9814 // perform get 9815 this._synchronize(); 9816 return this; 9817 } 9818 9819 }); 9820 9821 window.finesse = window.finesse || {}; 9822 window.finesse.restservices = window.finesse.restservices || {}; 9823 window.finesse.restservices.PhoneBooks = PhoneBooks; 9824 9825 return PhoneBooks; 9826 })); 9827 9828 /** 9829 * JavaScript representation of the Finesse WorkflowAction object. 9830 * 9831 * @requires finesse.clientservices.ClientServices 9832 * @requires Class 9833 * @requires finesse.FinesseBase 9834 * @requires finesse.restservices.RestBase 9835 */ 9836 9837 /*jslint browser: true, nomen: true, sloppy: true, forin: true */ 9838 /*global define,finesse */ 9839 9840 /** @private */ 9841 (function (factory) { 9842 9843 9844 // Define as an AMD module if possible 9845 if ( typeof define === 'function' && define.amd ) 9846 { 9847 define('restservices/WorkflowAction', ['restservices/RestBase'], factory ); 9848 } 9849 /* Define using browser globals otherwise 9850 * Prevent multiple instantiations if the script is loaded twice 9851 */ 9852 else 9853 { 9854 factory(finesse.restservices.RestBase); 9855 } 9856 }(function (RestBase) { 9857 9858 var WorkflowAction = RestBase.extend({ 9859 9860 _contacts: null, 9861 9862 actionTypes: [ 9863 { 9864 name: 'BROWSER_POP', 9865 params: [ 9866 { 9867 name: 'windowName', 9868 type: 'text' 9869 }, 9870 { 9871 name: 'path', 9872 type: 'systemVariableSingleLineEditor' 9873 } 9874 ] 9875 }, 9876 { 9877 name: 'HTTP_REQUEST', 9878 params: [ 9879 { 9880 name: 'method', 9881 type: 'dropdown', 9882 values: ['POST', 'PUT'] 9883 }, 9884 { 9885 name: 'location', 9886 type: 'dropdown', 9887 values: ['FINESSE', 'OTHER'] 9888 }, 9889 { 9890 name: 'contentType', 9891 type: 'text' 9892 }, 9893 { 9894 name: 'path', 9895 type: 'systemVariableSingleLineEditor' 9896 }, 9897 { 9898 name: 'body', 9899 type: 'systemVariableMultiLineEditor' 9900 } 9901 ] 9902 } 9903 // more action type definitions here 9904 ], 9905 9906 /** 9907 * @class 9908 * A WorkflowAction is an action (e.g. Browser Pop, Rest Request) defined in a 9909 * Workflow and triggered by a system event (Call Received, Call Ended, etc.). 9910 * 9911 * @augments finesse.restservices.RestBase 9912 * @see finesse.restservices.Workflow 9913 * @constructs 9914 */ 9915 _fakeConstuctor: function () { 9916 /* This is here to hide the real init constructor from the public docs */ 9917 }, 9918 9919 /** 9920 * @private 9921 * JavaScript representation of a WorkflowAction object. Also exposes 9922 * methods to operate on the object against the server. 9923 * 9924 * @param {Object} options 9925 * An object with the following properties:<ul> 9926 * <li><b>id:</b> The id of the object being constructed</li> 9927 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 9928 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 9929 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 9930 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 9931 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 9932 * <li><b>status:</b> {Number} The HTTP status code returned</li> 9933 * <li><b>content:</b> {String} Raw string of response</li> 9934 * <li><b>object:</b> {Object} Parsed object of response</li> 9935 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 9936 * <li><b>errorType:</b> {String} Type of error that was caught</li> 9937 * <li><b>errorMessage:</b> {String} Message associated with error</li> 9938 * </ul></li> 9939 * </ul></li> 9940 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 9941 **/ 9942 init: function (options) { 9943 this._super(options); 9944 }, 9945 9946 /** 9947 * @private 9948 * Gets the REST class for the current object - this is the WorkflowAction class. 9949 * @returns {Object} The WorkflowAction class. 9950 */ 9951 getRestClass: function () { 9952 return finesse.restservices.WorkflowAction; 9953 }, 9954 9955 /** 9956 * @private 9957 * Gets the REST type for the current object - this is a "WorkflowAction". 9958 * @returns {String} The WorkflowAction string. 9959 */ 9960 getRestType: function () { 9961 return "WorkflowAction"; 9962 }, 9963 9964 /** 9965 * @private 9966 * Override default to indicate that this object doesn't support making 9967 * requests. 9968 */ 9969 supportsRequests: false, 9970 9971 /** 9972 * @private 9973 * Override default to indicate that this object doesn't support subscriptions. 9974 */ 9975 supportsSubscriptions: false, 9976 9977 /** 9978 * Getter for the name. 9979 * @returns {String} The name. 9980 */ 9981 getName: function () { 9982 this.isLoaded(); 9983 return this.getData().name; 9984 }, 9985 9986 /** 9987 * Getter for the type flag. 9988 * @returns {String} The type. 9989 */ 9990 getType: function () { 9991 this.isLoaded(); 9992 return this.getData().type; 9993 }, 9994 9995 /** 9996 * @private 9997 * Getter for the Uri value. 9998 * @returns {String} The Uri. 9999 */ 10000 getUri: function () { 10001 this.isLoaded(); 10002 return this.getData().uri; 10003 }, 10004 10005 /** 10006 * @private 10007 * Getter for the handledBy value. 10008 * @returns {String} handledBy. 10009 */ 10010 getHandledBy: function () { 10011 this.isLoaded(); 10012 return this.getData().handledBy; 10013 }, 10014 10015 /** 10016 * Getter for the parameters. 10017 * @returns {Object} key = param name, value = param value 10018 */ 10019 getParams: function () { 10020 var map = {}, 10021 params = this.getData().params.Param, 10022 i, 10023 param; 10024 10025 for(i=0; i<params.length; i+=1){ 10026 param = params[i]; 10027 map[param.name] = param.value || ""; 10028 } 10029 10030 return map; 10031 }, 10032 10033 /** 10034 * Getter for the ActionVariables 10035 * @returns {Object} key = action variable name, value = Object{name, type, node, testValue} 10036 */ 10037 getActionVariables: function() { 10038 var map = {}, 10039 actionVariablesParent = this.getData().actionVariables, 10040 actionVariables, 10041 i, 10042 actionVariable; 10043 10044 if (actionVariablesParent === null || typeof(actionVariablesParent) === "undefined" || actionVariablesParent.length === 0){ 10045 return map; 10046 } 10047 actionVariables = actionVariablesParent.ActionVariable; 10048 10049 if(actionVariables.length > 0){ 10050 for(i=0; i<actionVariables.length; i+=1){ 10051 actionVariable = actionVariables[i]; 10052 // escape nulls to empty string 10053 actionVariable.name = actionVariable.name || ""; 10054 actionVariable.type = actionVariable.type || ""; 10055 actionVariable.node = actionVariable.node || ""; 10056 actionVariable.testValue = actionVariable.testValue || ""; 10057 map[actionVariable.name] = actionVariable; 10058 } 10059 } else { 10060 map[actionVariables.name] = actionVariables; 10061 } 10062 10063 return map; 10064 }, 10065 10066 /** @private */ 10067 createPutSuccessHandler: function(action, contentBody, successHandler){ 10068 return function (rsp) { 10069 // Update internal structure based on response. Here we 10070 // inject the contentBody from the PUT request into the 10071 // rsp.object element to mimic a GET as a way to take 10072 // advantage of the existing _processResponse method. 10073 rsp.object = contentBody; 10074 action._processResponse(rsp); 10075 10076 //Remove the injected WorkflowAction object before cascading response 10077 rsp.object = {}; 10078 10079 //cascade response back to consumer's response handler 10080 successHandler(rsp); 10081 }; 10082 }, 10083 10084 /** @private */ 10085 createPostSuccessHandler: function (action, contentBody, successHandler) { 10086 return function (rsp) { 10087 rsp.object = contentBody; 10088 action._processResponse(rsp); 10089 10090 //Remove the injected WorkflowAction object before cascading response 10091 rsp.object = {}; 10092 10093 //cascade response back to consumer's response handler 10094 successHandler(rsp); 10095 }; 10096 }, 10097 10098 /** 10099 * @private 10100 * Build params array out of all the values coming into add or update methods 10101 * paramMap is a map of params.. we need to translate it into an array of Param objects 10102 * where path and windowName are params for the BROWSER_POP type 10103 */ 10104 buildParamsForRest: function(paramMap){ 10105 var params = {"Param": []}, 10106 i; 10107 for(i in paramMap){ 10108 if(paramMap.hasOwnProperty(i)){ 10109 params.Param.push({name: i, value: paramMap[i]}); 10110 } 10111 } 10112 return params; 10113 }, 10114 10115 /** 10116 * @private 10117 * Build actionVariables array out of all the values coming into add or update methods 10118 * actionVariableMap is a map of actionVariables.. we need to translate it into an array of ActionVariable objects 10119 * where path and windowName are params for the BROWSER_POP type 10120 */ 10121 buildActionVariablesForRest: function(actionVariableMap){ 10122 var actionVariables = {"ActionVariable": []}, 10123 i, 10124 actionVariable; 10125 for(i in actionVariableMap){ 10126 if(actionVariableMap.hasOwnProperty(i)){ 10127 // {name: "callVariable1", type: "SYSTEM", node: "", testValue: "<blink>"} 10128 actionVariable = { 10129 "name": actionVariableMap[i].name, 10130 "type": actionVariableMap[i].type, 10131 "node": actionVariableMap[i].node, 10132 "testValue": actionVariableMap[i].testValue 10133 }; 10134 actionVariables.ActionVariable.push(actionVariable); 10135 } 10136 } 10137 return actionVariables; 10138 }, 10139 10140 /** 10141 * Add 10142 */ 10143 add: function (newValues, handlers) { 10144 var contentBody = {}; 10145 10146 contentBody[this.getRestType()] = { 10147 "name": newValues.name, 10148 "type": newValues.type, 10149 "handledBy": newValues.handledBy, 10150 "params": this.buildParamsForRest(newValues.params), 10151 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables) 10152 }; 10153 10154 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10155 handlers = handlers || {}; 10156 10157 this.restRequest(this.getRestUrl(), { 10158 method: 'POST', 10159 success: this.createPostSuccessHandler(this, contentBody, handlers.success), 10160 error: handlers.error, 10161 content: contentBody 10162 }); 10163 10164 return this; // Allow cascading 10165 }, 10166 10167 /** 10168 * @private 10169 * Update 10170 */ 10171 update: function (newValues, handlers) { 10172 this.isLoaded(); 10173 var contentBody = {}; 10174 10175 contentBody[this.getRestType()] = { 10176 "uri": this.getId(), 10177 "name": newValues.name, 10178 "type": newValues.type, 10179 "handledBy": newValues.handledBy, 10180 "params": this.buildParamsForRest(newValues.params), 10181 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables) 10182 }; 10183 10184 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10185 handlers = handlers || {}; 10186 10187 this.restRequest(this.getRestUrl(), { 10188 method: 'PUT', 10189 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 10190 error: handlers.error, 10191 content: contentBody 10192 }); 10193 10194 return this; // Allow cascading 10195 }, 10196 10197 10198 /** 10199 * @private 10200 * Delete 10201 */ 10202 "delete": function ( handlers) { 10203 this.isLoaded(); 10204 10205 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10206 handlers = handlers || {}; 10207 10208 this.restRequest(this.getRestUrl(), { 10209 method: 'DELETE', 10210 success: this.createPutSuccessHandler(this, {}, handlers.success), 10211 error: handlers.error, 10212 content: undefined 10213 }); 10214 10215 return this; // Allow cascading 10216 } 10217 10218 10219 10220 }); 10221 10222 window.finesse = window.finesse || {}; 10223 window.finesse.restservices = window.finesse.restservices || {}; 10224 window.finesse.restservices.WorkflowAction = WorkflowAction; 10225 10226 return WorkflowAction; 10227 })); 10228 10229 /** 10230 * JavaScript representation of the Finesse WorkflowActions collection 10231 * object which contains a list of WorkflowAction objects. 10232 * 10233 * @requires finesse.clientservices.ClientServices 10234 * @requires Class 10235 * @requires finesse.FinesseBase 10236 * @requires finesse.restservices.RestBase 10237 * @requires finesse.restservices.Dialog 10238 * @requires finesse.restservices.RestCollectionBase 10239 */ 10240 10241 /** @private */ 10242 (function (factory) { 10243 10244 10245 // Define as an AMD module if possible 10246 if ( typeof define === 'function' && define.amd ) 10247 { 10248 define('restservices/WorkflowActions', ['restservices/RestCollectionBase', 10249 'restservices/RestBase', 10250 'restservices/WorkflowAction'], factory ); 10251 } 10252 /* Define using browser globals otherwise 10253 * Prevent multiple instantiations if the script is loaded twice 10254 */ 10255 else 10256 { 10257 factory(finesse.restservices.RestCollectionBase, finesse.restservices.RestBase, finesse.restservices.WorkflowAction); 10258 } 10259 }(function (RestCollectionBase, RestBase, WorkflowAction) { 10260 10261 var WorkflowActions = RestCollectionBase.extend({ 10262 10263 /** 10264 * @class 10265 * JavaScript representation of a WorkflowActions collection object. 10266 * @augments finesse.restservices.RestCollectionBase 10267 * @constructs 10268 * @see finesse.restservices.WorkflowAction 10269 * @see finesse.restservices.Workflow 10270 * @see finesse.restservices.Workflows 10271 * @example 10272 * _workflowActions = _user.getWorkflowActions( { 10273 * onCollectionAdd : _handleWorkflowActionAdd, 10274 * onCollectionDelete : _handleWorkflowActionDelete, 10275 * onLoad : _handleWorkflowActionsLoaded 10276 * }); 10277 */ 10278 _fakeConstuctor: function () { 10279 /* This is here to hide the real init constructor from the public docs */ 10280 }, 10281 10282 /** 10283 * @private 10284 * JavaScript representation of a WorkflowActions collection object. Also exposes 10285 * methods to operate on the object against the server. 10286 * 10287 * @param {Object} options 10288 * An object with the following properties:<ul> 10289 * <li><b>id:</b> The id of the object being constructed</li> 10290 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 10291 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 10292 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 10293 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 10294 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 10295 * <li><b>status:</b> {Number} The HTTP status code returned</li> 10296 * <li><b>content:</b> {String} Raw string of response</li> 10297 * <li><b>object:</b> {Object} Parsed object of response</li> 10298 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 10299 * <li><b>errorType:</b> {String} Type of error that was caught</li> 10300 * <li><b>errorMessage:</b> {String} Message associated with error</li> 10301 * </ul></li> 10302 * </ul></li> 10303 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 10304 **/ 10305 init: function (options) { 10306 this._super(options); 10307 }, 10308 10309 /** 10310 * @private 10311 * Gets the REST class for the current object - this is the WorkflowActions class. 10312 */ 10313 getRestClass: function () { 10314 return WorkflowActions; 10315 }, 10316 10317 /** 10318 * @private 10319 * Gets the REST class for the objects that make up the collection. - this 10320 * is the WorkflowAction class. 10321 */ 10322 getRestItemClass: function () { 10323 return WorkflowAction; 10324 }, 10325 10326 /** 10327 * @private 10328 * Gets the REST type for the current object - this is a "WorkflowActions". 10329 */ 10330 getRestType: function () { 10331 return "WorkflowActions"; 10332 }, 10333 10334 /** 10335 * @private 10336 * Gets the REST type for the objects that make up the collection - this is "WorkflowActions". 10337 */ 10338 getRestItemType: function () { 10339 return "WorkflowAction"; 10340 }, 10341 10342 /** 10343 * @private 10344 * Override default to indicates that the collection supports making 10345 * requests. 10346 */ 10347 supportsRequests: true, 10348 10349 /** 10350 * @private 10351 * Override default to indicates that the collection subscribes to its objects. 10352 */ 10353 supportsRestItemSubscriptions: false, 10354 10355 /** 10356 * @private 10357 * Retrieve the WorkflowActions. 10358 * 10359 * @returns {finesse.restservices.WorkflowActions} 10360 * This WorkflowActions object to allow cascading. 10361 */ 10362 get: function () { 10363 // set loaded to false so it will rebuild the collection after the get 10364 this._loaded = false; 10365 // reset collection 10366 this._collection = {}; 10367 // perform get 10368 this._synchronize(); 10369 return this; 10370 } 10371 }); 10372 10373 window.finesse = window.finesse || {}; 10374 window.finesse.restservices = window.finesse.restservices || {}; 10375 window.finesse.restservices.WorkflowActions = WorkflowActions; 10376 10377 return WorkflowActions; 10378 })); 10379 10380 /** 10381 * JavaScript representation of the Finesse Workflow object. 10382 * 10383 * @requires finesse.clientservices.ClientServices 10384 * @requires Class 10385 * @requires finesse.FinesseBase 10386 * @requires finesse.restservices.RestBase 10387 */ 10388 10389 /*jslint browser: true, nomen: true, sloppy: true, forin: true */ 10390 /*global define,finesse */ 10391 10392 /** @private */ 10393 (function (factory) { 10394 10395 10396 if (typeof define === 'function' && define.amd) { 10397 // Define as an AMD module if possible 10398 define('restservices/Workflow',['restservices/RestBase', 10399 'restservices/WorkflowActions'], factory); 10400 } else { 10401 /* Define using browser globals otherwise 10402 * Prevent multiple instantiations if the script is loaded twice 10403 */ 10404 factory(finesse.restservices.RestBase, 10405 finesse.restservices.WorkflowActions); 10406 } 10407 }(function (RestBase, WorkflowActions) { 10408 10409 var Workflow = RestBase.extend({ 10410 10411 /** 10412 * @class 10413 * JavaScript representation of a Workflow object. Also exposes 10414 * methods to operate on the object against the server. 10415 * 10416 * @param {Object} options 10417 * An object with the following properties:<ul> 10418 * <li><b>id:</b> The id of the object being constructed</li> 10419 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 10420 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 10421 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 10422 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 10423 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 10424 * <li><b>status:</b> {Number} The HTTP status code returned</li> 10425 * <li><b>content:</b> {String} Raw string of response</li> 10426 * <li><b>object:</b> {Object} Parsed object of response</li> 10427 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 10428 * <li><b>errorType:</b> {String} Type of error that was caught</li> 10429 * <li><b>errorMessage:</b> {String} Message associated with error</li> 10430 * </ul></li> 10431 * </ul></li> 10432 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 10433 * @constructs 10434 **/ 10435 init: function (options) { 10436 this._super(options); 10437 }, 10438 10439 /** 10440 * @private 10441 * Gets the REST class for the current object - this is the Workflow class. 10442 * @returns {Object} The Workflow class. 10443 */ 10444 getRestClass: function () { 10445 return Workflow; 10446 }, 10447 10448 /** 10449 * @private 10450 * Gets the REST type for the current object - this is a "Workflow". 10451 * @returns {String} The Workflow string. 10452 */ 10453 getRestType: function () { 10454 return "Workflow"; 10455 }, 10456 10457 /** 10458 * @private 10459 * Override default to indicate that this object doesn't support making 10460 * requests. 10461 */ 10462 supportsRequests: false, 10463 10464 /** 10465 * @private 10466 * Override default to indicate that this object doesn't support subscriptions. 10467 */ 10468 supportsSubscriptions: false, 10469 10470 /** 10471 * @private 10472 * Getter for the Uri value. 10473 * @returns {String} The Uri. 10474 */ 10475 getUri: function () { 10476 this.isLoaded(); 10477 return this.getData().uri; 10478 }, 10479 10480 /** 10481 * Getter for the name. 10482 * @returns {String} The name. 10483 */ 10484 getName: function () { 10485 this.isLoaded(); 10486 return this.getData().name; 10487 }, 10488 10489 /** 10490 * Getter for the description. 10491 * @returns {String} The description. 10492 */ 10493 getDescription: function () { 10494 this.isLoaded(); 10495 return this.getData().description; 10496 }, 10497 10498 /** 10499 * Getter for the trigger set. 10500 * @returns {String} The trigger set. 10501 */ 10502 getTriggerSet: function () { 10503 this.isLoaded(); 10504 return this.getData().TriggerSet; 10505 }, 10506 10507 /** 10508 * Getter for the condition set. 10509 * @returns {String} The condition set. 10510 */ 10511 getConditionSet: function () { 10512 this.isLoaded(); 10513 return this.getData().ConditionSet; 10514 }, 10515 10516 /** 10517 * Getter for the assigned workflowActions. 10518 * @returns {String} The workflowActions object. 10519 */ 10520 getWorkflowActions: function () { 10521 this.isLoaded(); 10522 var workflowActions = this.getData().workflowActions; 10523 if (workflowActions === null) { 10524 workflowActions = ""; 10525 } 10526 return workflowActions; 10527 }, 10528 10529 createPutSuccessHandler: function (workflow, contentBody, successHandler) { 10530 return function (rsp) { 10531 // Update internal structure based on response. Here we 10532 // inject the contentBody from the PUT request into the 10533 // rsp.object element to mimic a GET as a way to take 10534 // advantage of the existing _processResponse method. 10535 rsp.object = contentBody; 10536 workflow._processResponse(rsp); 10537 10538 //Remove the injected Workflow object before cascading response 10539 rsp.object = {}; 10540 10541 //cascade response back to consumer's response handler 10542 successHandler(rsp); 10543 }; 10544 }, 10545 10546 createPostSuccessHandler: function (workflow, contentBody, successHandler) { 10547 return function (rsp) { 10548 rsp.object = contentBody; 10549 workflow._processResponse(rsp); 10550 10551 //Remove the injected Workflow object before cascading response 10552 rsp.object = {}; 10553 10554 //cascade response back to consumer's response handler 10555 successHandler(rsp); 10556 }; 10557 }, 10558 10559 /** 10560 * @private 10561 * Add 10562 */ 10563 add: function (newValues, handlers) { 10564 // this.isLoaded(); 10565 var contentBody = {}; 10566 10567 contentBody[this.getRestType()] = { 10568 "name": newValues.name, 10569 "description": newValues.description, 10570 "TriggerSet" : newValues.TriggerSet, 10571 "ConditionSet" : newValues.ConditionSet, 10572 "workflowActions" : newValues.workflowActions 10573 }; 10574 10575 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10576 handlers = handlers || {}; 10577 10578 this.restRequest(this.getRestUrl(), { 10579 method: 'POST', 10580 success: this.createPostSuccessHandler(this, contentBody, handlers.success), 10581 error: handlers.error, 10582 content: contentBody 10583 }); 10584 10585 return this; // Allow cascading 10586 }, 10587 10588 /** 10589 * @private 10590 * Update 10591 */ 10592 update: function (newValues, handlers) { 10593 this.isLoaded(); 10594 var contentBody = {}; 10595 10596 contentBody[this.getRestType()] = { 10597 "uri": this.getId(), 10598 "name": newValues.name, 10599 "description": newValues.description, 10600 "TriggerSet" : newValues.TriggerSet, 10601 "ConditionSet" : newValues.ConditionSet, 10602 "workflowActions" : newValues.workflowActions 10603 }; 10604 10605 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10606 handlers = handlers || {}; 10607 10608 this.restRequest(this.getRestUrl(), { 10609 method: 'PUT', 10610 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 10611 error: handlers.error, 10612 content: contentBody 10613 }); 10614 10615 return this; // Allow cascading 10616 }, 10617 10618 10619 /** 10620 * @private 10621 * Delete 10622 */ 10623 "delete": function (handlers) { 10624 this.isLoaded(); 10625 10626 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10627 handlers = handlers || {}; 10628 10629 this.restRequest(this.getRestUrl(), { 10630 method: 'DELETE', 10631 success: this.createPutSuccessHandler(this, {}, handlers.success), 10632 error: handlers.error, 10633 content: undefined 10634 }); 10635 10636 return this; // Allow cascading 10637 } 10638 10639 10640 10641 }); 10642 10643 window.finesse = window.finesse || {}; 10644 window.finesse.restservices = window.finesse.restservices || {}; 10645 window.finesse.restservices.Workflow = Workflow; 10646 10647 return Workflow; 10648 })); 10649 10650 /** 10651 * JavaScript representation of the Finesse workflows collection 10652 * object which contains a list of workflow objects. 10653 * 10654 * @requires finesse.clientservices.ClientServices 10655 * @requires Class 10656 * @requires finesse.FinesseBase 10657 * @requires finesse.restservices.RestBase 10658 * @requires finesse.restservices.Dialog 10659 * @requires finesse.restservices.RestCollectionBase 10660 */ 10661 10662 /** @private */ 10663 (function (factory) { 10664 10665 10666 // Define as an AMD module if possible 10667 if ( typeof define === 'function' && define.amd ) 10668 { 10669 define('restservices/Workflows', ['restservices/RestCollectionBase', 10670 'restservices/RestBase', 10671 'restservices/Workflow'], factory ); 10672 } 10673 /* Define using browser globals otherwise 10674 * Prevent multiple instantiations if the script is loaded twice 10675 */ 10676 else 10677 { 10678 factory(finesse.restservices.RestCollectionBase, finesse.restservices.RestBase, finesse.restservices.Workflow); 10679 } 10680 }(function (RestCollectionBase, RestBase, Workflow) { 10681 10682 var Workflows = RestCollectionBase.extend({ 10683 10684 /** 10685 * @class 10686 * JavaScript representation of a workflows collection object. Also exposes 10687 * methods to operate on the object against the server. 10688 * 10689 * @param {Object} options 10690 * An object with the following properties:<ul> 10691 * <li><b>id:</b> The id of the object being constructed</li> 10692 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 10693 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 10694 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 10695 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 10696 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 10697 * <li><b>status:</b> {Number} The HTTP status code returned</li> 10698 * <li><b>content:</b> {String} Raw string of response</li> 10699 * <li><b>object:</b> {Object} Parsed object of response</li> 10700 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 10701 * <li><b>errorType:</b> {String} Type of error that was caught</li> 10702 * <li><b>errorMessage:</b> {String} Message associated with error</li> 10703 * </ul></li> 10704 * </ul></li> 10705 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 10706 * @constructs 10707 **/ 10708 init: function (options) { 10709 this._super(options); 10710 }, 10711 10712 /** 10713 * @private 10714 * Gets the REST class for the current object - this is the workflows class. 10715 */ 10716 getRestClass: function () { 10717 return Workflows; 10718 }, 10719 10720 /** 10721 * @private 10722 * Gets the REST class for the objects that make up the collection. - this 10723 * is the workflow class. 10724 */ 10725 getRestItemClass: function () { 10726 return Workflow; 10727 }, 10728 10729 /** 10730 * @private 10731 * Gets the REST type for the current object - this is a "workflows". 10732 */ 10733 getRestType: function () { 10734 return "Workflows"; 10735 }, 10736 10737 /** 10738 * @private 10739 * Gets the REST type for the objects that make up the collection - this is "workflows". 10740 */ 10741 getRestItemType: function () { 10742 return "Workflow"; 10743 }, 10744 10745 /** 10746 * @private 10747 * Override default to indicates that the collection supports making requests. 10748 */ 10749 supportsRequests: true, 10750 10751 /** 10752 * @private 10753 * Override default to indicates that the collection does not subscribe to its objects. 10754 */ 10755 supportsRestItemSubscriptions: false, 10756 10757 /** 10758 * @private 10759 * Retrieve the workflows. This call will re-query the server and refresh the collection. 10760 * 10761 * @returns {finesse.restservices.workflows} 10762 * This workflows object to allow cascading. 10763 */ 10764 get: function () { 10765 // set loaded to false so it will rebuild the collection after the get 10766 this._loaded = false; 10767 // reset collection 10768 this._collection = {}; 10769 // perform get 10770 this._synchronize(); 10771 return this; 10772 } 10773 }); 10774 10775 window.finesse = window.finesse || {}; 10776 window.finesse.restservices = window.finesse.restservices || {}; 10777 window.finesse.restservices.Workflows = Workflows; 10778 10779 return Workflows; 10780 })); 10781 10782 /** 10783 * JavaScript representation of the Finesse MediaPropertiesLayout object 10784 * 10785 * @requires finesse.clientservices.ClientServices 10786 * @requires Class 10787 * @requires finesse.FinesseBase 10788 * @requires finesse.restservices.RestBase 10789 */ 10790 10791 /** The following comment is to prevent jslint errors about 10792 * using variables before they are defined. 10793 */ 10794 /*global finesse*/ 10795 10796 /** @private */ 10797 (function (factory) { 10798 10799 10800 // Define as an AMD module if possible 10801 if ( typeof define === 'function' && define.amd ) 10802 { 10803 define('restservices/MediaPropertiesLayout', ['restservices/RestBase'], factory ); 10804 } 10805 /* Define using browser globals otherwise 10806 * Prevent multiple instantiations if the script is loaded twice 10807 */ 10808 else 10809 { 10810 factory(finesse.restservices.RestBase); 10811 } 10812 }(function (RestBase) { 10813 var MediaPropertiesLayout = RestBase.extend(/** @lends finesse.restservices.MediaPropertiesLayout.prototype */{ 10814 10815 /** 10816 * @class 10817 * The MediaPropertiesLayout handles which call variables are associated with Dialogs. 10818 * 10819 * @augments finesse.restservices.RestBase 10820 * @see finesse.restservices.Dialog#getMediaProperties 10821 * @see finesse.restservices.User#getMediaPropertiesLayout 10822 * @constructs 10823 */ 10824 _fakeConstuctor: function () { 10825 /* This is here to hide the real init constructor from the public docs */ 10826 }, 10827 10828 /** 10829 * @private 10830 * JavaScript representation of a MediaPropertiesLayout object. Also exposes 10831 * methods to operate on the object against the server. 10832 * 10833 * @param {Object} options 10834 * An object with the following properties:<ul> 10835 * <li><b>id:</b> The id of the object being constructed</li> 10836 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 10837 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 10838 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 10839 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 10840 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 10841 * <li><b>status:</b> {Number} The HTTP status code returned</li> 10842 * <li><b>content:</b> {String} Raw string of response</li> 10843 * <li><b>object:</b> {Object} Parsed object of response</li> 10844 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 10845 * <li><b>errorType:</b> {String} Type of error that was caught</li> 10846 * <li><b>errorMessage:</b> {String} Message associated with error</li> 10847 * </ul></li> 10848 * </ul></li> 10849 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 10850 **/ 10851 init: function (options) { 10852 this._super(options); 10853 }, 10854 10855 /** 10856 * @private 10857 * Gets the REST class for the current object - this is the MediaPropertiesLayout object. 10858 */ 10859 getRestClass: function () { 10860 return MediaPropertiesLayout; 10861 }, 10862 10863 /** 10864 * @private 10865 * Gets the REST type for the current object - this is a "MediaPropertiesLayout". 10866 */ 10867 getRestType: function () { 10868 return "MediaPropertiesLayout"; 10869 }, 10870 10871 /** 10872 * @private 10873 * Overrides the parent class. Returns the url for the MediaPropertiesLayout resource 10874 */ 10875 getRestUrl: function () { 10876 return ("/finesse/api/User/" + this.getId() + "/" + this.getRestType()); 10877 }, 10878 10879 /** 10880 * @private 10881 * Returns whether this object supports subscriptions 10882 */ 10883 supportsSubscriptions: false, 10884 10885 /** 10886 * Retrieve the media properties layout. This call will re-query the server and refresh the layout object. 10887 * @returns {finesse.restservices.MediaPropertiesLayout} 10888 * This MediaPropertiesLayout object to allow cascading 10889 */ 10890 get: function () { 10891 this._synchronize(); 10892 10893 return this; //Allow cascading 10894 }, 10895 10896 /** 10897 * Gets the data for this object. 10898 * 10899 * Performs safe conversion from raw API data to ensure that the returned layout object 10900 * always has a header with correct entry fields, and exactly two columns with lists of entries. 10901 * 10902 * @returns {finesse.restservices.MediaPropertiesLayout.Object} Data in columns (unless only one defined). 10903 */ 10904 getData: function () { 10905 10906 var layout = this._data, result, _addColumnData; 10907 10908 result = this.getEmptyData(); 10909 10910 /** 10911 * @private 10912 */ 10913 _addColumnData = function (entryData, colIndex) { 10914 10915 if (!entryData) { 10916 //If there's no entry data at all, rewrite entryData to be an empty collection of entries 10917 entryData = {}; 10918 } else if (entryData.mediaProperty) { 10919 //If entryData contains the keys for a single entry rather than being a collection of entries, 10920 //rewrite it to be a collection containing a single entry 10921 entryData = { "": entryData }; 10922 } 10923 10924 //Add each of the entries in the list to the column 10925 jQuery.each(entryData, function (i, entryData) { 10926 10927 //If the entry has no displayName specified, explicitly set it to the empty string 10928 if (!entryData.displayName) { 10929 entryData.displayName = ""; 10930 } 10931 10932 result.columns[colIndex].push(entryData); 10933 10934 }); 10935 10936 }; 10937 10938 //The header should only contain a single entry 10939 if (layout.header && layout.header.entry) { 10940 10941 //If the entry has no displayName specified, explicitly set it to the empty string 10942 if (!layout.header.entry.displayName) { 10943 layout.header.entry.displayName = ""; 10944 } 10945 10946 result.header = layout.header.entry; 10947 10948 } else { 10949 10950 throw "MediaPropertiesLayout.getData() - Header does not contain an entry"; 10951 10952 } 10953 10954 //If the column object contains an entry object that wasn't part of a list of entries, 10955 //it must be a single right-hand entry object (left-hand entry object would be part of a list.) 10956 //Force the entry object to be the 2nd element in an otherwise-empty list. 10957 if (layout.column && layout.column.entry) { 10958 layout.column = [ 10959 null, 10960 { "entry": layout.column.entry } 10961 ]; 10962 } 10963 10964 if (layout.column && layout.column.length > 0 && layout.column.length <= 2) { 10965 10966 //Render left column entries 10967 if (layout.column[0] && layout.column[0].entry) { 10968 _addColumnData(layout.column[0].entry, 0); 10969 } 10970 10971 //Render right column entries 10972 if (layout.column[1] && layout.column[1].entry) { 10973 _addColumnData(layout.column[1].entry, 1); 10974 } 10975 10976 } 10977 10978 return result; 10979 10980 }, 10981 10982 /** 10983 * @private 10984 * Empty/template version of getData(). 10985 * 10986 * Used by getData(), and by callers of getData() in error cases. 10987 */ 10988 getEmptyData: function () { 10989 10990 return { 10991 header : { 10992 displayName: null, 10993 mediaProperty: null 10994 }, 10995 columns : [[], []] 10996 }; 10997 10998 }, 10999 11000 /** 11001 * @private 11002 * Set the layout of this MediaPropertiesLayout. 11003 * @param {String} layout 11004 * The layout you are setting 11005 * @param {finesse.interfaces.RequestHandlers} handlers 11006 * An object containing the handlers for the request 11007 * @returns {finesse.restservices.MediaPropertiesLayout} 11008 * This MediaPropertiesLayout object to allow cascading 11009 */ 11010 setLayout: function (layout, handlers) { 11011 11012 var contentBody = {}; 11013 11014 contentBody[this.getRestType()] = layout; 11015 11016 //Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 11017 handlers = handlers || {}; 11018 11019 this.restRequest(this.getRestUrl(), { 11020 method: 'PUT', 11021 success: handlers.success, 11022 error: handlers.error, 11023 content: contentBody 11024 }); 11025 11026 return this; // Allow cascading 11027 } 11028 11029 }); 11030 11031 MediaPropertiesLayout.Object = /** @lends finesse.restservices.MediaPropertiesLayout.Object.prototype */ { 11032 /** 11033 * @class Format of MediaPropertiesLayout Object.<br> 11034 * Object { <ul> 11035 * <li>header : { <ul> 11036 * <li>dispayName {String} 11037 * <li>mediaProperty {String}</ul>} 11038 * <li>columns : { <ul> 11039 * <li>[ [] , [] ] 11040 * </ul> 11041 * where column arrays consists of the same Object format as header.<br> 11042 * }</ul> 11043 * }<br> 11044 * @constructs 11045 */ 11046 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 11047 11048 }; 11049 11050 window.finesse = window.finesse || {}; 11051 window.finesse.restservices = window.finesse.restservices || {}; 11052 window.finesse.restservices.MediaPropertiesLayout = MediaPropertiesLayout; 11053 11054 return MediaPropertiesLayout; 11055 })); 11056 11057 /** 11058 * JavaScript representation of the Finesse User object 11059 * 11060 * @requires finesse.clientservices.ClientServices 11061 * @requires Class 11062 * @requires finesse.FinesseBase 11063 * @requires finesse.restservices.RestBase 11064 */ 11065 11066 /** @private */ 11067 (function (factory) { 11068 11069 11070 // Define as an AMD module if possible 11071 if ( typeof define === 'function' && define.amd ) 11072 { 11073 define('restservices/User', ['restservices/RestBase', 11074 'restservices/Dialogs', 11075 'restservices/ClientLog', 11076 'restservices/Queues', 11077 'restservices/WrapUpReasons', 11078 'restservices/PhoneBooks', 11079 'restservices/Workflows', 11080 'restservices/MediaPropertiesLayout', 11081 'utilities/Utilities'], factory ); 11082 } 11083 /* Define using browser globals otherwise 11084 * Prevent multiple instantiations if the script is loaded twice 11085 */ 11086 else 11087 { 11088 factory(finesse.restservices.RestBase, 11089 finesse.restservices.Dialogs, 11090 finesse.restservices.ClientLog, 11091 finesse.restservices.Queues, 11092 finesse.restservices.WrapUpReasons, 11093 finesse.restservices.PhoneBooks, 11094 finesse.restservices.Workflows, 11095 finesse.restservices.MediaPropertiesLayout, 11096 finesse.utilities.Utilities); 11097 } 11098 }(function (RestBase, 11099 Dialogs, 11100 ClientLog, 11101 Queues, 11102 WrapUpReasons, 11103 PhoneBooks, 11104 Workflows, 11105 MediaPropertiesLayout, 11106 Utilities) { 11107 11108 var User = RestBase.extend(/** @lends finesse.restservices.User.prototype */{ 11109 11110 _dialogs : null, 11111 _clientLogObj : null, 11112 _wrapUpReasons : null, 11113 _phoneBooks : null, 11114 _workflows : null, 11115 _mediaPropertiesLayout : null, 11116 _queues : null, 11117 11118 /** 11119 * @class 11120 * The User represents a Finesse Agent or Supervisor. 11121 * 11122 * @param {Object} options 11123 * An object with the following properties:<ul> 11124 * <li><b>id:</b> The id of the object being constructed</li> 11125 * <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li> 11126 * <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li> 11127 * <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li> 11128 * <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li> 11129 * <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul> 11130 * <li><b>status:</b> {Number} The HTTP status code returned</li> 11131 * <li><b>content:</b> {String} Raw string of response</li> 11132 * <li><b>object:</b> {Object} Parsed object of response</li> 11133 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 11134 * <li><b>errorType:</b> {String} Type of error that was caught</li> 11135 * <li><b>errorMessage:</b> {String} Message associated with error</li> 11136 * </ul></li> 11137 * </ul></li> 11138 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 11139 * @augments finesse.restservices.RestBase 11140 * @constructs 11141 * @example 11142 * _user = new finesse.restservices.User({ 11143 * id: _id, 11144 * onLoad : _handleUserLoad, 11145 * onChange : _handleUserChange 11146 * }); 11147 **/ 11148 init: function (options) { 11149 this._super(options); 11150 }, 11151 11152 Callbacks: {}, 11153 11154 /** 11155 * @private 11156 * Gets the REST class for the current object - this is the User object. 11157 */ 11158 getRestClass: function () { 11159 return User; 11160 }, 11161 11162 /** 11163 * @private 11164 * Gets the REST type for the current object - this is a "User". 11165 */ 11166 getRestType: function () { 11167 return "User"; 11168 }, 11169 /** 11170 * @private 11171 * overloading this to return URI 11172 */ 11173 getXMPPNodePath: function () { 11174 return this.getRestUrl(); 11175 }, 11176 /** 11177 * @private 11178 * Returns whether this object supports subscriptions 11179 */ 11180 supportsSubscriptions: function () { 11181 return true; 11182 }, 11183 11184 /** 11185 * Getter for the firstName of this User. 11186 * @returns {String} 11187 * The firstName for this User 11188 */ 11189 getFirstName: function () { 11190 this.isLoaded(); 11191 return Utilities.convertNullToEmptyString(this.getData().firstName); 11192 }, 11193 11194 /** 11195 * Getter for the lastName of this User. 11196 * @returns {String} 11197 * The lastName for this User 11198 */ 11199 getLastName: function () { 11200 this.isLoaded(); 11201 return Utilities.convertNullToEmptyString(this.getData().lastName); 11202 }, 11203 11204 /** 11205 * Getter for the extension of this User. 11206 * @returns {String} 11207 * The extension, if any, of this User 11208 */ 11209 getExtension: function () { 11210 this.isLoaded(); 11211 return Utilities.convertNullToEmptyString(this.getData().extension); 11212 }, 11213 11214 /** 11215 * Getter for the id of the Team of this User 11216 * @returns {String} 11217 * The current (or last fetched) id of the Team of this User 11218 */ 11219 getTeamId: function () { 11220 this.isLoaded(); 11221 return this.getData().teamId; 11222 }, 11223 11224 /** 11225 * Getter for the name of the Team of this User 11226 * @returns {String} 11227 * The current (or last fetched) name of the Team of this User 11228 */ 11229 getTeamName: function () { 11230 this.isLoaded(); 11231 return this.getData().teamName; 11232 }, 11233 11234 /** 11235 * Is user an agent? 11236 * @returns {Boolean} True if user has role of agent, else false. 11237 */ 11238 hasAgentRole: function () { 11239 this.isLoaded(); 11240 return this.hasRole("Agent"); 11241 }, 11242 11243 /** 11244 * Is user a supervisor? 11245 * @returns {Boolean} True if user has role of supervisor, else false. 11246 */ 11247 hasSupervisorRole: function () { 11248 this.isLoaded(); 11249 return this.hasRole("Supervisor"); 11250 }, 11251 11252 /** 11253 * @private 11254 * Checks to see if user has "theRole" 11255 * @returns {Boolean} 11256 */ 11257 hasRole: function (theRole) { 11258 this.isLoaded(); 11259 var result = false, i, roles, len; 11260 11261 roles = this.getData().roles.role; 11262 len = roles.length; 11263 if (typeof roles === 'string') { 11264 if (roles === theRole) { 11265 result = true; 11266 } 11267 } else { 11268 for (i = 0; i < len ; i = i + 1) { 11269 if (roles[i] === theRole) { 11270 result = true; 11271 break; 11272 } 11273 } 11274 } 11275 11276 return result; 11277 }, 11278 11279 /** 11280 * Getter for the pending state of this User. 11281 * @returns {String} 11282 * The pending state of this User 11283 * @see finesse.restservices.User.States 11284 */ 11285 getPendingState: function () { 11286 this.isLoaded(); 11287 return Utilities.convertNullToEmptyString(this.getData().pendingState); 11288 }, 11289 11290 /** 11291 * Getter for the state of this User. 11292 * @returns {String} 11293 * The current (or last fetched) state of this User 11294 * @see finesse.restservices.User.States 11295 */ 11296 getState: function () { 11297 this.isLoaded(); 11298 return this.getData().state; 11299 }, 11300 11301 /** 11302 * Getter for the state change time of this User. 11303 * @returns {String} 11304 * The state change time of this User 11305 */ 11306 getStateChangeTime: function () { 11307 this.isLoaded(); 11308 return this.getData().stateChangeTime; 11309 }, 11310 11311 /** 11312 * Getter for the wrap-up mode of this User. 11313 * @see finesse.restservices.User.WrapUpMode 11314 * @returns {String} The wrap-up mode of this user that is a value of {@link finesse.restservices.User.WrapUpMode} 11315 */ 11316 getWrapUpOnIncoming: function () { 11317 this.isLoaded(); 11318 return this.getData().settings.wrapUpOnIncoming; 11319 }, 11320 11321 /** 11322 * Is User required to go into wrap-up? 11323 * @see finesse.restservices.User.WrapUpMode 11324 * @return {Boolean} 11325 * True if this agent is required to go into wrap-up. 11326 */ 11327 isWrapUpRequired: function () { 11328 return (this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED || 11329 this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED_WITH_WRAP_UP_DATA); 11330 }, 11331 11332 /** 11333 * Checks to see if the user is considered a mobile agent by checking for 11334 * the existence of the mobileAgent node. 11335 * @returns {Boolean} 11336 * True if this agent is a mobile agent. 11337 */ 11338 isMobileAgent: function () { 11339 this.isLoaded(); 11340 var ma = this.getData().mobileAgent; 11341 return ma !== null && typeof ma === "object"; 11342 }, 11343 11344 /** 11345 * Getter for the mobile agent work mode. 11346 * @returns {finesse.restservices.User.WorkMode} 11347 * If available, return the mobile agent work mode, otherwise null. 11348 */ 11349 getMobileAgentMode: function () { 11350 this.isLoaded(); 11351 if (this.isMobileAgent()) { 11352 return this.getData().mobileAgent.mode; 11353 } 11354 return null; 11355 }, 11356 11357 /** 11358 * Getter for the mobile agent dial number. 11359 * @returns {String} 11360 * If available, return the mobile agent dial number, otherwise null. 11361 */ 11362 getMobileAgentDialNumber: function () { 11363 this.isLoaded(); 11364 if (this.isMobileAgent()) { 11365 return this.getData().mobileAgent.dialNumber; 11366 } 11367 return null; 11368 }, 11369 11370 /** 11371 * Getter for a Dialogs collection object that is associated with User. 11372 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11373 * applicable when Object has not been previously created). 11374 * @returns {finesse.restservices.Dialogs} 11375 * A Dialogs collection object. 11376 */ 11377 getDialogs: function (callbacks) { 11378 var options = callbacks || {}; 11379 options.parentObj = this; 11380 this.isLoaded(); 11381 11382 if (this._dialogs === null) { 11383 this._dialogs = new Dialogs(options); 11384 } 11385 11386 return this._dialogs; 11387 }, 11388 11389 /** 11390 * @private 11391 * Getter for a ClientLog object that is associated with User. 11392 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11393 * applicable when Object has not been previously created). 11394 * @returns {finesse.restservices.ClientLog} 11395 * A ClientLog collection object. 11396 */ 11397 getClientLog: function (callbacks) { 11398 var options = callbacks || {}; 11399 options.parentObj = this; 11400 this.isLoaded(); 11401 11402 if (this._clientLogObj === null) { 11403 this._clientLogObj = new ClientLog(options); 11404 } 11405 else { 11406 if(options.onLoad && typeof options.onLoad === "function") { 11407 options.onLoad(this._clientLogObj); 11408 } 11409 } 11410 return this._clientLogObj; 11411 }, 11412 11413 /** 11414 * Getter for a Queues collection object that is associated with User. 11415 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11416 * applicable when Object has not been previously created). 11417 * @returns {finesse.restservices.Queues} 11418 * A Queues collection object. 11419 */ 11420 getQueues: function (callbacks) { 11421 var options = callbacks || {}; 11422 options.parentObj = this; 11423 this.isLoaded(); 11424 11425 if (this._queues === null) { 11426 this._queues = new Queues(options); 11427 } 11428 11429 return this._queues; 11430 }, 11431 11432 /** 11433 * Getter for a WrapUpReasons collection object that is associated with User. 11434 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11435 * applicable when Object has not been previously created). 11436 * @returns {finesse.restservices.WrapUpReasons} 11437 * A WrapUpReasons collection object. 11438 */ 11439 getWrapUpReasons: function (callbacks) { 11440 var options = callbacks || {}; 11441 options.parentObj = this; 11442 this.isLoaded(); 11443 11444 if (this._wrapUpReasons === null) { 11445 this._wrapUpReasons = new WrapUpReasons(options); 11446 } 11447 11448 return this._wrapUpReasons; 11449 }, 11450 11451 /** 11452 * Getter for a PhoneBooks collection object that is associated with User. 11453 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11454 * applicable when Object has not been previously created). 11455 * @returns {finesse.restservices.PhoneBooks} 11456 * A PhoneBooks collection object. 11457 */ 11458 getPhoneBooks: function (callbacks) { 11459 var options = callbacks || {}; 11460 options.parentObj = this; 11461 this.isLoaded(); 11462 11463 if (this._phoneBooks === null) { 11464 this._phoneBooks = new PhoneBooks(options); 11465 } 11466 11467 return this._phoneBooks; 11468 }, 11469 11470 /** 11471 * @private 11472 * Loads the Workflows collection object that is associated with User and 11473 * 'returns' them to the caller via the handlers. 11474 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11475 * applicable when Object has not been previously created). 11476 * @see finesse.restservices.Workflow 11477 * @see finesse.restservices.Workflows 11478 * @see finesse.restservices.RestCollectionBase 11479 */ 11480 loadWorkflows: function (callbacks) { 11481 var options = callbacks || {}; 11482 options.parentObj = this; 11483 this.isLoaded(); 11484 11485 if (this._workflows === null) { 11486 this._workflows = new Workflows(options); 11487 } else { 11488 this._workflows.refresh(); 11489 } 11490 11491 }, 11492 11493 /** 11494 * Getter for a MediaPropertiesLayout object that is associated with User. 11495 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11496 * applicable when Object has not been previously created). 11497 * @returns {finesse.restservices.MediaPropertiesLayout} 11498 * The MediaPropertiesLayout object associated with this user 11499 */ 11500 getMediaPropertiesLayout: function (callbacks) { 11501 var options = callbacks || {}; 11502 options.parentObj = this; 11503 options.id = this._id; 11504 11505 this.isLoaded(); 11506 if (this._mediaPropertiesLayout === null) { 11507 this._mediaPropertiesLayout = new MediaPropertiesLayout(options); 11508 } 11509 return this._mediaPropertiesLayout; 11510 }, 11511 11512 /** 11513 * Getter for the supervised Teams this User (Supervisor) supervises, if any. 11514 * @see finesse.restservices.Team 11515 * @returns {Array} 11516 * An array of Teams supervised by this User (Supervisor) 11517 */ 11518 getSupervisedTeams: function () { 11519 this.isLoaded(); 11520 11521 try { 11522 return Utilities.getArray(this.getData().teams.Team); 11523 } catch (e) { 11524 return []; 11525 } 11526 11527 }, 11528 11529 /** 11530 * Perform an agent login for this user, associating him with the 11531 * specified extension. 11532 * @param {Object} params 11533 * An object containing properties for agent login. 11534 * @param {String} params.extension 11535 * The extension to associate with this user 11536 * @param {Object} [params.mobileAgent] 11537 * A mobile agent object containing the mode and dial number properties. 11538 * @param {finesse.interfaces.RequestHandlers} params.handlers 11539 * @see finesse.interfaces.RequestHandlers 11540 * @returns {finesse.restservices.User} 11541 * This User object, to allow cascading 11542 * @private 11543 */ 11544 _login: function (params) { 11545 var handlers, contentBody = {}, 11546 restType = this.getRestType(); 11547 11548 // Protect against null dereferencing. 11549 params = params || {}; 11550 11551 contentBody[restType] = { 11552 "state": User.States.LOGIN, 11553 "extension": params.extension 11554 }; 11555 11556 // Create mobile agent node if available. 11557 if (typeof params.mobileAgent === "object") { 11558 contentBody[restType].mobileAgent = { 11559 "mode": params.mobileAgent.mode, 11560 "dialNumber": params.mobileAgent.dialNumber 11561 }; 11562 } 11563 11564 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 11565 handlers = params.handlers || {}; 11566 11567 this.restRequest(this.getRestUrl(), { 11568 method: 'PUT', 11569 success: handlers.success, 11570 error: handlers.error, 11571 content: contentBody 11572 }); 11573 11574 return this; // Allow cascading 11575 }, 11576 11577 /** 11578 * Perform an agent login for this user, associating him with the 11579 * specified extension. 11580 * @param {String} extension 11581 * The extension to associate with this user 11582 * @param {finesse.interfaces.RequestHandlers} handlers 11583 * An object containing the handlers for the request 11584 * @returns {finesse.restservices.User} 11585 * This User object, to allow cascading 11586 */ 11587 login: function (extension, handlers) { 11588 this.isLoaded(); 11589 var params = { 11590 "extension": extension, 11591 "handlers": handlers 11592 }; 11593 return this._login(params); 11594 }, 11595 11596 /** 11597 * Perform an agent login for this user, associating him with the 11598 * specified extension. 11599 * @param {String} extension 11600 * The extension to associate with this user 11601 * @param {String} mode 11602 * The mobile agent work mode as defined in finesse.restservices.User.WorkMode. 11603 * @param {String} extension 11604 * The external dial number desired to be used by the mobile agent. 11605 * @param {finesse.interfaces.RequestHandlers} handlers 11606 * An object containing the handlers for the request 11607 * @returns {finesse.restservices.User} 11608 * This User object, to allow cascading 11609 */ 11610 loginMobileAgent: function (extension, mode, dialNumber, handlers) { 11611 this.isLoaded(); 11612 var params = { 11613 "extension": extension, 11614 "mobileAgent": { 11615 "mode": mode, 11616 "dialNumber": dialNumber 11617 }, 11618 "handlers": handlers 11619 }; 11620 return this._login(params); 11621 }, 11622 11623 /** 11624 * Perform an agent logout for this user. 11625 * @param {String} reasonCode 11626 * The reason this user is logging out. Pass null for no reason. 11627 * @param {finesse.interfaces.RequestHandlers} handlers 11628 * An object containing the handlers for the request 11629 * @returns {finesse.restservices.User} 11630 * This User object, to allow cascading 11631 */ 11632 logout: function (reasonCode, handlers) { 11633 return this.setState("LOGOUT", reasonCode, handlers); 11634 }, 11635 11636 /** 11637 * Set the state of the user. 11638 * @param {String} newState 11639 * The state you are setting 11640 * @param {ReasonCode} reasonCode 11641 * The reason this user is logging out. Pass null for no reason. 11642 * @param {finesse.interfaces.RequestHandlers} handlers 11643 * An object containing the handlers for the request 11644 * @see finesse.restservices.User.States 11645 * @returns {finesse.restservices.User} 11646 * This User object, to allow cascading 11647 */ 11648 setState: function (newState, reasonCode, handlers) { 11649 this.isLoaded(); 11650 11651 var options, contentBody = {}; 11652 11653 if (!reasonCode) { 11654 contentBody[this.getRestType()] = { 11655 "state": newState 11656 }; 11657 } else { 11658 contentBody[this.getRestType()] = { 11659 "state": newState, 11660 "reasonCodeId": reasonCode.id 11661 }; 11662 } 11663 11664 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 11665 handlers = handlers || {}; 11666 11667 options = { 11668 method: 'PUT', 11669 success: handlers.success, 11670 error: handlers.error, 11671 content: contentBody 11672 }; 11673 11674 // After removing the selective 202 handling, we should be able to just use restRequest 11675 this.restRequest(this.getRestUrl(), options); 11676 11677 return this; // Allow cascading 11678 }, 11679 11680 /** 11681 * Make call to a particular phone number. 11682 * 11683 * @param {String} 11684 * The number to call 11685 * @param {finesse.interfaces.RequestHandlers} handlers 11686 * An object containing the handlers for the request 11687 * @returns {finesse.restservices.User} 11688 * This User object, to allow cascading 11689 */ 11690 makeCall: function (number, handlers) { 11691 this.isLoaded(); 11692 11693 this.getDialogs().createNewCallDialog(number, this.getExtension(), handlers); 11694 11695 return this; // Allow cascading 11696 }, 11697 11698 /** 11699 * Make a silent monitor call to a particular agent's phone number. 11700 * 11701 * @param {String} 11702 * The number to call 11703 * @param {finesse.interfaces.RequestHandlers} handlers 11704 * An object containing the handlers for the request 11705 * @returns {finesse.restservices.User} 11706 * This User object, to allow cascading 11707 */ 11708 makeSMCall: function (number, handlers) { 11709 this.isLoaded(); 11710 11711 var actionType = "SILENT_MONITOR"; 11712 11713 this.getDialogs().createNewSuperviseCallDialog(number, this.getExtension(), actionType, handlers); 11714 11715 return this; // Allow cascading 11716 }, 11717 11718 11719 /** 11720 * Make a silent monitor call to a particular agent's phone number. 11721 * 11722 * @param {String} 11723 * The number to call 11724 * @param {String} dialogUri 11725 * The associated dialog uri of SUPERVISOR_MONITOR call 11726 * @param {finesse.interfaces.RequestHandlers} handlers 11727 * An object containing the handlers for the request 11728 * @see finesse.restservices.dialog 11729 * @returns {finesse.restservices.User} 11730 * This User object, to allow cascading 11731 */ 11732 makeBargeCall:function (number, dialogURI, handlers) { 11733 this.isLoaded(); 11734 var actionType = "BARGE_CALL"; 11735 this.getDialogs().createNewBargeCall( this.getExtension(), number, actionType, dialogURI,handlers); 11736 11737 return this; // Allow cascading 11738 }, 11739 11740 /** 11741 * Returns true if the user's current state will result in a pending state change. A pending state 11742 * change is a request to change state that does not result in an immediate state change. For 11743 * example if an agent attempts to change to the NOT_READY state while in the TALKING state, the 11744 * agent will not change state until the call ends. 11745 * 11746 * The current set of states that result in pending state changes is as follows: 11747 * TALKING 11748 * HOLD 11749 * RESERVED_OUTBOUND_PREVIEW 11750 * @returns {Boolean} True if there is a pending state change. 11751 * @see finesse.restservices.User.States 11752 */ 11753 isPendingStateChange: function () { 11754 var state = this.getState(); 11755 return state && ((state === User.States.TALKING) || (state === User.States.HOLD) || (state === User.States.RESERVED_OUTBOUND_PREVIEW)); 11756 }, 11757 11758 /** 11759 * Returns true if the user's current state is WORK or WORK_READY. This is used so 11760 * that a pending state is not cleared when moving into wrap up (work) mode. 11761 * Note that we don't add this as a pending state, since changes while in wrap up 11762 * occur immediately (and we don't want any "pending state" to flash on screen. 11763 * 11764 * @see finesse.restservices.User.States 11765 * @returns {Boolean} True if user is in wrap-up mode. 11766 */ 11767 isWrapUp: function () { 11768 var state = this.getState(); 11769 return state && ((state === User.States.WORK) || (state === User.States.WORK_READY)); 11770 }, 11771 11772 /** 11773 * @private 11774 * Parses a uriString to retrieve the id portion 11775 * @param {String} uriString 11776 * @return {String} id 11777 */ 11778 _parseIdFromUriString : function (uriString) { 11779 return Utilities.getId(uriString); 11780 }, 11781 11782 /** 11783 * Gets the user's Reason Code label. 11784 * Works for both Not Ready and Logout reason codes 11785 * @return {String} the reason code label, or empty string if none 11786 */ 11787 getReasonCodeLabel : function () { 11788 this.isLoaded(); 11789 11790 if (this.getData().reasonCode) { 11791 return this.getData().reasonCode.label; 11792 } else { 11793 return ""; 11794 } 11795 }, 11796 11797 /** 11798 * Gets the user's Not Ready reason code. 11799 * @return {String} Reason Code Id, or undefined if not set or indeterminate 11800 */ 11801 getNotReadyReasonCodeId : function () { 11802 this.isLoaded(); 11803 11804 var reasoncodeIdResult, finesseServerReasonCodeId; 11805 finesseServerReasonCodeId = this.getData().reasonCodeId; 11806 11807 //FinesseServer will give "-l" => we will set to undefined (for convenience) 11808 if (finesseServerReasonCodeId !== "-1") { 11809 reasoncodeIdResult = finesseServerReasonCodeId; 11810 } 11811 11812 return reasoncodeIdResult; 11813 }, 11814 11815 /** 11816 * Performs a GET against the Finesse server looking up the reasonCodeId specified. 11817 * Note that there is no return value; use the success handler to process a 11818 * valid return. 11819 * @param {finesse.interfaces.RequestHandlers} handlers 11820 * An object containing the handlers for the request 11821 * @param {String} reasonCodeId The id for the reason code to lookup 11822 * 11823 */ 11824 getReasonCodeById : function (handlers, reasonCodeId) 11825 { 11826 var self = this, contentBody, reasonCode, url; 11827 contentBody = {}; 11828 11829 url = this.getRestUrl() + "/ReasonCode/" + reasonCodeId; 11830 this.restRequest(url, { 11831 method: 'GET', 11832 success: function (rsp) { 11833 reasonCode = { 11834 uri: rsp.object.ReasonCode.uri, 11835 label: rsp.object.ReasonCode.label, 11836 id: self._parseIdFromUriString(rsp.object.ReasonCode.uri) 11837 }; 11838 handlers.success(reasonCode); 11839 }, 11840 error: function (rsp) { 11841 handlers.error(rsp); 11842 }, 11843 content: contentBody 11844 }); 11845 }, 11846 11847 /** 11848 * Performs a GET against Finesse server retrieving all the specified type of reason codes. 11849 * @param {String} type (LOGOUT or NOT_READY) 11850 * @param {finesse.interfaces.RequestHandlers} handlers 11851 * An object containing the handlers for the request 11852 */ 11853 _getReasonCodesByType : function (type, handlers) 11854 { 11855 var self = this, contentBody = {}, url, reasonCodes, i, reasonCodeArray; 11856 11857 url = this.getRestUrl() + "/ReasonCodes?category=" + type; 11858 this.restRequest(url, { 11859 method: 'GET', 11860 success: function (rsp) { 11861 reasonCodes = []; 11862 11863 reasonCodeArray = rsp.object.ReasonCodes.ReasonCode; 11864 if (reasonCodeArray === undefined) { 11865 reasonCodes = undefined; 11866 } else if (reasonCodeArray[0] !== undefined) { 11867 for (i = 0; i < reasonCodeArray.length; i = i + 1) { 11868 reasonCodes[i] = { 11869 label: rsp.object.ReasonCodes.ReasonCode[i].label, 11870 id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode[i].uri) 11871 }; 11872 } 11873 } else { 11874 reasonCodes[0] = { 11875 label: rsp.object.ReasonCodes.ReasonCode.label, 11876 id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode.uri) 11877 }; 11878 } 11879 handlers.success(reasonCodes); 11880 }, 11881 error: function (rsp) { 11882 handlers.error(rsp); 11883 }, 11884 content: contentBody 11885 }); 11886 }, 11887 11888 /** 11889 * Performs a GET against Finesse server retrieving all the Signout reason codes. 11890 * Note that there is no return value; use the success handler to process a 11891 * valid return. 11892 * @param {finesse.interfaces.RequestHandlers} handlers 11893 * An object containing the handlers for the request 11894 */ 11895 getSignoutReasonCodes : function (handlers) 11896 { 11897 this._getReasonCodesByType("LOGOUT", handlers); 11898 }, 11899 11900 /** 11901 * Performs a GET against Finesse server retrieving all the Not Ready reason codes. 11902 * Note that there is no return value; use the success handler to process a 11903 * valid return. 11904 * @param {finesse.interfaces.RequestHandlers} handlers 11905 * An object containing the handlers for the request 11906 */ 11907 getNotReadyReasonCodes : function (handlers) 11908 { 11909 this._getReasonCodesByType("NOT_READY", handlers); 11910 } 11911 }); 11912 11913 User.States = /** @lends finesse.restservices.User.States.prototype */ { 11914 /** 11915 * User Login. Note that while this is an action, is not technically a state, since a 11916 * logged-in User will always be in a specific state (READY, NOT_READY, TALKING, etc.). 11917 */ 11918 LOGIN: "LOGIN", 11919 /** 11920 * User is logged out. 11921 */ 11922 LOGOUT: "LOGOUT", 11923 /** 11924 * User is not ready. Note that in UCCX implementations, the user is in this state while on a non-routed call. 11925 */ 11926 NOT_READY: "NOT_READY", 11927 /** 11928 * User is ready for calls. 11929 */ 11930 READY: "READY", 11931 /** 11932 * User has a call coming in, but has not answered it. 11933 */ 11934 RESERVED: "RESERVED", 11935 /** 11936 * User has an outbound call being made, but has not been connected to it. 11937 */ 11938 RESERVED_OUTBOUND: "RESERVED_OUTBOUND", 11939 /** 11940 * User has an outbound call's preview information being displayed, but has not acted on it. 11941 */ 11942 RESERVED_OUTBOUND_PREVIEW: "RESERVED_OUTBOUND_PREVIEW", 11943 /** 11944 * User is on a call. Note that in UCCX implementations, this is for routed calls only. 11945 */ 11946 TALKING: "TALKING", 11947 /** 11948 * User is on hold. Note that in UCCX implementations, the user remains in TALKING state while on hold. 11949 */ 11950 HOLD: "HOLD", 11951 /** 11952 * User is wrap-up/work mode. This mode is typically configured to time out, after which the user becomes NOT_READY. 11953 */ 11954 WORK: "WORK", 11955 /** 11956 * This is the same as WORK, except that after time out user becomes READY. 11957 */ 11958 WORK_READY: "WORK_READY", 11959 /** 11960 * @class Possible User state values. 11961 * @constructs 11962 */ 11963 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 11964 11965 }; 11966 11967 User.WorkMode = { /** @lends finesse.restservices.User.WorkMode.prototype */ 11968 /** 11969 * Mobile agent is connected (dialed) for each incoming call received. 11970 */ 11971 CALL_BY_CALL: "CALL_BY_CALL", 11972 /** 11973 * Mobile agent is connected (dialed) at login. 11974 */ 11975 NAILED_CONNECTION: "NAILED_CONNECTION", 11976 /** 11977 * @class Possible Mobile Agent Work Mode Types. 11978 * @constructs 11979 */ 11980 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 11981 11982 }; 11983 11984 User.WrapUpMode = { /** @lends finesse.restservices.User.WrapUpMode.prototype */ 11985 /** 11986 * Agent must go into wrap-up when call ends 11987 */ 11988 REQUIRED: "REQUIRED", 11989 /** 11990 * Agent must go into wrap-up when call ends and must enter wrap-up data 11991 */ 11992 REQUIRED_WITH_WRAP_UP_DATA: "REQUIRED_WITH_WRAP_UP_DATA", 11993 /** 11994 * Agent can choose to go into wrap-up on a call-by-call basis when the call ends 11995 */ 11996 OPTIONAL: "OPTIONAL", 11997 /** 11998 * Agent is not allowed to go into wrap-up when call ends. 11999 */ 12000 NOT_ALLOWED: "NOT_ALLOWED", 12001 /** 12002 * @class Possible Wrap-up Mode Types. 12003 * @constructs 12004 */ 12005 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 12006 12007 }; 12008 12009 window.finesse = window.finesse || {}; 12010 window.finesse.restservices = window.finesse.restservices || {}; 12011 window.finesse.restservices.User = User; 12012 12013 return User; 12014 })); 12015 12016 /** 12017 * JavaScript representation of the Finesse Users collection 12018 * object which contains a list of Users objects. 12019 * 12020 * @requires finesse.clientservices.ClientServices 12021 * @requires Class 12022 * @requires finesse.FinesseBase 12023 * @requires finesse.restservices.RestBase 12024 * @requires finesse.restservices.RestCollectionBase 12025 * @requires finesse.restservices.User 12026 */ 12027 12028 /** @private */ 12029 (function (factory) { 12030 12031 12032 // Define as an AMD module if possible 12033 if ( typeof define === 'function' && define.amd ) 12034 { 12035 define('restservices/Users', ['restservices/RestCollectionBase', 12036 'restservices/RestBase', 12037 'restservices/User'], factory ); 12038 } 12039 /* Define using browser globals otherwise 12040 * Prevent multiple instantiations if the script is loaded twice 12041 */ 12042 else 12043 { 12044 factory(finesse.restservices.RestCollectionBase, finesse.restservices.RestBase, finesse.restservices.User); 12045 } 12046 }(function (RestCollectionBase, RestBase, User) { 12047 12048 var Users = RestCollectionBase.extend(/** @lends finesse.restservices.Users.prototype */{ 12049 12050 /** 12051 * @class 12052 * JavaScript representation of a Users collection object. 12053 * While there is no method provided to retrieve all Users, this collection is 12054 * used to return the Users in a supervised Team. 12055 * @augments finesse.restservices.RestCollectionBase 12056 * @constructs 12057 * @see finesse.restservices.Team 12058 * @see finesse.restservices.User 12059 * @see finesse.restservices.User#getSupervisedTeams 12060 * @example 12061 * // Note: The following method gets an Array of Teams, not a Collection. 12062 * _teams = _user.getSupervisedTeams(); 12063 * if (_teams.length > 0) { 12064 * _team0Users = _teams[0].getUsers(); 12065 * } 12066 */ 12067 _fakeConstuctor: function () { 12068 /* This is here to hide the real init constructor from the public docs */ 12069 }, 12070 12071 /** 12072 * @private 12073 * JavaScript representation of the Finesse Users collection 12074 * object which contains a list of Users objects. 12075 * 12076 * @param {Object} options 12077 * An object with the following properties:<ul> 12078 * <li><b>id:</b> The id of the object being constructed</li> 12079 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 12080 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 12081 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 12082 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 12083 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 12084 * <li><b>status:</b> {Number} The HTTP status code returned</li> 12085 * <li><b>content:</b> {String} Raw string of response</li> 12086 * <li><b>object:</b> {Object} Parsed object of response</li> 12087 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 12088 * <li><b>errorType:</b> {String} Type of error that was caught</li> 12089 * <li><b>errorMessage:</b> {String} Message associated with error</li> 12090 * </ul></li> 12091 * </ul></li> 12092 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 12093 **/ 12094 init: function (options) { 12095 this._super(options); 12096 }, 12097 12098 /** 12099 * @private 12100 * Gets the REST class for the current object - this is the Users class. 12101 */ 12102 getRestClass: function () { 12103 return Users; 12104 }, 12105 12106 /** 12107 * @private 12108 * Gets the REST class for the objects that make up the collection. - this 12109 * is the User class. 12110 */ 12111 getRestItemClass: function () { 12112 return User; 12113 }, 12114 12115 /** 12116 * @private 12117 * Gets the REST type for the current object - this is a "Users". 12118 */ 12119 getRestType: function () { 12120 return "Users"; 12121 }, 12122 12123 /** 12124 * @private 12125 * Gets the REST type for the objects that make up the collection - this is "User". 12126 */ 12127 getRestItemType: function () { 12128 return "User"; 12129 }, 12130 12131 /** 12132 * @private 12133 * Gets the node path for the current object - this is the team Users node 12134 * @returns {String} The node path 12135 */ 12136 getXMPPNodePath: function () { 12137 return this.getRestUrl(); 12138 }, 12139 12140 /** 12141 * @private 12142 * Overloading _doGET to reroute the GET to /Team/id from /Team/id/Users 12143 * This needs to be done because the GET /Team/id/Users API is missing 12144 * @returns {Users} This Users (collection) object to allow cascading 12145 */ 12146 _doGET: function (handlers) { 12147 var _this = this; 12148 handlers = handlers || {}; 12149 // Only do this for /Team/id/Users 12150 if (this._restObj && this._restObj.getRestType() === "Team") { 12151 this._restObj._doGET({ 12152 success: function (rspObj) { 12153 // Making sure the response was a valid Team 12154 if (_this._restObj._validate(rspObj.object)) { 12155 // Shimmying the response to look like a Users collection by extracting it from the Team response 12156 rspObj.object[_this.getRestType()] = rspObj.object[_this._restObj.getRestType()][_this.getRestType().toLowerCase()]; 12157 handlers.success(rspObj); 12158 } else { 12159 handlers.error(rspObj); 12160 } 12161 }, 12162 error: handlers.error 12163 }); 12164 return this; // Allow cascading 12165 } else { 12166 return this._super(handlers); 12167 } 12168 }, 12169 12170 /** 12171 * @private 12172 * Override default to indicates that the collection doesn't support making 12173 * requests. 12174 */ 12175 supportsRequests: false, 12176 12177 /** 12178 * @private 12179 * Indicates that this collection handles the subscription for its items 12180 */ 12181 handlesItemSubscription: true, 12182 12183 /** 12184 * @private 12185 * Override default to indicate that we need to subscribe explicitly 12186 */ 12187 explicitSubscription: true 12188 12189 }); 12190 12191 window.finesse = window.finesse || {}; 12192 window.finesse.restservices = window.finesse.restservices || {}; 12193 window.finesse.restservices.Users = Users; 12194 12195 return Users; 12196 })); 12197 12198 /** 12199 * JavaScript representation of the Finesse Team Not Ready Reason Code Assignment object. 12200 * 12201 * @requires finesse.clientservices.ClientServices 12202 * @requires Class 12203 * @requires finesse.FinesseBase 12204 * @requires finesse.restservices.RestBase 12205 */ 12206 12207 /** @private */ 12208 (function (factory) { 12209 12210 12211 // Define as an AMD module if possible 12212 if ( typeof define === 'function' && define.amd ) 12213 { 12214 define('restservices/TeamNotReadyReasonCode', ['restservices/RestBase'], factory ); 12215 } 12216 /* Define using browser globals otherwise 12217 * Prevent multiple instantiations if the script is loaded twice 12218 */ 12219 else 12220 { 12221 factory(finesse.restservices.RestBase); 12222 } 12223 }(function (RestBase) { 12224 12225 var TeamNotReadyReasonCode = RestBase.extend( { 12226 12227 /** 12228 * @class 12229 * JavaScript representation of a Team Not Ready ReasonCode object. Also exposes 12230 * methods to operate on the object against the server. 12231 * 12232 * @param {Object} options 12233 * An object with the following properties:<ul> 12234 * <li><b>id:</b> The id of the object being constructed</li> 12235 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 12236 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 12237 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 12238 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 12239 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 12240 * <li><b>status:</b> {Number} The HTTP status code returned</li> 12241 * <li><b>content:</b> {String} Raw string of response</li> 12242 * <li><b>object:</b> {Object} Parsed object of response</li> 12243 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 12244 * <li><b>errorType:</b> {String} Type of error that was caught</li> 12245 * <li><b>errorMessage:</b> {String} Message associated with error</li> 12246 * </ul></li> 12247 * </ul></li> 12248 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 12249 * @constructs 12250 **/ 12251 init: function (options) { 12252 this._super(options); 12253 }, 12254 12255 /** 12256 * @private 12257 * Gets the REST class for the current object - this is the TeamNotReadyReasonCode class. 12258 * @returns {Object} The TeamNotReadyReasonCode class. 12259 */ 12260 getRestClass: function () { 12261 return TeamNotReadyReasonCode; 12262 }, 12263 12264 /** 12265 * @private 12266 * Gets the REST type for the current object - this is a "ReasonCode". 12267 * @returns {String} The ReasonCode string. 12268 */ 12269 getRestType: function () { 12270 return "ReasonCode"; 12271 }, 12272 12273 /** 12274 * @private 12275 * Override default to indicate that this object doesn't support making 12276 * requests. 12277 */ 12278 supportsRequests: false, 12279 12280 /** 12281 * @private 12282 * Override default to indicate that this object doesn't support subscriptions. 12283 */ 12284 supportsSubscriptions: false, 12285 12286 /** 12287 * Getter for the category. 12288 * @returns {String} The category. 12289 */ 12290 getCategory: function () { 12291 this.isLoaded(); 12292 return this.getData().category; 12293 }, 12294 12295 /** 12296 * Getter for the code. 12297 * @returns {String} The code. 12298 */ 12299 getCode: function () { 12300 this.isLoaded(); 12301 return this.getData().code; 12302 }, 12303 12304 /** 12305 * Getter for the label. 12306 * @returns {String} The label. 12307 */ 12308 getLabel: function () { 12309 this.isLoaded(); 12310 return this.getData().label; 12311 }, 12312 12313 /** 12314 * Getter for the forAll value. 12315 * @returns {String} The forAll. 12316 */ 12317 getForAll: function () { 12318 this.isLoaded(); 12319 return this.getData().forAll; 12320 }, 12321 12322 /** 12323 * Getter for the Uri value. 12324 * @returns {String} The Uri. 12325 */ 12326 getUri: function () { 12327 this.isLoaded(); 12328 return this.getData().uri; 12329 } 12330 12331 }); 12332 12333 window.finesse = window.finesse || {}; 12334 window.finesse.restservices = window.finesse.restservices || {}; 12335 window.finesse.restservices.TeamNotReadyReasonCode = TeamNotReadyReasonCode; 12336 12337 return TeamNotReadyReasonCode; 12338 })); 12339 12340 /** 12341 * JavaScript representation of the Finesse TeamNotReadyReasonCodes collection 12342 * object which contains a list of TeamNotReadyReasonCode objects. 12343 * 12344 * @requires finesse.clientservices.ClientServices 12345 * @requires Class 12346 * @requires finesse.FinesseBase 12347 * @requires finesse.restservices.RestBase 12348 * @requires finesse.restservices.Dialog 12349 * @requires finesse.restservices.RestCollectionBase 12350 */ 12351 12352 /** @private */ 12353 (function (factory) { 12354 12355 12356 // Define as an AMD module if possible 12357 if ( typeof define === 'function' && define.amd ) 12358 { 12359 define('restservices/TeamNotReadyReasonCodes', ['restservices/RestCollectionBase', 12360 'restservices/RestBase', 12361 'restservices/TeamNotReadyReasonCode'], factory ); 12362 } 12363 /* Define using browser globals otherwise 12364 * Prevent multiple instantiations if the script is loaded twice 12365 */ 12366 else 12367 { 12368 factory(finesse.restservices.RestCollectionBase, finesse.restservices.RestBase, finesse.restservices.TeamNotReadyReasonCode); 12369 } 12370 }(function (RestCollectionBase, RestBase, TeamNotReadyReasonCode) { 12371 12372 var TeamNotReadyReasonCodes = RestCollectionBase.extend( { 12373 12374 /** 12375 * @class 12376 * JavaScript representation of a TeamNotReadyReasonCodes collection object. Also exposes 12377 * methods to operate on the object against the server. 12378 * 12379 * @param {Object} options 12380 * An object with the following properties:<ul> 12381 * <li><b>id:</b> The id of the object being constructed</li> 12382 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 12383 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 12384 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 12385 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 12386 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 12387 * <li><b>status:</b> {Number} The HTTP status code returned</li> 12388 * <li><b>content:</b> {String} Raw string of response</li> 12389 * <li><b>object:</b> {Object} Parsed object of response</li> 12390 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 12391 * <li><b>errorType:</b> {String} Type of error that was caught</li> 12392 * <li><b>errorMessage:</b> {String} Message associated with error</li> 12393 * </ul></li> 12394 * </ul></li> 12395 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 12396 * @augments finesse.restservices.RestCollectionBase 12397 * @constructs 12398 **/ 12399 init: function (options) { 12400 this._super(options); 12401 }, 12402 12403 /** 12404 * @private 12405 * Gets the REST class for the current object - this is the TeamNotReadyReasonCodes class. 12406 */ 12407 getRestClass: function () { 12408 return TeamNotReadyReasonCodes; 12409 }, 12410 12411 /** 12412 * @private 12413 * Gets the REST class for the objects that make up the collection. - this 12414 * is the TeamNotReadyReasonCode class. 12415 */ 12416 getRestItemClass: function () { 12417 return TeamNotReadyReasonCode; 12418 }, 12419 12420 /** 12421 * @private 12422 * Gets the REST type for the current object - this is a "ReasonCodes". 12423 */ 12424 getRestType: function () { 12425 return "ReasonCodes"; 12426 }, 12427 12428 /** 12429 * @private 12430 * Overrides the parent class. Returns the url for the NotReadyReasonCodes resource 12431 */ 12432 getRestUrl: function () { 12433 // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY"); 12434 var restObj = this._restObj, 12435 restUrl = ""; 12436 //Prepend the base REST object if one was provided. 12437 //Otherwise prepend with the default webapp name. 12438 if (restObj instanceof RestBase) { 12439 restUrl += restObj.getRestUrl(); 12440 } 12441 else { 12442 restUrl += "/finesse/api"; 12443 } 12444 //Append the REST type. 12445 restUrl += "/ReasonCodes?category=NOT_READY"; 12446 //Append ID if it is not undefined, null, or empty. 12447 if (this._id) { 12448 restUrl += "/" + this._id; 12449 } 12450 return restUrl; 12451 }, 12452 12453 /** 12454 * @private 12455 * Gets the REST type for the objects that make up the collection - this is "ReasonCode". 12456 */ 12457 getRestItemType: function () { 12458 return "ReasonCode"; 12459 }, 12460 12461 /** 12462 * @private 12463 * Override default to indicates that the collection supports making 12464 * requests. 12465 */ 12466 supportsRequests: true, 12467 12468 /** 12469 * @private 12470 * Override default to indicate that this object doesn't support subscriptions. 12471 */ 12472 supportsRestItemSubscriptions: false, 12473 12474 /** 12475 * @private 12476 * Retrieve the Not Ready Reason Codes. 12477 * 12478 * @returns {TeamNotReadyReasonCodes} 12479 * This TeamNotReadyReasonCodes object to allow cascading. 12480 */ 12481 get: function () { 12482 // set loaded to false so it will rebuild the collection after the get 12483 this._loaded = false; 12484 // reset collection 12485 this._collection = {}; 12486 // perform get 12487 this._synchronize(); 12488 return this; 12489 }, 12490 12491 /** 12492 * @private 12493 * Set up the PutSuccessHandler for TeamNotReadyReasonCodes 12494 * @param {Object} reasonCodes 12495 * @param {String} contentBody 12496 * @param successHandler 12497 * @return {function} 12498 */ 12499 createPutSuccessHandler: function (reasonCodes, contentBody, successHandler) { 12500 return function (rsp) { 12501 // Update internal structure based on response. Here we 12502 // inject the contentBody from the PUT request into the 12503 // rsp.object element to mimic a GET as a way to take 12504 // advantage of the existing _processResponse method. 12505 rsp.object = contentBody; 12506 reasonCodes._processResponse(rsp); 12507 12508 //Remove the injected contentBody object before cascading response 12509 rsp.object = {}; 12510 12511 //cascade response back to consumer's response handler 12512 successHandler(rsp); 12513 }; 12514 }, 12515 12516 /** 12517 * @private 12518 * Perform the REST API PUT call to update the reason code assignments for the team 12519 * @param {string[]} newValues 12520 * @param handlers 12521 */ 12522 update: function (newValues, handlers) { 12523 this.isLoaded(); 12524 var contentBody = {}, contentBodyInner = [], i, innerObject = {}; 12525 12526 contentBody[this.getRestType()] = { 12527 }; 12528 12529 for (i in newValues) { 12530 if (newValues.hasOwnProperty(i)) { 12531 innerObject = { 12532 "uri": newValues[i] 12533 }; 12534 contentBodyInner.push(innerObject); 12535 } 12536 } 12537 12538 contentBody[this.getRestType()] = { 12539 "ReasonCode" : contentBodyInner 12540 }; 12541 12542 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 12543 handlers = handlers || {}; 12544 12545 this.restRequest(this.getRestUrl(), { 12546 method: 'PUT', 12547 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 12548 error: handlers.error, 12549 content: contentBody 12550 }); 12551 12552 return this; // Allow cascading 12553 } 12554 }); 12555 12556 window.finesse = window.finesse || {}; 12557 window.finesse.restservices = window.finesse.restservices || {}; 12558 window.finesse.restservices.TeamNotReadyReasonCodes = TeamNotReadyReasonCodes; 12559 12560 return TeamNotReadyReasonCodes; 12561 })); 12562 12563 /** 12564 * JavaScript representation of the Finesse Team Wrap Up Reason object. 12565 * 12566 * @requires finesse.clientservices.ClientServices 12567 * @requires Class 12568 * @requires finesse.FinesseBase 12569 * @requires finesse.restservices.RestBase 12570 */ 12571 /** @private */ 12572 (function (factory) { 12573 12574 12575 // Define as an AMD module if possible 12576 if ( typeof define === 'function' && define.amd ) 12577 { 12578 define('restservices/TeamWrapUpReason',['restservices/RestBase'], factory); 12579 } 12580 /* Define using browser globals otherwise 12581 * Prevent multiple instantiations if the script is loaded twice 12582 */ 12583 else 12584 { 12585 factory(finesse.restservices.RestBase); 12586 } 12587 }(function (RestBase) { 12588 12589 var TeamWrapUpReason = RestBase.extend({ 12590 12591 /** 12592 * @class 12593 * JavaScript representation of a TeamWrapUpReason object. Also exposes 12594 * methods to operate on the object against the server. 12595 * 12596 * @param {Object} options 12597 * An object with the following properties:<ul> 12598 * <li><b>id:</b> The id of the object being constructed</li> 12599 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 12600 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 12601 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 12602 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 12603 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 12604 * <li><b>status:</b> {Number} The HTTP status code returned</li> 12605 * <li><b>content:</b> {String} Raw string of response</li> 12606 * <li><b>object:</b> {Object} Parsed object of response</li> 12607 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 12608 * <li><b>errorType:</b> {String} Type of error that was caught</li> 12609 * <li><b>errorMessage:</b> {String} Message associated with error</li> 12610 * </ul></li> 12611 * </ul></li> 12612 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 12613 * @constructs 12614 **/ 12615 init: function (options) { 12616 this._super(options); 12617 }, 12618 12619 /** 12620 * @private 12621 * Gets the REST class for the current object - this is the TeamWrapUpReason class. 12622 * @returns {Object} The TeamWrapUpReason class. 12623 */ 12624 getRestClass: function () { 12625 return TeamWrapUpReason; 12626 }, 12627 12628 /** 12629 * @private 12630 * Gets the REST type for the current object - this is a "WrapUpReason". 12631 * @returns {String} The WrapUpReason string. 12632 */ 12633 getRestType: function () { 12634 return "WrapUpReason"; 12635 }, 12636 12637 /** 12638 * @private 12639 * Override default to indicate that this object doesn't support making 12640 * requests. 12641 */ 12642 supportsRequests: false, 12643 12644 /** 12645 * @private 12646 * Override default to indicate that this object doesn't support subscriptions. 12647 */ 12648 supportsSubscriptions: false, 12649 12650 /** 12651 * Getter for the label. 12652 * @returns {String} The label. 12653 */ 12654 getLabel: function () { 12655 this.isLoaded(); 12656 return this.getData().label; 12657 }, 12658 12659 /** 12660 * @private 12661 * Getter for the forAll value. 12662 * @returns {Boolean} True if global 12663 */ 12664 getForAll: function () { 12665 this.isLoaded(); 12666 return this.getData().forAll; 12667 }, 12668 12669 /** 12670 * @private 12671 * Getter for the Uri value. 12672 * @returns {String} The Uri. 12673 */ 12674 getUri: function () { 12675 this.isLoaded(); 12676 return this.getData().uri; 12677 } 12678 }); 12679 12680 window.finesse = window.finesse || {}; 12681 window.finesse.restservices = window.finesse.restservices || {}; 12682 window.finesse.restservices.TeamWrapUpReason = TeamWrapUpReason; 12683 12684 return TeamWrapUpReason; 12685 })); 12686 12687 /** 12688 * JavaScript representation of the Finesse Team Wrap-Up Reasons collection 12689 * object which contains a list of Wrap-Up Reasons objects. 12690 * 12691 * @requires finesse.clientservices.ClientServices 12692 * @requires Class 12693 * @requires finesse.FinesseBase 12694 * @requires finesse.restservices.RestBase 12695 * @requires finesse.restservices.Dialog 12696 * @requires finesse.restservices.RestCollectionBase 12697 */ 12698 /** @private */ 12699 (function (factory) { 12700 12701 12702 // Define as an AMD module if possible 12703 if ( typeof define === 'function' && define.amd ) 12704 { 12705 define('restservices/TeamWrapUpReasons', ['restservices/RestCollectionBase', 12706 'restservices/RestBase', 12707 'restservices/TeamWrapUpReason'], factory ); 12708 } 12709 /* Define using browser globals otherwise 12710 * Prevent multiple instantiations if the script is loaded twice 12711 */ 12712 else 12713 { 12714 factory(finesse.restservices.RestCollectionBase, finesse.restservices.RestBase, finesse.restservices.TeamWrapUpReason); 12715 } 12716 }(function (RestCollectionBase, RestBase, TeamWrapUpReason) { 12717 12718 var TeamWrapUpReasons = RestCollectionBase.extend({ 12719 12720 /** 12721 * @class 12722 * JavaScript representation of a TeamWrapUpReasons collection object. Also exposes 12723 * methods to operate on the object against the server. 12724 * 12725 * @param {Object} options 12726 * An object with the following properties:<ul> 12727 * <li><b>id:</b> The id of the object being constructed</li> 12728 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 12729 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 12730 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 12731 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 12732 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 12733 * <li><b>status:</b> {Number} The HTTP status code returned</li> 12734 * <li><b>content:</b> {String} Raw string of response</li> 12735 * <li><b>object:</b> {Object} Parsed object of response</li> 12736 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 12737 * <li><b>errorType:</b> {String} Type of error that was caught</li> 12738 * <li><b>errorMessage:</b> {String} Message associated with error</li> 12739 * </ul></li> 12740 * </ul></li> 12741 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 12742 * @constructs 12743 **/ 12744 init: function (options) { 12745 this._super(options); 12746 }, 12747 12748 /** 12749 * @private 12750 * Gets the REST class for the current object - this is the TeamWrapUpReasons class. 12751 */ 12752 getRestClass: function () { 12753 return TeamWrapUpReasons; 12754 }, 12755 12756 /** 12757 * @private 12758 * Gets the REST class for the objects that make up the collection. - this 12759 * is the TeamWrapUpReason class. 12760 */ 12761 getRestItemClass: function () { 12762 return TeamWrapUpReason; 12763 }, 12764 12765 /** 12766 * @private 12767 * Gets the REST type for the current object - this is a "WrapUpReasons". 12768 */ 12769 getRestType: function () { 12770 return "WrapUpReasons"; 12771 }, 12772 12773 /** 12774 * @private 12775 * Gets the REST type for the objects that make up the collection - this is "WrapUpReason". 12776 */ 12777 getRestItemType: function () { 12778 return "WrapUpReason"; 12779 }, 12780 12781 /** 12782 * @private 12783 * Override default to indicates that the collection supports making 12784 * requests. 12785 */ 12786 supportsRequests: true, 12787 12788 /** 12789 * @private 12790 * Override default to indicate that this object doesn't support subscriptions. 12791 */ 12792 supportsRestItemSubscriptions: false, 12793 12794 /** 12795 * Retrieve the Team Wrap Up Reasons. 12796 * 12797 * @returns {finesse.restservices.TeamWrapUpReasons} 12798 * This TeamWrapUpReasons object to allow cascading. 12799 */ 12800 get: function () { 12801 // set loaded to false so it will rebuild the collection after the get 12802 this._loaded = false; 12803 // reset collection 12804 this._collection = {}; 12805 // perform get 12806 this._synchronize(); 12807 return this; 12808 }, 12809 12810 /** 12811 * Set up the PutSuccessHandler for TeamWrapUpReasons 12812 * @param {Object} wrapUpReasons 12813 * @param {Object} contentBody 12814 * @param successHandler 12815 * @returns response 12816 */ 12817 createPutSuccessHandler: function (wrapUpReasons, contentBody, successHandler) { 12818 return function (rsp) { 12819 // Update internal structure based on response. Here we 12820 // inject the contentBody from the PUT request into the 12821 // rsp.object element to mimic a GET as a way to take 12822 // advantage of the existing _processResponse method. 12823 rsp.object = contentBody; 12824 12825 wrapUpReasons._processResponse(rsp); 12826 12827 //Remove the injected contentBody object before cascading response 12828 rsp.object = {}; 12829 12830 //cascade response back to consumer's response handler 12831 successHandler(rsp); 12832 }; 12833 }, 12834 12835 /** 12836 * Perform the REST API PUT call to update the reason code assignments for the team 12837 * @param {String Array} newValues 12838 * @param handlers 12839 * @returns {Object} this 12840 */ 12841 update: function (newValues, handlers) { 12842 this.isLoaded(); 12843 var contentBody = {}, contentBodyInner = [], i, innerObject = {}; 12844 12845 contentBody[this.getRestType()] = { 12846 }; 12847 12848 for (i in newValues) { 12849 if (newValues.hasOwnProperty(i)) { 12850 innerObject = { 12851 "uri": newValues[i] 12852 }; 12853 contentBodyInner.push(innerObject); 12854 } 12855 } 12856 12857 contentBody[this.getRestType()] = { 12858 "WrapUpReason" : contentBodyInner 12859 }; 12860 12861 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 12862 handlers = handlers || {}; 12863 12864 this.restRequest(this.getRestUrl(), { 12865 method: 'PUT', 12866 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 12867 error: handlers.error, 12868 content: contentBody 12869 }); 12870 12871 return this; // Allow cascading 12872 } 12873 }); 12874 12875 window.finesse = window.finesse || {}; 12876 window.finesse.restservices = window.finesse.restservices || {}; 12877 window.finesse.restservices.TeamWrapUpReasons = TeamWrapUpReasons; 12878 12879 return TeamWrapUpReasons; 12880 })); 12881 12882 /** 12883 * JavaScript representation of a TeamSignOutReasonCode. 12884 * 12885 * @requires finesse.clientservices.ClientServices 12886 * @requires Class 12887 * @requires finesse.FinesseBase 12888 * @requires finesse.restservices.RestBase 12889 */ 12890 12891 /** @private */ 12892 (function (factory) { 12893 12894 12895 // Define as an AMD module if possible 12896 if ( typeof define === 'function' && define.amd ) 12897 { 12898 define('restservices/TeamSignOutReasonCode', ['restservices/RestBase'], factory ); 12899 } 12900 /* Define using browser globals otherwise 12901 * Prevent multiple instantiations if the script is loaded twice 12902 */ 12903 else 12904 { 12905 factory(finesse.restservices.RestBase); 12906 } 12907 }(function (RestBase) { 12908 var TeamSignOutReasonCode = RestBase.extend({ 12909 12910 /** 12911 * @class 12912 * JavaScript representation of a TeamSignOutReasonCode object. Also exposes 12913 * methods to operate on the object against the server. 12914 * 12915 * @param {Object} options 12916 * An object with the following properties:<ul> 12917 * <li><b>id:</b> The id of the object being constructed</li> 12918 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 12919 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 12920 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 12921 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 12922 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 12923 * <li><b>status:</b> {Number} The HTTP status code returned</li> 12924 * <li><b>content:</b> {String} Raw string of response</li> 12925 * <li><b>object:</b> {Object} Parsed object of response</li> 12926 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 12927 * <li><b>errorType:</b> {String} Type of error that was caught</li> 12928 * <li><b>errorMessage:</b> {String} Message associated with error</li> 12929 * </ul></li> 12930 * </ul></li> 12931 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 12932 * @constructs 12933 * @ignore 12934 **/ 12935 init: function (options) { 12936 this._super(options); 12937 }, 12938 12939 /** 12940 * @private 12941 * Gets the REST class for the current object - this is the TeamSignOutReasonCode class. 12942 * @returns {Object} The TeamSignOutReasonCode class. 12943 */ 12944 getRestClass: function () { 12945 return TeamSignOutReasonCode; 12946 }, 12947 12948 /** 12949 * @private 12950 * Gets the REST type for the current object - this is a "ReasonCode". 12951 * @returns {String} The ReasonCode string. 12952 */ 12953 getRestType: function () { 12954 return "ReasonCode"; 12955 }, 12956 12957 /** 12958 * @private 12959 * Override default to indicate that this object doesn't support making 12960 * requests. 12961 */ 12962 supportsRequests: false, 12963 12964 /** 12965 * @private 12966 * Override default to indicate that this object doesn't support subscriptions. 12967 */ 12968 supportsSubscriptions: false, 12969 12970 /** 12971 * Getter for the category. 12972 * @returns {String} The category. 12973 */ 12974 getCategory: function () { 12975 this.isLoaded(); 12976 return this.getData().category; 12977 }, 12978 12979 /** 12980 * Getter for the code. 12981 * @returns {String} The code. 12982 */ 12983 getCode: function () { 12984 this.isLoaded(); 12985 return this.getData().code; 12986 }, 12987 12988 /** 12989 * Getter for the label. 12990 * @returns {String} The label. 12991 */ 12992 getLabel: function () { 12993 this.isLoaded(); 12994 return this.getData().label; 12995 }, 12996 12997 /** 12998 * Getter for the forAll value. 12999 * @returns {String} The forAll. 13000 */ 13001 getForAll: function () { 13002 this.isLoaded(); 13003 return this.getData().forAll; 13004 }, 13005 13006 /** 13007 * Getter for the Uri value. 13008 * @returns {String} The Uri. 13009 */ 13010 getUri: function () { 13011 this.isLoaded(); 13012 return this.getData().uri; 13013 } 13014 13015 }); 13016 13017 window.finesse = window.finesse || {}; 13018 window.finesse.restservices = window.finesse.restservices || {}; 13019 window.finesse.restservices.TeamSignOutReasonCode = TeamSignOutReasonCode; 13020 13021 return TeamSignOutReasonCode; 13022 })); 13023 13024 /** 13025 * JavaScript representation of the TeamSignOutReasonCodes collection 13026 * object which contains a list of TeamSignOutReasonCode objects. 13027 * 13028 * @requires finesse.clientservices.ClientServices 13029 * @requires Class 13030 * @requires finesse.FinesseBase 13031 * @requires finesse.restservices.RestBase 13032 * @requires finesse.restservices.Dialog 13033 * @requires finesse.restservices.RestCollectionBase 13034 */ 13035 13036 /** @private */ 13037 (function (factory) { 13038 13039 13040 // Define as an AMD module if possible 13041 if ( typeof define === 'function' && define.amd ) 13042 { 13043 define('restservices/TeamSignOutReasonCodes', ['restservices/RestCollectionBase', 13044 'restservices/RestBase', 13045 'restservices/TeamSignOutReasonCode'], factory ); 13046 } 13047 /* Define using browser globals otherwise 13048 * Prevent multiple instantiations if the script is loaded twice 13049 */ 13050 else 13051 { 13052 factory(finesse.restservices.RestCollectionBase, finesse.restservices.RestBase, finesse.restservices.TeamSignOutReasonCode); 13053 } 13054 }(function (RestCollectionBase, RestBase, TeamSignOutReasonCode) { 13055 13056 var TeamSignOutReasonCodes = RestCollectionBase.extend({ 13057 /** 13058 * @class 13059 * JavaScript representation of a TeamSignOutReasonCodes collection object. Also exposes 13060 * methods to operate on the object against the server. 13061 * 13062 * @param {Object} options 13063 * An object with the following properties:<ul> 13064 * <li><b>id:</b> The id of the object being constructed</li> 13065 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 13066 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 13067 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 13068 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 13069 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 13070 * <li><b>status:</b> {Number} The HTTP status code returned</li> 13071 * <li><b>content:</b> {String} Raw string of response</li> 13072 * <li><b>object:</b> {Object} Parsed object of response</li> 13073 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 13074 * <li><b>errorType:</b> {String} Type of error that was caught</li> 13075 * <li><b>errorMessage:</b> {String} Message associated with error</li> 13076 * </ul></li> 13077 * </ul></li> 13078 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 13079 * @constructs 13080 **/ 13081 init: function (options) { 13082 this._super(options); 13083 }, 13084 13085 /** 13086 * @private 13087 * Gets the REST class for the current object - this is the TeamSignOutReasonCodes class. 13088 */ 13089 getRestClass: function () { 13090 return TeamSignOutReasonCodes; 13091 }, 13092 13093 /** 13094 * @private 13095 * Gets the REST class for the objects that make up the collection. - this 13096 * is the TeamSignOutReasonCode class. 13097 */ 13098 getRestItemClass: function () { 13099 return TeamSignOutReasonCode; 13100 }, 13101 13102 /** 13103 * @private 13104 * Gets the REST type for the current object - this is a "ReasonCodes". 13105 */ 13106 getRestType: function () { 13107 return "ReasonCodes"; 13108 }, 13109 13110 /** 13111 * Overrides the parent class. Returns the url for the SignOutReasonCodes resource 13112 */ 13113 getRestUrl: function () { 13114 var restObj = this._restObj, restUrl = ""; 13115 13116 //Prepend the base REST object if one was provided. 13117 //Otherwise prepend with the default webapp name. 13118 if (restObj instanceof RestBase) { 13119 restUrl += restObj.getRestUrl(); 13120 } else { 13121 restUrl += "/finesse/api"; 13122 } 13123 //Append the REST type. 13124 restUrl += "/ReasonCodes?category=LOGOUT"; 13125 //Append ID if it is not undefined, null, or empty. 13126 if (this._id) { 13127 restUrl += "/" + this._id; 13128 } 13129 return restUrl; 13130 }, 13131 13132 /** 13133 * @private 13134 * Gets the REST type for the objects that make up the collection - this is "ReasonCode". 13135 */ 13136 getRestItemType: function () { 13137 return "ReasonCode"; 13138 }, 13139 13140 /** 13141 * @private 13142 * Override default to indicates that the collection supports making requests. 13143 */ 13144 supportsRequests: true, 13145 13146 /** 13147 * @private 13148 * Override default to indicates that the collection does not subscribe to its objects. 13149 */ 13150 supportsRestItemSubscriptions: false, 13151 13152 /** 13153 * Retrieve the Sign Out Reason Codes. 13154 * 13155 * @returns {finesse.restservices.TeamSignOutReasonCodes} 13156 * This TeamSignOutReasonCodes object to allow cascading. 13157 */ 13158 get: function () { 13159 // set loaded to false so it will rebuild the collection after the get 13160 this._loaded = false; 13161 // reset collection 13162 this._collection = {}; 13163 // perform get 13164 this._synchronize(); 13165 return this; 13166 }, 13167 13168 /* We only use PUT and GET on Reason Code team assignments 13169 * @param {Object} contact 13170 * @param {Object} contentBody 13171 * @param {Function} successHandler 13172 */ 13173 createPutSuccessHandler: function (contact, contentBody, successHandler) { 13174 return function (rsp) { 13175 // Update internal structure based on response. Here we 13176 // inject the contentBody from the PUT request into the 13177 // rsp.object element to mimic a GET as a way to take 13178 // advantage of the existing _processResponse method. 13179 rsp.object = contentBody; 13180 contact._processResponse(rsp); 13181 13182 //Remove the injected contentBody object before cascading response 13183 rsp.object = {}; 13184 13185 //cascade response back to consumer's response handler 13186 successHandler(rsp); 13187 }; 13188 }, 13189 13190 /** 13191 * Update - This should be all that is needed. 13192 * @param {Object} newValues 13193 * @param {Object} handlers 13194 * @returns {finesse.restservices.TeamSignOutReasonCodes} 13195 * This TeamSignOutReasonCodes object to allow cascading. 13196 */ 13197 update: function (newValues, handlers) { 13198 this.isLoaded(); 13199 var contentBody = {}, contentBodyInner = [], i, innerObject = {}; 13200 13201 contentBody[this.getRestType()] = { 13202 }; 13203 13204 for (i in newValues) { 13205 if (newValues.hasOwnProperty(i)) { 13206 innerObject = { 13207 "uri": newValues[i] 13208 }; 13209 contentBodyInner.push(innerObject); 13210 } 13211 } 13212 13213 contentBody[this.getRestType()] = { 13214 "ReasonCode" : contentBodyInner 13215 }; 13216 13217 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 13218 handlers = handlers || {}; 13219 13220 this.restRequest(this.getRestUrl(), { 13221 method: 'PUT', 13222 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 13223 error: handlers.error, 13224 content: contentBody 13225 }); 13226 13227 return this; // Allow cascading 13228 } 13229 13230 }); 13231 13232 window.finesse = window.finesse || {}; 13233 window.finesse.restservices = window.finesse.restservices || {}; 13234 window.finesse.restservices.TeamSignOutReasonCodes = TeamSignOutReasonCodes; 13235 13236 return TeamSignOutReasonCodes; 13237 })); 13238 13239 /** 13240 * JavaScript representation of the Finesse PhoneBook Assignment object. 13241 * 13242 * @requires finesse.clientservices.ClientServices 13243 * @requires Class 13244 * @requires finesse.FinesseBase 13245 * @requires finesse.restservices.RestBase 13246 */ 13247 13248 /** 13249 * The following comment prevents JSLint errors concerning undefined global variables. 13250 * It tells JSLint that these identifiers are defined elsewhere. 13251 */ 13252 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */ 13253 13254 /** The following comment is to prevent jslint errors about 13255 * using variables before they are defined. 13256 */ 13257 /*global $, jQuery, Handlebars, dojox, dojo, finesse */ 13258 13259 /** @private */ 13260 (function (factory) { 13261 13262 13263 // Define as an AMD module if possible 13264 if ( typeof define === 'function' && define.amd ) 13265 { 13266 define('restservices/TeamPhoneBook', ['restservices/RestBase'], factory ); 13267 } 13268 /* Define using browser globals otherwise 13269 * Prevent multiple instantiations if the script is loaded twice 13270 */ 13271 else 13272 { 13273 factory(finesse.restservices.RestBase); 13274 } 13275 }(function (RestBase) { 13276 var TeamPhoneBook = RestBase.extend({ 13277 13278 /** 13279 * @class 13280 * JavaScript representation of a PhoneBook object. Also exposes 13281 * methods to operate on the object against the server. 13282 * 13283 * @param {Object} options 13284 * An object with the following properties:<ul> 13285 * <li><b>id:</b> The id of the object being constructed</li> 13286 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 13287 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 13288 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 13289 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 13290 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 13291 * <li><b>status:</b> {Number} The HTTP status code returned</li> 13292 * <li><b>content:</b> {String} Raw string of response</li> 13293 * <li><b>object:</b> {Object} Parsed object of response</li> 13294 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 13295 * <li><b>errorType:</b> {String} Type of error that was caught</li> 13296 * <li><b>errorMessage:</b> {String} Message associated with error</li> 13297 * </ul></li> 13298 * </ul></li> 13299 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 13300 * @constructs 13301 **/ 13302 init: function (options) { 13303 this._super(options); 13304 }, 13305 13306 /** 13307 * @private 13308 * Gets the REST class for the current object - this is the PhoneBooks class. 13309 * @returns {Object} The PhoneBooks class. 13310 */ 13311 getRestClass: function () { 13312 return TeamPhoneBook; 13313 }, 13314 13315 /** 13316 * @private 13317 * Gets the REST type for the current object - this is a "PhoneBook". 13318 * @returns {String} The PhoneBook string. 13319 */ 13320 getRestType: function () { 13321 return "PhoneBook"; 13322 }, 13323 13324 /** 13325 * @private 13326 * Override default to indicate that this object doesn't support making 13327 * requests. 13328 */ 13329 supportsRequests: false, 13330 13331 /** 13332 * @private 13333 * Override default to indicate that this object doesn't support subscriptions. 13334 */ 13335 supportsSubscriptions: false, 13336 13337 /** 13338 * Getter for the name. 13339 * @returns {String} The name. 13340 */ 13341 getName: function () { 13342 this.isLoaded(); 13343 return this.getData().name; 13344 }, 13345 13346 /** 13347 * Getter for the Uri value. 13348 * @returns {String} The Uri. 13349 */ 13350 getUri: function () { 13351 this.isLoaded(); 13352 return this.getData().uri; 13353 } 13354 13355 }); 13356 13357 window.finesse = window.finesse || {}; 13358 window.finesse.restservices = window.finesse.restservices || {}; 13359 window.finesse.restservices.TeamPhoneBook = TeamPhoneBook; 13360 13361 return TeamPhoneBook; 13362 })); 13363 13364 /** 13365 * JavaScript representation of the Finesse PhoneBook Assignments collection 13366 * object which contains a list of Not Ready Reason Codes objects. 13367 * 13368 * @requires finesse.clientservices.ClientServices 13369 * @requires Class 13370 * @requires finesse.FinesseBase 13371 * @requires finesse.restservices.RestBase 13372 * @requires finesse.restservices.Dialog 13373 * @requires finesse.restservices.RestCollectionBase 13374 */ 13375 13376 /** 13377 * The following comment prevents JSLint errors concerning undefined global variables. 13378 * It tells JSLint that these identifiers are defined elsewhere. 13379 */ 13380 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */ 13381 13382 /** The following comment is to prevent jslint errors about 13383 * using variables before they are defined. 13384 */ 13385 /*global $, jQuery, Handlebars, dojox, dojo, finesse */ 13386 13387 /** @private */ 13388 (function (factory) { 13389 13390 13391 // Define as an AMD module if possible 13392 if ( typeof define === 'function' && define.amd ) 13393 { 13394 define('restservices/TeamPhoneBooks', ['restservices/RestCollectionBase', 13395 'restservices/RestBase', 13396 'restservices/TeamPhoneBook'], factory ); 13397 } 13398 /* Define using browser globals otherwise 13399 * Prevent multiple instantiations if the script is loaded twice 13400 */ 13401 else 13402 { 13403 factory(finesse.restservices.RestCollectionBase, finesse.restservices.RestBase, finesse.restservices.TeamPhoneBook); 13404 } 13405 }(function (RestCollectionBase, RestBase, TeamPhoneBook) { 13406 var TeamPhoneBooks = RestCollectionBase.extend({ 13407 13408 /** 13409 * @class 13410 * JavaScript representation of a TeamPhoneBooks collection object. Also exposes 13411 * methods to operate on the object against the server. 13412 * 13413 * @param {Object} options 13414 * An object with the following properties:<ul> 13415 * <li><b>id:</b> The id of the object being constructed</li> 13416 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 13417 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 13418 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 13419 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 13420 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 13421 * <li><b>status:</b> {Number} The HTTP status code returned</li> 13422 * <li><b>content:</b> {String} Raw string of response</li> 13423 * <li><b>object:</b> {Object} Parsed object of response</li> 13424 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 13425 * <li><b>errorType:</b> {String} Type of error that was caught</li> 13426 * <li><b>errorMessage:</b> {String} Message associated with error</li> 13427 * </ul></li> 13428 * </ul></li> 13429 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 13430 * @constructs 13431 **/ 13432 init: function (options) { 13433 this._super(options); 13434 }, 13435 13436 /** 13437 * @private 13438 * Gets the REST class for the current object - this is the TeamPhoneBooks class. 13439 */ 13440 getRestClass: function () { 13441 return TeamPhoneBooks; 13442 }, 13443 13444 /** 13445 * @private 13446 * Gets the REST class for the objects that make up the collection. - this 13447 * is the TeamPhoneBooks class. 13448 */ 13449 getRestItemClass: function () { 13450 return TeamPhoneBook; 13451 }, 13452 13453 /** 13454 * @private 13455 * Gets the REST type for the current object - this is a "ReasonCodes". 13456 */ 13457 getRestType: function () { 13458 return "PhoneBooks"; 13459 }, 13460 13461 /** 13462 * Overrides the parent class. Returns the url for the PhoneBooks resource 13463 */ 13464 getRestUrl: function () { 13465 // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY"); 13466 var restObj = this._restObj, 13467 restUrl = ""; 13468 //Prepend the base REST object if one was provided. 13469 if (restObj instanceof RestBase) { 13470 restUrl += restObj.getRestUrl(); 13471 } 13472 //Otherwise prepend with the default webapp name. 13473 else { 13474 restUrl += "/finesse/api"; 13475 } 13476 //Append the REST type. 13477 restUrl += "/PhoneBooks"; 13478 //Append ID if it is not undefined, null, or empty. 13479 if (this._id) { 13480 restUrl += "/" + this._id; 13481 } 13482 return restUrl; 13483 }, 13484 13485 /** 13486 * @private 13487 * Gets the REST type for the objects that make up the collection - this is "ReasonCode". 13488 */ 13489 getRestItemType: function () { 13490 return "PhoneBook"; 13491 }, 13492 13493 /** 13494 * @private 13495 * Override default to indicates that the collection supports making 13496 * requests. 13497 */ 13498 supportsRequests: true, 13499 13500 /** 13501 * @private 13502 * Override default to indicates that the collection subscribes to its objects. 13503 */ 13504 supportsRestItemSubscriptions: false, 13505 13506 /** 13507 * Retrieve the Not Ready Reason Codes. 13508 * 13509 * @returns {finesse.restservices.TeamPhoneBooks} 13510 * This TeamPhoneBooks object to allow cascading. 13511 */ 13512 get: function () { 13513 // set loaded to false so it will rebuild the collection after the get 13514 /** @private */ 13515 this._loaded = false; 13516 // reset collection 13517 /** @private */ 13518 this._collection = {}; 13519 // perform get 13520 this._synchronize(); 13521 return this; 13522 }, 13523 13524 /* We only use PUT and GET on Reason Code team assignments 13525 */ 13526 createPutSuccessHandler: function(contact, contentBody, successHandler){ 13527 return function (rsp) { 13528 // Update internal structure based on response. Here we 13529 // inject the contentBody from the PUT request into the 13530 // rsp.object element to mimic a GET as a way to take 13531 // advantage of the existing _processResponse method. 13532 rsp.object = contentBody; 13533 contact._processResponse(rsp); 13534 13535 //Remove the injected Contact object before cascading response 13536 rsp.object = {}; 13537 13538 //cascade response back to consumer's response handler 13539 successHandler(rsp); 13540 }; 13541 }, 13542 13543 /** 13544 * Update - This should be all that is needed. 13545 */ 13546 update: function (newValues, handlers) { 13547 this.isLoaded(); 13548 var contentBody = {}, contentBodyInner = [], i, innerObject; 13549 13550 contentBody[this.getRestType()] = { 13551 }; 13552 13553 for (i in newValues) { 13554 if (newValues.hasOwnProperty(i)) { 13555 innerObject = {}; 13556 innerObject = { 13557 "uri": newValues[i] 13558 }; 13559 contentBodyInner.push(innerObject); 13560 } 13561 } 13562 13563 contentBody[this.getRestType()] = { 13564 "PhoneBook" : contentBodyInner 13565 }; 13566 13567 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 13568 handlers = handlers || {}; 13569 13570 this.restRequest(this.getRestUrl(), { 13571 method: 'PUT', 13572 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 13573 error: handlers.error, 13574 content: contentBody 13575 }); 13576 13577 return this; // Allow cascading 13578 } 13579 13580 }); 13581 13582 window.finesse = window.finesse || {}; 13583 window.finesse.restservices = window.finesse.restservices || {}; 13584 window.finesse.restservices.TeamPhoneBooks = TeamPhoneBooks; 13585 13586 return TeamPhoneBooks; 13587 })); 13588 13589 /** 13590 * JavaScript representation of the Finesse LayoutConfig object 13591 * @requires ClientServices 13592 * @requires finesse.FinesseBase 13593 * @requires finesse.restservices.RestBase 13594 */ 13595 13596 /** @private */ 13597 (function (factory) { 13598 13599 13600 // Define as an AMD module if possible 13601 if ( typeof define === 'function' && define.amd ) 13602 { 13603 define('restservices/LayoutConfig', ['restservices/RestBase'], factory ); 13604 } 13605 /* Define using browser globals otherwise 13606 * Prevent multiple instantiations if the script is loaded twice 13607 */ 13608 else 13609 { 13610 factory(finesse.restservices.RestBase); 13611 } 13612 }(function (RestBase) { 13613 /** @private */ 13614 var LayoutConfig = RestBase.extend({ 13615 13616 /** 13617 * @class 13618 * JavaScript representation of a LayoutConfig object. Also exposes methods to operate 13619 * on the object against the server. 13620 * 13621 * @param {String} id 13622 * Not required... 13623 * @param {Object} callbacks 13624 * An object containing callbacks for instantiation and runtime 13625 * @param {Function} callbacks.onLoad(this) 13626 * Callback to invoke upon successful instantiation 13627 * @param {Function} callbacks.onLoadError(rsp) 13628 * Callback to invoke on instantiation REST request error 13629 * as passed by finesse.clientservices.ClientServices.ajax() 13630 * { 13631 * status: {Number} The HTTP status code returned 13632 * content: {String} Raw string of response 13633 * object: {Object} Parsed object of response 13634 * error: {Object} Wrapped exception that was caught 13635 * error.errorType: {String} Type of error that was caught 13636 * error.errorMessage: {String} Message associated with error 13637 * } 13638 * @param {Function} callbacks.onChange(this) 13639 * Callback to invoke upon successful update 13640 * @param {Function} callbacks.onError(rsp) 13641 * Callback to invoke on update error (refresh or event) 13642 * as passed by finesse.clientservices.ClientServices.ajax() 13643 * { 13644 * status: {Number} The HTTP status code returned 13645 * content: {String} Raw string of response 13646 * object: {Object} Parsed object of response 13647 * error: {Object} Wrapped exception that was caught 13648 * error.errorType: {String} Type of error that was caught 13649 * error.errorMessage: {String} Message associated with error 13650 * } 13651 * 13652 * @constructs 13653 */ 13654 init: function (callbacks) { 13655 this._super("", callbacks); 13656 //when post is performed and id is empty 13657 /*if (id === "") { 13658 this._loaded = true; 13659 }*/ 13660 this._layoutxml = {}; 13661 }, 13662 13663 /** 13664 * Returns REST class of LayoutConfig object 13665 */ 13666 getRestClass: function () { 13667 return LayoutConfig; 13668 }, 13669 13670 /** 13671 * The type of this REST object is LayoutConfig 13672 */ 13673 getRestType: function () { 13674 return "LayoutConfig"; 13675 }, 13676 13677 /** 13678 * Gets the REST URL of this object. 13679 * 13680 * If the parent has an id, the id is appended. 13681 * On occasions of POST, it will not have an id. 13682 */ 13683 getRestUrl: function () { 13684 var layoutUri = "/finesse/api/" + this.getRestType() + "/default"; 13685 /*if (this._id) { 13686 layoutUri = layoutUri + "/" + this._id; 13687 }*/ 13688 return layoutUri; 13689 }, 13690 13691 /** 13692 * This API does not support subscription 13693 */ 13694 supportsSubscriptions: false, 13695 13696 keepRestResponse: true, 13697 13698 13699 /** 13700 * Gets finesselayout.xml retrieved from the API call 13701 */ 13702 getLayoutxml: function () { 13703 this.isLoaded(); 13704 var layoutxml = this.getData().layoutxml; 13705 13706 // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with update()) 13707 layoutxml = layoutxml.replace(/&/g,"&"); 13708 13709 return layoutxml; 13710 }, 13711 13712 /** 13713 * Gets the type of this LayoutConfig object 13714 */ 13715 /* 13716 getType: function () { 13717 this.isLoaded(); 13718 return this.getData().type; 13719 },*/ 13720 13721 /** 13722 * Retrieve the LayoutConfig settings. 13723 * If the id is not provided the API call will fail. 13724 * @returns {LayoutConfig} 13725 * This LayoutConfig object to allow cascading. 13726 */ 13727 get: function () { 13728 this._synchronize(); 13729 return this; 13730 }, 13731 13732 /** 13733 * Closure handle updating of the internal data for the LayoutConfig object 13734 * upon a successful update (PUT) request before calling the intended 13735 * success handler provided by the consumer 13736 * 13737 * @param {Object} 13738 * layoutconfig Reference to this LayoutConfig object 13739 * @param {Object} 13740 * LayoutConfig Object that contains the settings to be 13741 * submitted in the api request 13742 * @param {Function} 13743 * successHandler The success handler specified by the consumer 13744 * of this object 13745 * @returns {LayoutConfig} This LayoutConfig object to allow cascading 13746 */ 13747 13748 createPutSuccessHandler: function (layoutconfig, contentBody, successHandler) { 13749 return function (rsp) { 13750 // Update internal structure based on response. Here we 13751 // inject the contentBody from the PUT request into the 13752 // rsp.object element to mimic a GET as a way to take 13753 // advantage of the existing _processResponse method. 13754 rsp.content = contentBody; 13755 rsp.object.LayoutConfig = {}; 13756 rsp.object.LayoutConfig.finesseLayout = contentBody; 13757 layoutconfig._processResponse(rsp); 13758 13759 //Remove the injected layoutConfig object before cascading response 13760 rsp.object.LayoutConfig = {}; 13761 13762 //cascade response back to consumer's response handler 13763 successHandler(rsp); 13764 }; 13765 }, 13766 13767 /** 13768 * Update LayoutConfig 13769 * @param {Object} finesselayout 13770 * The XML for FinesseLayout being stored 13771 * 13772 * @param {Object} handlers 13773 * An object containing callback handlers for the request. Optional. 13774 * @param {Function} options.success(rsp) 13775 * A callback function to be invoked for a successful request. 13776 * { 13777 * status: {Number} The HTTP status code returned 13778 * content: {String} Raw string of response 13779 * object: {Object} Parsed object of response 13780 * } 13781 * @param {Function} options.error(rsp) 13782 * A callback function to be invoked for an unsuccessful request. 13783 * { 13784 * status: {Number} The HTTP status code returned 13785 * content: {String} Raw string of response 13786 * object: {Object} Parsed object of response (HTTP errors) 13787 * error: {Object} Wrapped exception that was caught 13788 * error.errorType: {String} Type of error that was caught 13789 * error.errorMessage: {String} Message associated with error 13790 * } 13791 * @returns {finesse.restservices.LayoutConfig} 13792 * This LayoutConfig object to allow cascading 13793 */ 13794 13795 update: function (layoutxml, handlers) { 13796 this.isLoaded(); 13797 var contentBody = {}; 13798 13799 // We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml()) 13800 layoutxml = layoutxml.replace(/&(?!amp;)/g, "&"); 13801 13802 contentBody[this.getRestType()] = { 13803 "layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true) 13804 }; 13805 13806 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 13807 handlers = handlers || {}; 13808 13809 this.restRequest(this.getRestUrl(), { 13810 method: 'PUT', 13811 success: this.createPutSuccessHandler(this, layoutxml, handlers.success), 13812 error: handlers.error, 13813 content: contentBody 13814 }); 13815 13816 return this; // Allow cascading 13817 } 13818 13819 /** 13820 *TODO createPostSuccessHandler needs to be debugged to make it working 13821 * Closure handle creating new LayoutConfig object 13822 * upon a successful create (POST) request before calling the intended 13823 * success handler provided by the consumer 13824 * 13825 * @param {Object} 13826 * layoutconfig Reference to this LayoutConfig object 13827 * @param {Object} 13828 * LayoutConfig Object that contains the settings to be 13829 * submitted in the api request 13830 * @param {Function} 13831 * successHandler The success handler specified by the consumer 13832 * of this object 13833 * @returns {finesse.restservices.LayoutConfig} This LayoutConfig object to allow cascading 13834 */ 13835 /* 13836 createPostSuccessHandler: function (layoutconfig, contentBody, successHandler) { 13837 return function (rsp) { 13838 13839 rsp.object = contentBody; 13840 layoutconfig._processResponse(rsp); 13841 13842 //Remove the injected layoutConfig object before cascading response 13843 rsp.object = {}; 13844 13845 //cascade response back to consumer's response handler 13846 successHandler(rsp); 13847 }; 13848 }, */ 13849 13850 /** 13851 * TODO Method needs to be debugged to make POST working 13852 * Add LayoutConfig 13853 * @param {Object} finesselayout 13854 * The XML for FinesseLayout being stored 13855 * 13856 * @param {Object} handlers 13857 * An object containing callback handlers for the request. Optional. 13858 * @param {Function} options.success(rsp) 13859 * A callback function to be invoked for a successful request. 13860 * { 13861 * status: {Number} The HTTP status code returned 13862 * content: {String} Raw string of response 13863 * object: {Object} Parsed object of response 13864 * } 13865 * @param {Function} options.error(rsp) 13866 * A callback function to be invoked for an unsuccessful request. 13867 * { 13868 * status: {Number} The HTTP status code returned 13869 * content: {String} Raw string of response 13870 * object: {Object} Parsed object of response (HTTP errors) 13871 * error: {Object} Wrapped exception that was caught 13872 * error.errorType: {String} Type of error that was caught 13873 * error.errorMessage: {String} Message associated with error 13874 * } 13875 * @returns {finesse.restservices.LayoutConfig} 13876 * This LayoutConfig object to allow cascading 13877 */ 13878 /* 13879 add: function (layoutxml, handlers) { 13880 this.isLoaded(); 13881 var contentBody = {}; 13882 13883 13884 contentBody[this.getRestType()] = { 13885 "layoutxml": layoutxml, 13886 "type": "current" 13887 }; 13888 13889 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 13890 handlers = handlers || {}; 13891 13892 this.restRequest(this.getRestUrl(), { 13893 method: 'POST', 13894 success: this.createPostSuccessHandler(this, contentBody, handlers.success), 13895 error: handlers.error, 13896 content: contentBody 13897 }); 13898 13899 return this; // Allow cascading 13900 } */ 13901 }); 13902 13903 window.finesse = window.finesse || {}; 13904 window.finesse.restservices = window.finesse.restservices || {}; 13905 window.finesse.restservices.LayoutConfig = LayoutConfig; 13906 13907 return LayoutConfig; 13908 13909 })); 13910 13911 /** 13912 * JavaScript representation of the Finesse LayoutConfig object for a Team. 13913 * 13914 * @requires finesse.clientservices.ClientServices 13915 * @requires Class 13916 * @requires finesse.FinesseBase 13917 * @requires finesse.restservices.RestBase 13918 * @requires finesse.utilities.Utilities 13919 * @requires finesse.restservices.LayoutConfig 13920 */ 13921 13922 /** The following comment is to prevent jslint errors about 13923 * using variables before they are defined. 13924 */ 13925 /*global Exception */ 13926 13927 /** @private */ 13928 (function (factory) { 13929 13930 13931 // Define as an AMD module if possible 13932 if ( typeof define === 'function' && define.amd ) 13933 { 13934 define('restservices/TeamLayoutConfig', ['restservices/RestBase', 13935 'utilities/Utilities', 13936 'restservices/LayoutConfig'], factory ); 13937 } 13938 /* Define using browser globals otherwise 13939 * Prevent multiple instantiations if the script is loaded twice 13940 */ 13941 else 13942 { 13943 factory(finesse.restservices.RestBase, finesse.utilities.Utilities, finesse.restservices.LayoutConfig); 13944 } 13945 }(function (RestBase, Utilities, LayoutConfig) { 13946 13947 var TeamLayoutConfig = RestBase.extend({ 13948 // Keep the restresponse so we can parse the layoutxml out of it in getLayoutXML() 13949 keepRestResponse: true, 13950 13951 /** 13952 * @class 13953 * JavaScript representation of a LayoutConfig object for a Team. Also exposes 13954 * methods to operate on the object against the server. 13955 * 13956 * @param {Object} options 13957 * An object with the following properties:<ul> 13958 * <li><b>id:</b> The id of the object being constructed</li> 13959 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 13960 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 13961 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 13962 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 13963 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 13964 * <li><b>status:</b> {Number} The HTTP status code returned</li> 13965 * <li><b>content:</b> {String} Raw string of response</li> 13966 * <li><b>object:</b> {Object} Parsed object of response</li> 13967 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 13968 * <li><b>errorType:</b> {String} Type of error that was caught</li> 13969 * <li><b>errorMessage:</b> {String} Message associated with error</li> 13970 * </ul></li> 13971 * </ul></li> 13972 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 13973 * @constructs 13974 **/ 13975 init: function (options) { 13976 this._super(options); 13977 }, 13978 13979 /** 13980 * @private 13981 * Gets the REST class for the current object - this is the LayoutConfigs class. 13982 * @returns {Object} The LayoutConfigs class. 13983 */ 13984 getRestClass: function () { 13985 return TeamLayoutConfig; 13986 }, 13987 13988 /** 13989 * @private 13990 * Gets the REST type for the current object - this is a "LayoutConfig". 13991 * @returns {String} The LayoutConfig string. 13992 */ 13993 getRestType: function () { 13994 return "TeamLayoutConfig"; 13995 }, 13996 13997 /** 13998 * @private 13999 * Override default to indicate that this object doesn't support making 14000 * requests. 14001 */ 14002 supportsRequests: false, 14003 14004 /** 14005 * @private 14006 * Override default to indicate that this object doesn't support subscriptions. 14007 */ 14008 supportsSubscriptions: false, 14009 14010 /** 14011 * Getter for the category. 14012 * @returns {String} The category. 14013 */ 14014 getLayoutXML: function () { 14015 this.isLoaded(); 14016 var layoutxml = this.getData().layoutxml; 14017 14018 // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with put()) 14019 layoutxml = layoutxml.replace(/&/g,"&"); 14020 14021 return layoutxml; 14022 }, 14023 14024 /** 14025 * Getter for the code. 14026 * @returns {String} The code. 14027 */ 14028 getUseDefault: function () { 14029 this.isLoaded(); 14030 return this.getData().useDefault; 14031 }, 14032 14033 /** 14034 * Retrieve the TeamLayoutConfig. 14035 * 14036 * @returns {finesse.restservices.TeamLayoutConfig} 14037 */ 14038 get: function () { 14039 // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway 14040 this._id = "0"; 14041 // set loaded to false so it will rebuild the collection after the get 14042 this._loaded = false; 14043 // reset collection 14044 this._collection = {}; 14045 // perform get 14046 this._synchronize(); 14047 return this; 14048 }, 14049 14050 createPutSuccessHandler: function(contact, contentBody, successHandler){ 14051 return function (rsp) { 14052 // Update internal structure based on response. Here we 14053 // inject the contentBody from the PUT request into the 14054 // rsp.object element to mimic a GET as a way to take 14055 // advantage of the existing _processResponse method. 14056 rsp.object = contentBody; 14057 contact._processResponse(rsp); 14058 14059 //Remove the injected Contact object before cascading response 14060 rsp.object = {}; 14061 14062 //cascade response back to consumer's response handler 14063 successHandler(rsp); 14064 }; 14065 }, 14066 14067 put: function (newValues, handlers) { 14068 // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway 14069 this._id = "0"; 14070 this.isLoaded(); 14071 14072 // We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml()) 14073 var layoutxml = newValues.layoutXML.replace(/&(?!amp;)/g, "&"), 14074 contentBody = {}; 14075 14076 contentBody[this.getRestType()] = { 14077 "useDefault": newValues.useDefault, 14078 // The LayoutConfig restservice javascript class only translates ampersands, so we'll do that also 14079 "layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true) 14080 }; 14081 14082 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 14083 handlers = handlers || {}; 14084 14085 this.restRequest(this.getRestUrl(), { 14086 method: 'PUT', 14087 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 14088 error: handlers.error, 14089 content: contentBody 14090 }); 14091 14092 return this; // Allow cascading 14093 }, 14094 14095 getRestUrl: function(){ 14096 // return team's url + /LayoutConfig 14097 // eg: /api/Team/1/LayoutConfig 14098 if(this._restObj === undefined){ 14099 throw new Exception("TeamLayoutConfig instances must have a parent team object."); 14100 } 14101 return this._restObj.getRestUrl() + '/LayoutConfig'; 14102 } 14103 14104 }); 14105 14106 window.finesse = window.finesse || {}; 14107 window.finesse.restservices = window.finesse.restservices || {}; 14108 window.finesse.restservices.TeamLayoutConfig = TeamLayoutConfig; 14109 14110 return TeamLayoutConfig; 14111 })); 14112 14113 /** 14114 * JavaScript representation of a TeamWorkflow. 14115 * 14116 * @requires finesse.clientservices.ClientServices 14117 * @requires Class 14118 * @requires finesse.FinesseBase 14119 * @requires finesse.restservices.RestBase 14120 */ 14121 /** @private */ 14122 (function (factory) { 14123 14124 14125 // Define as an AMD module if possible 14126 if ( typeof define === 'function' && define.amd ) 14127 { 14128 define('restservices/TeamWorkflow', ['restservices/RestBase'], factory ); 14129 } 14130 /* Define using browser globals otherwise 14131 * Prevent multiple instantiations if the script is loaded twice 14132 */ 14133 else 14134 { 14135 factory(finesse.restservices.RestBase); 14136 } 14137 }(function (RestBase) { 14138 14139 var TeamWorkflow = RestBase.extend({ 14140 14141 /** 14142 * @class 14143 * JavaScript representation of a TeamWorkflow object. Also exposes 14144 * methods to operate on the object against the server. 14145 * 14146 * @param {Object} options 14147 * An object with the following properties:<ul> 14148 * <li><b>id:</b> The id of the object being constructed</li> 14149 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 14150 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 14151 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 14152 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 14153 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 14154 * <li><b>status:</b> {Number} The HTTP status description returned</li> 14155 * <li><b>content:</b> {String} Raw string of response</li> 14156 * <li><b>object:</b> {Object} Parsed object of response</li> 14157 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 14158 * <li><b>errorType:</b> {String} Type of error that was caught</li> 14159 * <li><b>errorMessage:</b> {String} Message associated with error</li> 14160 * </ul></li> 14161 * </ul></li> 14162 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 14163 * @constructs 14164 **/ 14165 init: function (options) { 14166 this._super(options); 14167 }, 14168 14169 /** 14170 * @private 14171 * Gets the REST class for the current object - this is the TeamWorkflow class. 14172 * @returns {Object} The TeamWorkflow class. 14173 */ 14174 getRestClass: function () { 14175 return TeamWorkflow; 14176 }, 14177 14178 /** 14179 * @private 14180 * Gets the REST type for the current object - this is a "Workflow". 14181 * @returns {String} The Workflow string. 14182 */ 14183 getRestType: function () { 14184 return "Workflow"; 14185 }, 14186 14187 /** 14188 * @private 14189 * Override default to indicate that this object doesn't support making 14190 * requests. 14191 */ 14192 supportsRequests: false, 14193 14194 /** 14195 * @private 14196 * Override default to indicate that this object doesn't support subscriptions. 14197 */ 14198 supportsSubscriptions: false, 14199 14200 /** 14201 * Getter for the name. 14202 * @returns {String} The name. 14203 */ 14204 getName: function () { 14205 this.isLoaded(); 14206 return this.getData().name; 14207 }, 14208 14209 /** 14210 * Getter for the description. 14211 * @returns {String} The description. 14212 */ 14213 getDescription: function () { 14214 this.isLoaded(); 14215 return this.getData().description; 14216 }, 14217 14218 /** 14219 * Getter for the Uri value. 14220 * @returns {String} The Uri. 14221 */ 14222 getUri: function () { 14223 this.isLoaded(); 14224 return this.getData().uri; 14225 } 14226 14227 }); 14228 14229 window.finesse = window.finesse || {}; 14230 window.finesse.restservices = window.finesse.restservices || {}; 14231 window.finesse.restservices.TeamWorkflow = TeamWorkflow; 14232 14233 return TeamWorkflow; 14234 })); 14235 14236 /** 14237 * JavaScript representation of the TeamWorkflows collection 14238 * object which contains a list of TeamWorkflow objects. 14239 * 14240 * @requires finesse.clientservices.ClientServices 14241 * @requires Class 14242 * @requires finesse.FinesseBase 14243 * @requires finesse.restservices.RestBase 14244 * @requires finesse.restservices.Dialog 14245 * @requires finesse.restservices.RestCollectionBase 14246 */ 14247 /** @private */ 14248 (function (factory) { 14249 14250 14251 // Define as an AMD module if possible 14252 if ( typeof define === 'function' && define.amd ) 14253 { 14254 define('restservices/TeamWorkflows', ['restservices/RestCollectionBase', 14255 'restservices/TeamWorkflow', 14256 'restservices/RestBase'], factory ); 14257 } 14258 /* Define using browser globals otherwise 14259 * Prevent multiple instantiations if the script is loaded twice 14260 */ 14261 else 14262 { 14263 factory(finesse.restservices.RestCollectionBase, finesse.restservices.TeamWorkflow, finesse.restservices.RestBase); 14264 } 14265 }(function (RestCollectionBase, TeamWorkflow, RestBase) { 14266 14267 var TeamWorkflows = RestCollectionBase.extend({ 14268 14269 /** 14270 * @class 14271 * JavaScript representation of a TeamWorkflows collection object. Also exposes 14272 * methods to operate on the object against the server. 14273 * 14274 * @param {Object} options 14275 * An object with the following properties:<ul> 14276 * <li><b>id:</b> The id of the object being constructed</li> 14277 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 14278 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 14279 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 14280 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 14281 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 14282 * <li><b>status:</b> {Number} The HTTP status code returned</li> 14283 * <li><b>content:</b> {String} Raw string of response</li> 14284 * <li><b>object:</b> {Object} Parsed object of response</li> 14285 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 14286 * <li><b>errorType:</b> {String} Type of error that was caught</li> 14287 * <li><b>errorMessage:</b> {String} Message associated with error</li> 14288 * </ul></li> 14289 * </ul></li> 14290 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 14291 * @constructs 14292 **/ 14293 init: function (options) { 14294 this._super(options); 14295 }, 14296 14297 /** 14298 * @private 14299 * Gets the REST class for the current object - this is the TeamWorkflows class. 14300 */ 14301 getRestClass: function () { 14302 return TeamWorkflows; 14303 }, 14304 14305 /** 14306 * @private 14307 * Gets the REST class for the objects that make up the collection. - this 14308 * is the TeamWorkflow class. 14309 */ 14310 getRestItemClass: function () { 14311 return TeamWorkflow; 14312 }, 14313 14314 /** 14315 * @private 14316 * Gets the REST type for the current object - this is a "Workflows". 14317 */ 14318 getRestType: function () { 14319 return "Workflows"; 14320 }, 14321 14322 /** 14323 * Overrides the parent class. Returns the url for the Workflows resource 14324 */ 14325 getRestUrl: function () { 14326 var restObj = this._restObj, restUrl = ""; 14327 14328 //Prepend the base REST object if one was provided. 14329 //Otherwise prepend with the default webapp name. 14330 if (restObj instanceof RestBase) { 14331 restUrl += restObj.getRestUrl(); 14332 } else { 14333 restUrl += "/finesse/api/Team"; 14334 } 14335 //Append ID if it is not undefined, null, or empty. 14336 if (this._id) { 14337 restUrl += "/" + this._id; 14338 } 14339 //Append the REST type. 14340 restUrl += "/Workflows"; 14341 14342 return restUrl; 14343 }, 14344 14345 /** 14346 * @private 14347 * Gets the REST type for the objects that make up the collection - this is "Workflow". 14348 */ 14349 getRestItemType: function () { 14350 return "Workflow"; 14351 }, 14352 14353 /** 14354 * @private 14355 * Override default to indicates that the collection supports making requests. 14356 */ 14357 supportsRequests: true, 14358 14359 /** 14360 * @private 14361 * Override default to indicates that the collection does not subscribe to its objects. 14362 */ 14363 supportsRestItemSubscriptions: false, 14364 14365 /** 14366 * Retrieve the Sign Out Reason Codes. 14367 * 14368 * @returns {finesse.restservices.TeamWorkflows} 14369 * This TeamWorkflows object to allow cascading. 14370 */ 14371 get: function () { 14372 // set loaded to false so it will rebuild the collection after the get 14373 this._loaded = false; 14374 // reset collection 14375 this._collection = {}; 14376 // perform get 14377 this._synchronize(); 14378 return this; 14379 }, 14380 14381 /* We only use PUT and GET on Reason Code team assignments 14382 * @param {Object} contact 14383 * @param {Object} contentBody 14384 * @param {Function} successHandler 14385 */ 14386 createPutSuccessHandler: function (contact, contentBody, successHandler) { 14387 return function (rsp) { 14388 // Update internal structure based on response. Here we 14389 // inject the contentBody from the PUT request into the 14390 // rsp.object element to mimic a GET as a way to take 14391 // advantage of the existing _processResponse method. 14392 rsp.object = contentBody; 14393 contact._processResponse(rsp); 14394 14395 //Remove the injected contentBody object before cascading response 14396 rsp.object = {}; 14397 14398 //cascade response back to consumer's response handler 14399 successHandler(rsp); 14400 }; 14401 }, 14402 14403 /** 14404 * Update - This should be all that is needed. 14405 * @param {Object} newValues 14406 * @param {Object} handlers 14407 * @returns {finesse.restservices.TeamWorkflows} 14408 * This TeamWorkflows object to allow cascading. 14409 */ 14410 update: function (newValues, handlers) { 14411 this.isLoaded(); 14412 var contentBody = {}, contentBodyInner = [], i, innerObject = {}; 14413 14414 contentBody[this.getRestType()] = { 14415 }; 14416 14417 for (i in newValues) { 14418 if (newValues.hasOwnProperty(i)) { 14419 innerObject = { 14420 "uri": newValues[i] 14421 }; 14422 contentBodyInner.push(innerObject); 14423 } 14424 } 14425 14426 contentBody[this.getRestType()] = { 14427 "Workflow" : contentBodyInner 14428 }; 14429 14430 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 14431 handlers = handlers || {}; 14432 14433 this.restRequest(this.getRestUrl(), { 14434 method: 'PUT', 14435 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 14436 error: handlers.error, 14437 content: contentBody 14438 }); 14439 14440 return this; // Allow cascading 14441 } 14442 14443 }); 14444 14445 window.finesse = window.finesse || {}; 14446 window.finesse.restservices = window.finesse.restservices || {}; 14447 window.finesse.restservices.TeamWorkflows = TeamWorkflows; 14448 14449 return TeamWorkflows; 14450 })); 14451 14452 /** 14453 * JavaScript representation of the Finesse Team REST object. 14454 * 14455 * @requires finesse.clientservices.ClientServices 14456 * @requires Class 14457 * @requires finesse.FinesseBase 14458 * @requires finesse.restservices.RestBase 14459 * @requires finesse.restservices.RestCollectionBase 14460 * @requires finesse.restservices.User 14461 * @requires finesse.restservices.Users 14462 */ 14463 14464 /** 14465 * The following comment prevents JSLint errors concerning undefined global variables. 14466 * It tells JSLint that these identifiers are defined elsewhere. 14467 */ 14468 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */ 14469 14470 /** The following comment is to prevent jslint errors about 14471 * using variables before they are defined. 14472 */ 14473 /*global $, jQuery, Handlebars, dojox, dojo, finesse */ 14474 14475 /** @private */ 14476 (function (factory) { 14477 14478 14479 // Define as an AMD module if possible 14480 if ( typeof define === 'function' && define.amd ) 14481 { 14482 define('restservices/Team', ['restservices/RestBase', 14483 'utilities/Utilities', 14484 'restservices/Users', 14485 'restservices/TeamNotReadyReasonCodes', 14486 'restservices/TeamWrapUpReasons', 14487 'restservices/TeamSignOutReasonCodes', 14488 'restservices/TeamPhoneBooks', 14489 'restservices/TeamLayoutConfig', 14490 'restservices/TeamWorkflows'], factory ); 14491 } 14492 /* Define using browser globals otherwise 14493 * Prevent multiple instantiations if the script is loaded twice 14494 */ 14495 else 14496 { 14497 factory(finesse.restservices.RestBase, 14498 finesse.utilities.Utilities, 14499 finesse.restservices.Users, 14500 finesse.restservices.TeamNotReadyReasonCodes, 14501 finesse.restservices.TeamWrapUpReasons, 14502 finesse.restservices.TeamSignOutReasonCodes, 14503 finesse.restservices.TeamPhoneBooks, 14504 finesse.restservices.TeamLayoutConfig, 14505 finesse.restservices.TeamWorkflows); 14506 } 14507 }(function (RestBase, Utilities, Users, TeamNotReadyReasonCodes, TeamWrapUpReasons, TeamSignOutReasonCodes, TeamPhoneBooks, TeamLayoutConfig, TeamWorkflows) { 14508 var Team = RestBase.extend(/** @lends finesse.restservices.Team.prototype */{ 14509 14510 _teamLayoutConfig: null, 14511 14512 /** 14513 * @class 14514 * A Team is a set of Agent Users, typically supervised by one or more Supervisor Users. 14515 * 14516 * @augments finesse.restservices.RestBase 14517 * @see finesse.restservices.User#getSupervisedTeams 14518 * @see finesse.restservices.Users 14519 * @constructs 14520 */ 14521 _fakeConstuctor: function () { 14522 /* This is here to hide the real init constructor from the public docs */ 14523 }, 14524 14525 /** 14526 * @private 14527 * @class 14528 * JavaScript representation of a Team object. Also exposes methods to operate 14529 * on the object against the server. 14530 * 14531 * @param {Object} options 14532 * An object with the following properties:<ul> 14533 * <li><b>id:</b> The id of the object being constructed</li> 14534 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 14535 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 14536 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 14537 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 14538 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 14539 * <li><b>status:</b> {Number} The HTTP status code returned</li> 14540 * <li><b>content:</b> {String} Raw string of response</li> 14541 * <li><b>object:</b> {Object} Parsed object of response</li> 14542 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 14543 * <li><b>errorType:</b> {String} Type of error that was caught</li> 14544 * <li><b>errorMessage:</b> {String} Message associated with error</li> 14545 * </ul></li> 14546 * </ul></li> 14547 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 14548 **/ 14549 init: function (options) { 14550 this._super(options); 14551 }, 14552 14553 /** 14554 * @private 14555 * Gets the REST class for the current object - this is the Team class. 14556 * @returns {Object} The Team constructor. 14557 */ 14558 getRestClass: function () { 14559 return finesse.restesrvices.Team; 14560 }, 14561 14562 /** 14563 * @private 14564 * Gets the REST type for the current object - this is a "Team". 14565 * @returns {String} The Team string. 14566 */ 14567 getRestType: function () { 14568 return "Team"; 14569 }, 14570 14571 /** 14572 * @private 14573 * Override default to indicate that this object doesn't support making 14574 * requests. 14575 */ 14576 supportsSubscriptions: false, 14577 14578 /** 14579 * Getter for the team id. 14580 * @returns {String} The team id. 14581 */ 14582 getId: function () { 14583 this.isLoaded(); 14584 return this.getData().id; 14585 }, 14586 14587 /** 14588 * Getter for the team name. 14589 * @returns {String} The team name 14590 */ 14591 getName: function () { 14592 this.isLoaded(); 14593 return this.getData().name; 14594 }, 14595 14596 /** 14597 * @private 14598 * Getter for the team uri. 14599 * @returns {String} The team uri 14600 */ 14601 getUri: function () { 14602 this.isLoaded(); 14603 return this.getData().uri; 14604 }, 14605 14606 /** 14607 * Constructs and returns a collection of Users. 14608 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers. 14609 * @returns {finesse.restservices.Users} Users collection of User objects. 14610 */ 14611 getUsers: function (options) { 14612 this.isLoaded(); 14613 options = options || {}; 14614 14615 options.parentObj = this; 14616 // We are using getData() instead of getData.Users because the superclass (RestCollectionBase) 14617 // for Users needs the "Users" key to validate the provided payload matches the class type. 14618 options.data = this.getData(); 14619 14620 return new Users(options); 14621 }, 14622 14623 /** 14624 * @private 14625 * Getter for a teamNotReadyReasonCodes collection object that is associated with Team. 14626 * @param callbacks 14627 * @returns {teamNotReadyReasonCodes} 14628 * A teamNotReadyReasonCodes collection object. 14629 */ 14630 getTeamNotReadyReasonCodes: function (callbacks) { 14631 var options = callbacks || {}; 14632 options.parentObj = this; 14633 this.isLoaded(); 14634 14635 if (!this._teamNotReadyReasonCodes) { 14636 this._teamNotReadyReasonCodes = new TeamNotReadyReasonCodes(options); 14637 } 14638 14639 return this._teamNotReadyReasonCodes; 14640 }, 14641 14642 /** 14643 * @private 14644 * Getter for a teamWrapUpReasons collection object that is associated with Team. 14645 * @param callbacks 14646 * @returns {teamWrapUpReasons} 14647 * A teamWrapUpReasons collection object. 14648 */ 14649 getTeamWrapUpReasons: function (callbacks) { 14650 var options = callbacks || {}; 14651 options.parentObj = this; 14652 this.isLoaded(); 14653 14654 if (!this._teamWrapUpReasons) { 14655 this._teamWrapUpReasons = new TeamWrapUpReasons(options); 14656 } 14657 14658 return this._teamWrapUpReasons; 14659 }, 14660 14661 /** 14662 * @private 14663 * Getter for a teamSignOutReasonCodes collection object that is associated with Team. 14664 * @param callbacks 14665 * @returns {teamSignOutReasonCodes} 14666 * A teamSignOutReasonCodes collection object. 14667 */ 14668 14669 getTeamSignOutReasonCodes: function (callbacks) { 14670 var options = callbacks || {}; 14671 options.parentObj = this; 14672 this.isLoaded(); 14673 14674 if (!this._teamSignOutReasonCodes) { 14675 this._teamSignOutReasonCodes = new TeamSignOutReasonCodes(options); 14676 } 14677 14678 return this._teamSignOutReasonCodes; 14679 }, 14680 14681 /** 14682 * @private 14683 * Getter for a teamPhoneBooks collection object that is associated with Team. 14684 * @param callbacks 14685 * @returns {teamPhoneBooks} 14686 * A teamPhoneBooks collection object. 14687 */ 14688 getTeamPhoneBooks: function (callbacks) { 14689 var options = callbacks || {}; 14690 options.parentObj = this; 14691 this.isLoaded(); 14692 14693 if (!this._phonebooks) { 14694 this._phonebooks = new TeamPhoneBooks(options); 14695 } 14696 14697 return this._phonebooks; 14698 }, 14699 14700 /** 14701 * @private 14702 * Getter for a teamWorkflows collection object that is associated with Team. 14703 * @param callbacks 14704 * @returns {teamWorkflows} 14705 * A teamWorkflows collection object. 14706 */ 14707 getTeamWorkflows: function (callbacks) { 14708 var options = callbacks || {}; 14709 options.parentObj = this; 14710 this.isLoaded(); 14711 14712 if (!this._workflows) { 14713 this._workflows = new TeamWorkflows(options); 14714 } 14715 14716 return this._workflows; 14717 }, 14718 14719 /** 14720 * @private 14721 * Getter for a teamLayoutConfig object that is associated with Team. 14722 * @param callbacks 14723 * @returns {teamLayoutConfig} 14724 */ 14725 getTeamLayoutConfig: function (callbacks) { 14726 var options = callbacks || {}; 14727 options.parentObj = this; 14728 this.isLoaded(); 14729 14730 if (this._teamLayoutConfig === null) { 14731 this._teamLayoutConfig = new TeamLayoutConfig(options); 14732 } 14733 14734 return this._teamLayoutConfig; 14735 } 14736 14737 }); 14738 14739 window.finesse = window.finesse || {}; 14740 window.finesse.restservices = window.finesse.restservices || {}; 14741 window.finesse.restservices.Team = Team; 14742 14743 return Team; 14744 })); 14745 14746 /** 14747 * JavaScript representation of the Finesse Teams collection. 14748 * object which contains a list of Team objects 14749 * @requires finesse.clientservices.ClientServices 14750 * @requires Class 14751 * @requires finesse.FinesseBase 14752 * @requires finesse.restservices.RestBase 14753 * @requires finesse.restservices.RestCollectionBase 14754 */ 14755 14756 /** @private */ 14757 (function (factory) { 14758 14759 14760 // Define as an AMD module if possible 14761 if ( typeof define === 'function' && define.amd ) 14762 { 14763 define('restservices/Teams', ['restservices/RestCollectionBase', 14764 'restservices/Team'], factory ); 14765 } 14766 /* Define using browser globals otherwise 14767 * Prevent multiple instantiations if the script is loaded twice 14768 */ 14769 else 14770 { 14771 factory(finesse.restservices.RestCollectionBase, finesse.restservices.Team); 14772 } 14773 }(function (RestCollectionBase, Team) { 14774 /** @private */ 14775 var Teams = RestCollectionBase.extend({ 14776 14777 /** 14778 * @class 14779 * JavaScript representation of a Teams collection object. Also exposes methods to operate 14780 * on the object against the server. 14781 * 14782 * @param {Object} options 14783 * An object with the following properties:<ul> 14784 * <li><b>id:</b> The id of the object being constructed</li> 14785 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 14786 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 14787 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 14788 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 14789 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 14790 * <li><b>status:</b> {Number} The HTTP status code returned</li> 14791 * <li><b>content:</b> {String} Raw string of response</li> 14792 * <li><b>object:</b> {Object} Parsed object of response</li> 14793 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 14794 * <li><b>errorType:</b> {String} Type of error that was caught</li> 14795 * <li><b>errorMessage:</b> {String} Message associated with error</li> 14796 * </ul></li> 14797 * </ul></li> 14798 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 14799 * @constructs 14800 **/ 14801 init: function (options) { 14802 this._super(options); 14803 }, 14804 14805 /** 14806 * @private 14807 * Gets the REST class for the current object - this is the Teams class. 14808 * @returns {Object} The Teams constructor. 14809 */ 14810 getRestClass: function () { 14811 return Teams; 14812 }, 14813 14814 /** 14815 * @private 14816 * Gets the REST class for the objects that make up the collection. - this 14817 * is the Team class. 14818 */ 14819 getRestItemClass: function () { 14820 return Team; 14821 }, 14822 14823 /** 14824 * @private 14825 * Gets the REST type for the current object - this is a "Teams". 14826 * @returns {String} The Teams string. 14827 */ 14828 getRestType: function () { 14829 return "Teams"; 14830 }, 14831 14832 /** 14833 * @private 14834 * Gets the REST type for the objects that make up the collection - this is "Team". 14835 */ 14836 getRestItemType: function () { 14837 return "Team"; 14838 }, 14839 14840 /** 14841 * @private 14842 * Override default to indicates that the collection supports making 14843 * requests. 14844 */ 14845 supportsRequests: true, 14846 14847 /** 14848 * @private 14849 * Override default to indicate that this object doesn't support subscriptions. 14850 */ 14851 supportsRestItemSubscriptions: false, 14852 14853 /** 14854 * @private 14855 * Retrieve the Teams. This call will re-query the server and refresh the collection. 14856 * 14857 * @returns {finesse.restservices.Teams} 14858 * This Teams object to allow cascading. 14859 */ 14860 get: function () { 14861 // set loaded to false so it will rebuild the collection after the get 14862 this._loaded = false; 14863 // reset collection 14864 this._collection = {}; 14865 // perform get 14866 this._synchronize(); 14867 return this; 14868 } 14869 14870 }); 14871 14872 window.finesse = window.finesse || {}; 14873 window.finesse.restservices = window.finesse.restservices || {}; 14874 window.finesse.restservices.Teams = Teams; 14875 14876 return Teams; 14877 })); 14878 14879 /** 14880 * JavaScript representation of the Finesse SystemInfo object 14881 * 14882 * @requires finesse.clientservices.ClientServices 14883 * @requires Class 14884 * @requires finesse.FinesseBase 14885 * @requires finesse.restservices.RestBase 14886 */ 14887 14888 /** @private */ 14889 (function (factory) { 14890 14891 14892 // Define as an AMD module if possible 14893 if ( typeof define === 'function' && define.amd ) 14894 { 14895 define('restservices/SystemInfo', ['restservices/RestBase'], factory ); 14896 } 14897 /* Define using browser globals otherwise 14898 * Prevent multiple instantiations if the script is loaded twice 14899 */ 14900 else 14901 { 14902 factory(finesse.restservices.RestBase); 14903 } 14904 }(function (RestBase) { 14905 14906 var SystemInfo = RestBase.extend(/** @lends finesse.restservices.SystemInfo.prototype */{ 14907 /** 14908 * @private 14909 * Returns whether this object supports subscriptions 14910 */ 14911 supportsSubscriptions: false, 14912 14913 doNotRefresh: true, 14914 14915 /** 14916 * @class 14917 * JavaScript representation of a SystemInfo object. 14918 * 14919 * @augments finesse.restservices.RestBase 14920 * @see finesse.restservices.SystemInfo.Statuses 14921 * @constructs 14922 */ 14923 _fakeConstuctor: function () { 14924 /* This is here to hide the real init constructor from the public docs */ 14925 }, 14926 14927 /** 14928 * @private 14929 * JavaScript representation of a SystemInfo object. Also exposes methods to operate 14930 * on the object against the server. 14931 * 14932 * @param {Object} options 14933 * An object with the following properties:<ul> 14934 * <li><b>id:</b> The id of the object being constructed</li> 14935 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 14936 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 14937 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 14938 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 14939 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 14940 * <li><b>status:</b> {Number} The HTTP status code returned</li> 14941 * <li><b>content:</b> {String} Raw string of response</li> 14942 * <li><b>object:</b> {Object} Parsed object of response</li> 14943 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 14944 * <li><b>errorType:</b> {String} Type of error that was caught</li> 14945 * <li><b>errorMessage:</b> {String} Message associated with error</li> 14946 * </ul></li> 14947 * </ul></li> 14948 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 14949 **/ 14950 init: function (id, callbacks, restObj) 14951 { 14952 this._super(id, callbacks, restObj); 14953 }, 14954 14955 /** 14956 * @private 14957 * Gets the REST class for the current object - this is the SystemInfo object. 14958 */ 14959 getRestClass: function () { 14960 return SystemInfo; 14961 }, 14962 14963 /** 14964 * @private 14965 * Gets the REST type for the current object - this is a "SystemInfo". 14966 */ 14967 getRestType: function () 14968 { 14969 return "SystemInfo"; 14970 }, 14971 14972 _validate: function (obj) 14973 { 14974 return true; 14975 }, 14976 14977 /** 14978 * Returns the status of the Finesse system. 14979 * IN_SERVICE if the Finesse API reports that it is in service, 14980 * OUT_OF_SERVICE otherwise. 14981 * @returns {finesse.restservices.SystemInfo.Statuses} System Status 14982 */ 14983 getStatus: function () { 14984 this.isLoaded(); 14985 return this.getData().status; 14986 }, 14987 14988 /** 14989 * Returns the current timestamp from this SystemInfo object. 14990 * This is used to calculate time drift delta between server and client. 14991 * @returns {String} Time (GMT): yyyy-MM-dd'T'HH:mm:ss'Z' 14992 */ 14993 getCurrentTimestamp: function () { 14994 this.isLoaded(); 14995 return this.getData().currentTimestamp; 14996 }, 14997 14998 /** 14999 * Getter for the xmpp domain of the system. 15000 * @returns {String} The xmpp domain corresponding to this SystemInfo object. 15001 */ 15002 getXmppDomain: function () { 15003 this.isLoaded(); 15004 return this.getData().xmppDomain; 15005 }, 15006 15007 /** 15008 * Getter for the xmpp pubsub domain of the system. 15009 * @returns {String} The xmpp pubsub domain corresponding to this SystemInfo object. 15010 */ 15011 getXmppPubSubDomain: function () { 15012 this.isLoaded(); 15013 return this.getData().xmppPubSubDomain; 15014 }, 15015 15016 /** 15017 * Getter for the deployment type (UCCE or UCCX). 15018 * @returns {String} "UCCE" or "UCCX" 15019 */ 15020 getDeploymentType: function () { 15021 this.isLoaded(); 15022 return this.getData().deploymentType; 15023 }, 15024 15025 /** 15026 * Returns whether this is a single node deployment or not by checking for the existence of the secondary node in SystemInfo. 15027 * @returns {Boolean} True for single node deployments, false otherwise. 15028 */ 15029 isSingleNode: function () { 15030 var secondary = this.getData().secondaryNode; 15031 if (secondary && secondary.host) { 15032 return false; 15033 } 15034 return true; 15035 }, 15036 15037 /** 15038 * Checks all arguments against the primary and secondary hosts (FQDN) and returns the match. 15039 * This is useful for getting the FQDN of the current Finesse server. 15040 * @param {String} ...arguments[]... - any number of arguments to match against 15041 * @returns {String} FQDN (if properly configured) of the matched host of the primary or secondary node, or undefined if no match is found. 15042 */ 15043 getThisHost: function () { 15044 var i, 15045 primary = this.getData().primaryNode, 15046 secondary = this.getData().secondaryNode; 15047 15048 for (i = 0; (i < arguments.length); i = i + 1) { 15049 if (primary && arguments[i] === primary.host) { 15050 return primary.host; 15051 } else if (secondary && arguments[i] === secondary.host) { 15052 return secondary.host; 15053 } 15054 } 15055 }, 15056 15057 /** 15058 * Checks all arguments against the primary and secondary hosts (FQDN) and returns the other node. 15059 * This is useful for getting the FQDN of the other Finesse server, i.e. for failover purposes. 15060 * @param {String} arguments - any number of arguments to match against 15061 * @returns {String} FQDN (if properly configured) of the alternate node, defaults to primary if no match is found, undefined for single node deployments. 15062 */ 15063 getAlternateHost: function () { 15064 var i, 15065 isPrimary = false, 15066 primary = this.getData().primaryNode, 15067 secondary = this.getData().secondaryNode, 15068 xmppDomain = this.getData().xmppDomain, 15069 alternateHost; 15070 15071 if (primary && primary.host) { 15072 if (xmppDomain === primary.host) { 15073 isPrimary = true; 15074 } 15075 if (secondary && secondary.host) { 15076 if (isPrimary) { 15077 return secondary.host; 15078 } 15079 return primary.host; 15080 } 15081 } 15082 } 15083 }); 15084 15085 SystemInfo.Statuses = /** @lends finesse.restservices.SystemInfo.Statuses.prototype */ { 15086 /** 15087 * Finesse is in service. 15088 */ 15089 IN_SERVICE: "IN_SERVICE", 15090 /** 15091 * Finesse is not in service. 15092 */ 15093 OUT_OF_SERVICE: "OUT_OF_SERVICE", 15094 /** 15095 * @class SystemInfo status values. 15096 * @constructs 15097 */ 15098 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 15099 15100 }; 15101 15102 window.finesse = window.finesse || {}; 15103 window.finesse.restservices = window.finesse.restservices || {}; 15104 window.finesse.restservices.SystemInfo = SystemInfo; 15105 15106 return SystemInfo; 15107 })); 15108 15109 /** 15110 * Provides standard way resolve message keys with substitution 15111 * 15112 * @requires finesse.container.I18n or gadgets.Prefs 15113 */ 15114 15115 // Add Utilities to the finesse.utilities namespace 15116 var finesse = finesse || {}; 15117 finesse.utilities = finesse.utilities || {}; 15118 15119 (function (factory) { 15120 15121 15122 // Define as an AMD module if possible 15123 if ( typeof define === 'function' && define.amd ) 15124 { 15125 define('utilities/I18n', [], factory ); 15126 } 15127 /* Define using browser globals otherwise 15128 * Prevent multiple instantiations if the script is loaded twice 15129 */ 15130 else 15131 { 15132 finesse.utilities.I18n = factory(); 15133 } 15134 }(function () { 15135 var I18n = (function () { 15136 15137 /** 15138 * Shortcut to finesse.container.I18n.getMsg or gadgets.Prefs.getMsg 15139 * @private 15140 */ 15141 var _getMsg; 15142 15143 return { 15144 /** 15145 * Provides a message resolver for this utility singleton. 15146 * @param {Function} getMsg 15147 * A function that returns a string given a message key. 15148 * If the key is not found, this function must return 15149 * something that tests false (i.e. undefined or ""). 15150 */ 15151 setGetter : function (getMsg) { 15152 _getMsg = getMsg; 15153 }, 15154 15155 /** 15156 * Resolves the given message key, also performing substitution. 15157 * This generic utility will use a custom function to resolve the key 15158 * provided by finesse.utilities.I18n.setGetter. Otherwise, it will 15159 * discover either finesse.container.I18n.getMsg or gadgets.Prefs.getMsg 15160 * upon the first invocation and store that reference for efficiency. 15161 * 15162 * Since this will construct a new gadgets.Prefs object, it is recommended 15163 * for gadgets to explicitly provide the setter to prevent duplicate 15164 * gadgets.Prefs objects. This does not apply if your gadget does not need 15165 * access to gadgets.Prefs other than getMsg. 15166 * 15167 * @param {String} key 15168 * The key to lookup 15169 * @param {String} arguments 15170 * Arguments for substitution 15171 * @returns {String/Function} 15172 * The resolved string if successful, otherwise a function that returns 15173 * a '???' string that can also be casted into a string. 15174 */ 15175 getString : function (key) { 15176 var prefs, i, retStr, noMsg, getFailed = ""; 15177 if (!_getMsg) { 15178 if (finesse.container && finesse.container.I18n) { 15179 _getMsg = finesse.container.I18n.getMsg; 15180 } else if (gadgets) { 15181 prefs = new gadgets.Prefs(); 15182 _getMsg = prefs.getMsg; 15183 } 15184 } 15185 15186 try { 15187 retStr = _getMsg(key); 15188 } catch (e) { 15189 getFailed = "finesse.utilities.I18n.getString(): invalid _getMsg"; 15190 } 15191 15192 if (retStr) { // Lookup was successful, perform substitution (if any) 15193 for (i = 1; i < arguments.length; i += 1) { 15194 retStr = retStr.replace(new RegExp("\\{" + (i - 1) + "\\}", "g"), arguments[i]); 15195 } 15196 //in order to fix French text with single quotes in it, we need to replace \' with ' 15197 return retStr.replace(/\\'/g, "'"); 15198 } 15199 // We want a function because jQuery.html() and jQuery.text() is smart enough to invoke it. 15200 /** @private */ 15201 noMsg = function () { 15202 return "???" + key + "???" + getFailed; 15203 }; 15204 // We overload the toString() of this "function" to allow JavaScript to cast it into a string 15205 // For example, var myMsg = "something " + finesse.utilities.I18n.getMsg("unresolvable.key"); 15206 /** @private */ 15207 noMsg.toString = function () { 15208 return "???" + key + "???" + getFailed; 15209 }; 15210 return noMsg; 15211 15212 } 15213 }; 15214 }()); 15215 15216 return I18n; 15217 })); 15218 15219 /** 15220 * Logging.js: provides simple logging for clients to use and overrides synchronous native methods: alert(), confirm(), and prompt(). 15221 * 15222 * On Firefox, it will hook into console for logging. On IE, it will log to the status bar. 15223 */ 15224 // Add Utilities to the finesse.utilities namespace 15225 var finesse = finesse || {}; 15226 finesse.utilities = finesse.utilities || {}; 15227 15228 (function (factory) { 15229 15230 15231 // Define as an AMD module if possible 15232 if ( typeof define === 'function' && define.amd ) 15233 { 15234 define('utilities/Logger', [], factory ); 15235 } 15236 /* Define using browser globals otherwise 15237 * Prevent multiple instantiations if the script is loaded twice 15238 */ 15239 else 15240 { 15241 finesse.utilities.Logger = factory(); 15242 } 15243 }(function () { 15244 var Logger = (function () { 15245 15246 var 15247 15248 /** @private **/ 15249 debugOn, 15250 15251 /** 15252 * Pads a single digit number for display purposes (e.g. '4' shows as '04') 15253 * @param num is the number to pad to 2 digits 15254 * @returns a two digit padded string 15255 * @private 15256 */ 15257 padTwoDigits = function (num) { 15258 return (num < 10) ? '0' + num : num; 15259 }, 15260 15261 /** 15262 * Checks to see if we have a console - this allows us to support Firefox or IE. 15263 * @returns {Boolean} True for Firefox, False for IE 15264 * @private 15265 */ 15266 hasConsole = function () { 15267 var retval = false; 15268 try 15269 { 15270 if (window.console !== undefined) 15271 { 15272 retval = true; 15273 } 15274 } 15275 catch (err) 15276 { 15277 retval = false; 15278 } 15279 15280 return retval; 15281 }, 15282 15283 /** 15284 * Gets a timestamp. 15285 * @returns {String} is a timestamp in the following format: HH:MM:SS 15286 * @private 15287 */ 15288 getTimeStamp = function () { 15289 var date = new Date(), timeStr; 15290 timeStr = padTwoDigits(date.getHours()) + ":" + padTwoDigits(date.getMinutes()) + ":" + padTwoDigits(date.getSeconds()); 15291 15292 return timeStr; 15293 }; 15294 15295 return { 15296 /** 15297 * Enable debug mode. Debug mode may impact performance on the UI. 15298 * 15299 * @param {Boolean} enable 15300 * True to enable debug logging. 15301 * @private 15302 */ 15303 setDebug : function (enable) { 15304 debugOn = enable; 15305 }, 15306 15307 /** 15308 * Logs a string as DEBUG. 15309 * 15310 * @param str is the string to log. 15311 * @private 15312 */ 15313 log : function (str) { 15314 var timeStr = getTimeStamp(); 15315 15316 if (debugOn) { 15317 if (hasConsole()) 15318 { 15319 window.console.log(timeStr + ": " + "DEBUG" + " - " + str); 15320 } 15321 } 15322 }, 15323 15324 /** 15325 * Logs a string as INFO. 15326 * 15327 * @param str is the string to log. 15328 * @private 15329 */ 15330 info : function (str) { 15331 var timeStr = getTimeStamp(); 15332 15333 if (hasConsole()) 15334 { 15335 window.console.info(timeStr + ": " + "INFO" + " - " + str); 15336 } 15337 }, 15338 15339 /** 15340 * Logs a string as WARN. 15341 * 15342 * @param str is the string to log. 15343 * @private 15344 */ 15345 warn : function (str) { 15346 var timeStr = getTimeStamp(); 15347 15348 if (hasConsole()) 15349 { 15350 window.console.warn(timeStr + ": " + "WARN" + " - " + str); 15351 } 15352 }, 15353 /** 15354 * Logs a string as ERROR. 15355 * 15356 * @param str is the string to log. 15357 * @private 15358 */ 15359 error : function (str) { 15360 var timeStr = getTimeStamp(); 15361 15362 if (hasConsole()) 15363 { 15364 window.console.error(timeStr + ": " + "ERROR" + " - " + str); 15365 } 15366 } 15367 }; 15368 }()); 15369 15370 return Logger; 15371 })); 15372 15373 /** 15374 * Allows gadgets to call the log function to publish client logging messages over the hub. 15375 * 15376 * @requires OpenAjax 15377 */ 15378 /** @private */ 15379 (function (factory) { 15380 15381 15382 // Define as an AMD module if possible 15383 if ( typeof define === 'function' && define.amd ) 15384 { 15385 define('cslogger/ClientLogger',[], factory ); 15386 } 15387 15388 /* Define using browser globals otherwise 15389 * Prevent multiple instantiations if the script is loaded twice 15390 */ 15391 else 15392 { 15393 factory(); 15394 } 15395 15396 }(function () { 15397 15398 var ClientLogger = ( function () { /** @lends finesse.cslogger.ClientLogger.prototype */ 15399 var _hub, _logTopic, _originId, _sessId, _host, 15400 MONTH = { 0 : "Jan", 1 : "Feb", 2 : "Mar", 3 : "Apr", 4 : "May", 5 : "Jun", 15401 6 : "Jul", 7 : "Aug", 8 : "Sep", 9 : "Oct", 10 : "Nov", 11 : "Dec"}, 15402 15403 /** 15404 * Gets timestamp drift stored in sessionStorage 15405 * @returns drift in seconds if it is set in sessionStorage otherwise returns undefined. 15406 * @private 15407 */ 15408 getTsDrift = function() { 15409 if (window.sessionStorage.getItem('clientTimestampDrift') !== null) { 15410 return parseInt(window.sessionStorage.getItem('clientTimestampDrift'), 10); 15411 } 15412 else { 15413 return undefined; 15414 } 15415 }, 15416 15417 /** 15418 * Sets timestamp drift in sessionStorage 15419 * @param delta is the timestamp drift between server.and client. 15420 * @private 15421 */ 15422 setTsDrift = function(delta) { 15423 window.sessionStorage.setItem('clientTimestampDrift', delta.toString()); 15424 }, 15425 15426 /** 15427 * Gets Finesse server timezone offset from GMT in seconds 15428 * @returns offset in seconds if it is set in sessionStorage otherwise returns undefined. 15429 * @private 15430 */ 15431 getServerOffset = function() { 15432 if (window.sessionStorage.getItem('serverTimezoneOffset') !== null) { 15433 return parseInt(window.sessionStorage.getItem('serverTimezoneOffset'), 10); 15434 } 15435 else { 15436 return undefined; 15437 } 15438 }, 15439 15440 /** 15441 * Sets server timezone offset 15442 * @param sec is the server timezone GMT offset in seconds. 15443 * @private 15444 */ 15445 setServerOffset = function(sec) { 15446 window.sessionStorage.setItem('serverTimezoneOffset', sec.toString()); 15447 }, 15448 15449 /** 15450 * Checks to see if we have a console. 15451 * @returns Whether the console object exists. 15452 * @private 15453 */ 15454 hasConsole = function () { 15455 try { 15456 if (window.console !== undefined) { 15457 return true; 15458 } 15459 } 15460 catch (err) { 15461 // ignore and return false 15462 } 15463 15464 return false; 15465 }, 15466 15467 /** 15468 * Gets a short form (6 character) session ID from sessionStorage 15469 * @private 15470 */ 15471 getSessId = function() { 15472 if (!_sessId) { 15473 //when _sessId not defined yet, initiate it 15474 if (window.sessionStorage.getItem('enableLocalLog') === 'true') { 15475 _sessId= " "+window.sessionStorage.getItem('finSessKey'); 15476 } 15477 else { 15478 _sessId=" "; 15479 } 15480 } 15481 return _sessId; 15482 }, 15483 15484 /** 15485 * Pads a single digit number for display purposes (e.g. '4' shows as '04') 15486 * @param num is the number to pad to 2 digits 15487 * @returns a two digit padded string 15488 * @private 15489 */ 15490 padTwoDigits = function (num) 15491 { 15492 return (num < 10) ? '0' + num : num; 15493 }, 15494 15495 /** 15496 * Pads a single digit number for display purposes (e.g. '4' shows as '004') 15497 * @param num is the number to pad to 3 digits 15498 * @returns a three digit padded string 15499 * @private 15500 */ 15501 padThreeDigits = function (num) 15502 { 15503 if (num < 10) 15504 { 15505 return '00'+num; 15506 } 15507 else if (num < 100) 15508 { 15509 return '0'+num; 15510 } 15511 else 15512 { 15513 return num; 15514 } 15515 }, 15516 15517 /** 15518 * Compute the "hour" 15519 * 15520 * @param s is time in seconds 15521 * @returns {String} which is the hour 15522 * @private 15523 */ 15524 ho = function (s) { 15525 return ((s/60).toString()).split(".")[0]; 15526 }, 15527 15528 /** 15529 * Gets local timezone offset string. 15530 * 15531 * @param t is the time in seconds 15532 * @param s is the separator character between hours and minutes, e.g. ':' 15533 * @returns {String} is local timezone GMT offset in the following format: [+|-]hh[|:]MM 15534 * @private 15535 */ 15536 getGmtOffString = function (min,s) { 15537 var t, sign; 15538 if (min<0) { 15539 t = -min; 15540 sign = "-"; 15541 } 15542 else { 15543 t = min; 15544 sign = "+"; 15545 } 15546 15547 if (s===':') { 15548 return sign+padTwoDigits(ho(t))+s+padTwoDigits(t%60); 15549 } 15550 else { 15551 return sign+padTwoDigits(ho(t))+padTwoDigits(t%60); 15552 } 15553 }, 15554 15555 /** 15556 * Gets short form of a month name in English 15557 * 15558 * @param monthNum is zero-based month number 15559 * @returns {String} is short form of month name in English 15560 * @private 15561 */ 15562 getMonthShortStr = function (monthNum) { 15563 var result; 15564 try { 15565 result = MONTH[monthNum]; 15566 } 15567 catch (err) { 15568 if (hasConsole()) { 15569 window.console.log("Month must be between 0 and 11"); 15570 } 15571 } 15572 return result; 15573 }, 15574 15575 /** 15576 * Gets a timestamp. 15577 * @param aDate is a javascript Date object 15578 * @returns {String} is a timestamp in the following format: yyyy-mm-ddTHH:MM:ss.SSS [+|-]HH:MM 15579 * @private 15580 */ 15581 getDateTimeStamp = function (aDate) 15582 { 15583 var date, off, timeStr; 15584 if (aDate === null) { 15585 date = new Date(); 15586 } 15587 else { 15588 date = aDate; 15589 } 15590 off = -1*date.getTimezoneOffset(); 15591 timeStr = date.getFullYear().toString() + "-" + 15592 padTwoDigits(date.getMonth()+1) + "-" + 15593 padTwoDigits (date.getDate()) + "T"+ 15594 padTwoDigits(date.getHours()) + ":" + 15595 padTwoDigits(date.getMinutes()) + ":" + 15596 padTwoDigits(date.getSeconds())+"." + 15597 padThreeDigits(date.getMilliseconds()) + " "+ 15598 getGmtOffString(off, ':'); 15599 15600 return timeStr; 15601 }, 15602 15603 /** 15604 * Gets drift-adjusted timestamp. 15605 * @param aTimestamp is a timestamp in milliseconds 15606 * @param drift is a timestamp drift in milliseconds 15607 * @param serverOffset is a timezone GMT offset in minutes 15608 * @returns {String} is a timestamp in the Finesse server log format, e.g. Jan 07 2104 HH:MM:ss.SSS -0500 15609 * @private 15610 */ 15611 getDriftedDateTimeStamp = function (aTimestamp, drift, serverOffset) 15612 { 15613 var date, timeStr, localOffset; 15614 if (aTimestamp === null) { 15615 return "--- -- ---- --:--:--.--- -----"; 15616 } 15617 else if (drift === undefined || serverOffset === undefined) { 15618 if (hasConsole()) { 15619 window.console.log("drift or serverOffset must be a number"); 15620 } 15621 return "--- -- ---- --:--:--.--- -----"; 15622 } 15623 else { 15624 //need to get a zone diff in minutes 15625 localOffset = (new Date()).getTimezoneOffset(); 15626 date = new Date(aTimestamp+drift+(localOffset+serverOffset)*60000); 15627 timeStr = getMonthShortStr(date.getMonth()) + " "+ 15628 padTwoDigits (date.getDate())+ " "+ 15629 date.getFullYear().toString() + " "+ 15630 padTwoDigits(date.getHours()) + ":" + 15631 padTwoDigits(date.getMinutes()) + ":" + 15632 padTwoDigits(date.getSeconds())+"." + 15633 padThreeDigits(date.getMilliseconds())+" "+ 15634 getGmtOffString(serverOffset, ''); 15635 return timeStr; 15636 } 15637 }, 15638 15639 /** 15640 * Logs a message to a hidden textarea element on the page 15641 * 15642 * @param msg is the string to log. 15643 * @private 15644 */ 15645 writeToLogOutput = function (msg) { 15646 var logOutput = document.getElementById("finesseLogOutput"); 15647 15648 if (logOutput === null) 15649 { 15650 logOutput = document.createElement("textarea"); 15651 logOutput.id = "finesseLogOutput"; 15652 logOutput.style.display = "none"; 15653 document.body.appendChild(logOutput); 15654 } 15655 15656 if (logOutput.value === "") 15657 { 15658 logOutput.value = msg; 15659 } 15660 else 15661 { 15662 logOutput.value = logOutput.value + "\n" + msg; 15663 } 15664 }, 15665 15666 /* 15667 * Logs a message to console 15668 * @param str is the string to log. * @private 15669 */ 15670 logToConsole = function (str) 15671 { 15672 var now, msg, timeStr, driftedTimeStr, sessKey=getSessId(); 15673 now = new Date(); 15674 timeStr = getDateTimeStamp(now); 15675 if (getTsDrift() !== undefined) { 15676 driftedTimeStr = getDriftedDateTimeStamp(now.getTime(), getTsDrift(), getServerOffset()); 15677 } 15678 else { 15679 driftedTimeStr = getDriftedDateTimeStamp(null, 0, 0); 15680 } 15681 msg = timeStr + ":"+sessKey+": "+ _host + ": "+driftedTimeStr+ ": " + str; 15682 // Log to console 15683 if (hasConsole()) { 15684 window.console.log(msg); 15685 } 15686 15687 //Uncomment to print logs to hidden textarea. 15688 //writeToLogOutput(msg); 15689 15690 return msg; 15691 }; 15692 return { 15693 15694 /** 15695 * Publishes a Log Message over the hub. 15696 * 15697 * @param {String} message 15698 * The string to log. 15699 * @example 15700 * _clientLogger.log("This is some important message for MyGadget"); 15701 * 15702 */ 15703 log : function (message) { 15704 if(_hub) { 15705 _hub.publish(_logTopic, logToConsole(_originId + message)); 15706 } 15707 }, 15708 15709 /** 15710 * @class 15711 * Allows gadgets to call the log function to publish client logging messages over the hub. 15712 * 15713 * @constructs 15714 */ 15715 _fakeConstuctor: function () { 15716 /* This is here so we can document init() as a method rather than as a constructor. */ 15717 }, 15718 15719 /** 15720 * Initiates the client logger with a hub a gadgetId and gadget's config object. 15721 * @param {Object} hub 15722 * The hub to communicate with. 15723 * @param {String} gadgetId 15724 * A unique string to identify which gadget is doing the logging. 15725 * @param {Object} config 15726 * The config object used to get host name for that thirdparty gadget 15727 * @example 15728 * var _clientLogger = finesse.cslogger.ClientLogger; 15729 * _clientLogger.init(gadgets.Hub, "MyGadgetId", config); 15730 * 15731 */ 15732 init: function (hub, gadgetId, config) { 15733 _hub = hub; 15734 _logTopic = "finesse.clientLogging." + gadgetId; 15735 _originId = gadgetId + " : "; 15736 if ((config === undefined) || (config === "undefined")) 15737 { 15738 _host = ((finesse.container && finesse.container.Config && finesse.container.Config.host)?finesse.container.Config.host : "?.?.?.?"); 15739 } 15740 else 15741 { 15742 _host = ((config && config.host)?config.host : "?.?.?.?"); 15743 } 15744 } 15745 }; 15746 }()); 15747 15748 window.finesse = window.finesse || {}; 15749 window.finesse.cslogger = window.finesse.cslogger || {}; 15750 window.finesse.cslogger.ClientLogger = ClientLogger; 15751 15752 finesse = finesse || {}; 15753 /** @namespace Supports writing messages to a central log. */ 15754 finesse.cslogger = finesse.cslogger || {}; 15755 15756 return ClientLogger; 15757 })); 15758 15759 /* using variables before they are defined. 15760 */ 15761 /*global navigator,unescape,sessionStorage,localStorage,_initSessionList,_initSessionListComplete */ 15762 15763 /** 15764 * Allows each gadget to communicate with the server to send logs. 15765 */ 15766 15767 /** 15768 * @class 15769 * @private 15770 * Allows each product to initialize its method of storage 15771 */ 15772 (function (factory) { 15773 15774 15775 // Define as an AMD module if possible 15776 if ( typeof define === 'function' && define.amd ) 15777 { 15778 define('cslogger/FinesseLogger',["clientservices/ClientServices"], factory ); 15779 } 15780 15781 /* Define using browser globals otherwise 15782 * Prevent multiple instantiations if the script is loaded twice 15783 */ 15784 else 15785 { 15786 factory(finesse.clientservices.ClientServices); 15787 } 15788 15789 }(function (ClientServices) { 15790 window.finesse = window.finesse || {}; 15791 window.finesse.cslogger = window.finesse.cslogger || {}; 15792 /** @private */ 15793 window.finesse.cslogger.FinesseLogger = (function () { 15794 15795 var 15796 15797 /** 15798 * Array use to collect ongoing logs in memory 15799 * @private 15800 */ 15801 _logArray = [], 15802 15803 /** 15804 * The final data string sent to the server, =_logArray.join 15805 * @private 15806 */ 15807 _logStr = "", 15808 15809 /** 15810 * Keep track of size of log 15811 * @private 15812 */ 15813 _logSize = 0, 15814 15815 /** 15816 * Flag to keep track show/hide of send log link 15817 * @private 15818 */ 15819 _sendLogShown = false, 15820 15821 /** 15822 * Flag to keep track if local log initialized 15823 * @private 15824 */ 15825 _loggingInitialized = false, 15826 15827 15828 /** 15829 * local log size limit 15830 * @private 15831 */ 15832 _maxLocalStorageSize = 5000000, 15833 15834 /** 15835 * half local log size limit 15836 * @private 15837 */ 15838 _halfMaxLocalStorageSize = 0.5*_maxLocalStorageSize, 15839 15840 15841 /** 15842 * threshold for purge 15843 * @private 15844 */ 15845 _purgeStartPercent = 0.75, 15846 15847 /** 15848 * log item prefix 15849 * @private 15850 */ 15851 _linePrefix = null, 15852 15853 /** 15854 * locallog session 15855 * @private 15856 */ 15857 _session = null, 15858 15859 /** 15860 * Flag to keep track show/hide of send log link 15861 * @private 15862 */ 15863 _sessionKey = null, 15864 /** 15865 * Log session metadata 15866 * @private 15867 */ 15868 _logInfo = {}, 15869 15870 /** 15871 * Flag to find sessions 15872 * @private 15873 */ 15874 _findSessionsObj = null, 15875 15876 /** 15877 * Wrap up console.log esp. for IE9 15878 * @private 15879 */ 15880 _myConsoleLog = function (str) { 15881 if (window.console !== undefined) { 15882 window.console.log(str); 15883 } 15884 }, 15885 /** 15886 * Initialize the Local Logging 15887 * @private 15888 */ 15889 _initLogging = function () { 15890 if (_loggingInitialized) { 15891 return; 15892 } 15893 //Build a new store 15894 _session = sessionStorage.getItem("finSessKey"); 15895 //if the _session is null or empty, skip the init 15896 if (!_session) { 15897 return; 15898 } 15899 _sessionKey = "Fi"+_session; 15900 _linePrefix = _sessionKey + "_"; 15901 _logInfo = {}; 15902 _logInfo.name = _session; 15903 _logInfo.size = 0; 15904 _logInfo.head = 0; 15905 _logInfo.tail = 0; 15906 _logInfo.startTime = new Date().getTime(); 15907 _loggingInitialized = true; 15908 _initSessionList(); 15909 }, 15910 15911 /** 15912 * get total data size 15913 * 15914 * @return {Integer} which is the amount of data stored in local storage. 15915 * @private 15916 */ 15917 _getTotalData = function () 15918 { 15919 var sessName, sessLogInfoStr,sessLogInfoObj, sessionsInfoObj, totalData = 0, 15920 sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo"); 15921 if (!sessionsInfoStr) { 15922 return 0; 15923 } 15924 sessionsInfoObj = JSON.parse(sessionsInfoStr); 15925 15926 for (sessName in sessionsInfoObj.sessions) 15927 { 15928 if (sessionsInfoObj.sessions.hasOwnProperty(sessName)) { 15929 sessLogInfoStr = localStorage.getItem("Fi" + sessName); 15930 if (!sessLogInfoStr) { 15931 _myConsoleLog("_getTotalData failed to get log info for "+sessName); 15932 } 15933 else { 15934 sessLogInfoObj = JSON.parse(sessLogInfoStr); 15935 totalData = totalData + sessLogInfoObj.size; 15936 } 15937 } 15938 } 15939 15940 return totalData; 15941 }, 15942 15943 /** 15944 * Remove lines from tail up until store size decreases to half of max size limit. 15945 * 15946 * @private 15947 */ 15948 _purgeCurrentSession = function() { 15949 var curStoreSize, purgedSize=0, line, tailKey, secLogInfoStr, logInfoStr, theLogInfo; 15950 curStoreSize = _getTotalData(); 15951 if (curStoreSize < _halfMaxLocalStorageSize) { 15952 return; 15953 } 15954 logInfoStr = localStorage.getItem(_sessionKey); 15955 if (!logInfoStr) { 15956 return; 15957 } 15958 theLogInfo = JSON.parse(logInfoStr); 15959 //_myConsoleLog("Starting _purgeCurrentSession() - currentStoreSize=" + curStoreSize); 15960 while(curStoreSize > _halfMaxLocalStorageSize) { 15961 try { 15962 tailKey = _sessionKey+"_"+theLogInfo.tail; 15963 line = localStorage.getItem(tailKey); 15964 if (line) { 15965 purgedSize = purgedSize +line.length; 15966 localStorage.removeItem(tailKey); 15967 curStoreSize = curStoreSize - line.length; 15968 theLogInfo.size = theLogInfo.size - line.length; 15969 } 15970 } 15971 catch (err) { 15972 _myConsoleLog("purgeCurrentSession encountered err="+err); 15973 } 15974 if (theLogInfo.tail < theLogInfo.head) { 15975 theLogInfo.tail = theLogInfo.tail + 1; 15976 } 15977 else { 15978 break; 15979 } 15980 } 15981 //purge stops here, we need to update session's meta data in storage 15982 secLogInfoStr = localStorage.getItem(_sessionKey); 15983 if (!secLogInfoStr) { 15984 //somebody cleared the localStorage 15985 return; 15986 } 15987 15988 //_myConsoleLog("In _purgeCurrentSession() - after purging current session, currentStoreSize=" + curStoreSize); 15989 //_myConsoleLog("In _purgeCurrentSession() - after purging purgedSize=" + purgedSize); 15990 //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.size=" + theLogInfo.size); 15991 //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.tail=" + theLogInfo.tail); 15992 localStorage.setItem(_sessionKey, JSON.stringify(theLogInfo)); 15993 _myConsoleLog("Done _purgeCurrentSession() - currentStoreSize=" + curStoreSize); 15994 }, 15995 15996 /** 15997 * Purge a session 15998 * 15999 * @param sessionName is the name of the session 16000 * @return {Integer} which is the current amount of data purged 16001 * @private 16002 */ 16003 _purgeSession = function (sessionName) { 16004 var theLogInfo, logInfoStr, sessionsInfoStr, sessionsInfoObj; 16005 //Get the session logInfo 16006 logInfoStr = localStorage.getItem("Fi" + sessionName); 16007 if (!logInfoStr) { 16008 _myConsoleLog("_purgeSession failed to get logInfo for "+sessionName); 16009 return 0; 16010 } 16011 theLogInfo = JSON.parse(logInfoStr); 16012 16013 //Note: This assumes that we don't crash in the middle of purging 16014 //=> if we do then it should get deleted next time 16015 //Purge tail->head 16016 while (theLogInfo.tail <= theLogInfo.head) 16017 { 16018 try { 16019 localStorage.removeItem("Fi" + sessionName + "_" + theLogInfo.tail); 16020 theLogInfo.tail = theLogInfo.tail + 1; 16021 } 16022 catch (err) { 16023 _myConsoleLog("In _purgeSession err="+err); 16024 break; 16025 } 16026 } 16027 16028 //Remove the entire session 16029 localStorage.removeItem("Fi" + sessionName); 16030 16031 //Update FinesseSessionsInfo 16032 sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo"); 16033 if (!sessionsInfoStr) { 16034 _myConsoleLog("_purgeSession could not get sessions Info, it was cleared?"); 16035 return 0; 16036 } 16037 sessionsInfoObj = JSON.parse(sessionsInfoStr); 16038 if (sessionsInfoObj.sessions !== null) 16039 { 16040 delete sessionsInfoObj.sessions[sessionName]; 16041 16042 sessionsInfoObj.total = sessionsInfoObj.total - 1; 16043 sessionsInfoObj.lastWrittenBy = _session; 16044 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(sessionsInfoObj)); 16045 } 16046 16047 return theLogInfo.size; 16048 }, 16049 16050 /** 16051 * purge old sessions 16052 * 16053 * @param storeSize 16054 * @return {Boolean} whether purging reaches its target 16055 * @private 16056 */ 16057 _purgeOldSessions = function (storeSize) { 16058 var sessionsInfoStr, purgedSize = 0, sessName, sessions, curStoreSize, activeSession, sessionsInfoObj; 16059 sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo"); 16060 if (!sessionsInfoStr) { 16061 _myConsoleLog("Could not get FinesseSessionsInfo"); 16062 return true; 16063 } 16064 sessionsInfoObj = JSON.parse(sessionsInfoStr); 16065 curStoreSize = _getTotalData(); 16066 16067 activeSession = _session; 16068 sessions = sessionsInfoObj.sessions; 16069 for (sessName in sessions) { 16070 if (sessions.hasOwnProperty(sessName)) { 16071 if (sessName !== activeSession) { 16072 purgedSize = purgedSize + _purgeSession(sessName); 16073 if ((curStoreSize-purgedSize) < _halfMaxLocalStorageSize) { 16074 return true; 16075 } 16076 } 16077 } 16078 } 16079 //purge is not done, so return false 16080 return false; 16081 }, 16082 16083 /** 16084 * handle insert error 16085 * 16086 * @param error 16087 * @private 16088 */ 16089 _insertLineHandleError = function (error) { 16090 _myConsoleLog(error); 16091 }, 16092 16093 /** 16094 * check storage data size and if need purge 16095 * @private 16096 */ 16097 _checkSizeAndPurge = function () { 16098 var purgeIsDone=false, totalSize = _getTotalData(); 16099 if (totalSize > 0.75*_maxLocalStorageSize) { 16100 _myConsoleLog("in _checkSizeAndPurge, totalSize ("+totalSize+") exceeds limit"); 16101 purgeIsDone = _purgeOldSessions(totalSize); 16102 if (purgeIsDone) { 16103 _myConsoleLog("in _checkSizeAndPurge after purging old session, purge is done"); 16104 } 16105 else { 16106 //after all old sessions purged, still need purge 16107 totalSize = _getTotalData(); 16108 if (totalSize > 0.75*_maxLocalStorageSize) { 16109 _myConsoleLog("in _checkSizeAndPurge after purging old session,still needs purging, now storeSize ("+totalSize+")"); 16110 _purgeCurrentSession(); 16111 _myConsoleLog("in _checkSizeAndPurge done purging current session."); 16112 } 16113 } 16114 } 16115 }, 16116 16117 /** 16118 * check if the session is already in meta data 16119 * 16120 * @param metaData 16121 * @param sessionName 16122 * @return {Boolean} true if session has metaData (false otherwise) 16123 * @private 16124 */ 16125 _sessionsInfoContains = function (metaData, sessionName) { 16126 if (metaData && metaData.sessions && metaData.sessions.hasOwnProperty(sessionName)) { 16127 return true; 16128 } 16129 return false; 16130 }, 16131 16132 16133 /** 16134 * setup sessions in local storage 16135 * 16136 * @param logInfo 16137 * @private 16138 */ 16139 _getAndSetNumberOfSessions = function (logInfo) { 16140 var numOfSessionsPass1, numOfSessionsPass2, l; 16141 numOfSessionsPass1 = localStorage.getItem("FinesseSessionsInfo"); 16142 if (numOfSessionsPass1 === null) { 16143 //Init first time 16144 numOfSessionsPass1 = {}; 16145 numOfSessionsPass1.total = 1; 16146 numOfSessionsPass1.sessions = {}; 16147 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime; 16148 numOfSessionsPass1.lastWrittenBy = logInfo.name; 16149 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1)); 16150 } 16151 else { 16152 numOfSessionsPass1 = JSON.parse(numOfSessionsPass1); 16153 //check if the session is already in the FinesseSessionSInfo 16154 if (_sessionsInfoContains(numOfSessionsPass1, logInfo.name)) { 16155 return; 16156 } 16157 //Save numOfSessionsPass1 16158 numOfSessionsPass1.total = parseInt(numOfSessionsPass1.total, 10) + 1; 16159 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime; 16160 numOfSessionsPass1.lastWrittenBy = logInfo.name; 16161 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1)); 16162 numOfSessionsPass2 = localStorage.getItem("FinesseSessionsInfo"); 16163 if (!numOfSessionsPass2) { 16164 _myConsoleLog("Could not get FinesseSessionsInfo"); 16165 return; 16166 } 16167 numOfSessionsPass2 = JSON.parse(numOfSessionsPass2); 16168 //in future we need to confirm the numOfSessionsPass2 is the same as numOfSessionsPass1 16169 ////if (numOfSessionsPass1.lastWrittenBy !== numOfSessionsPass2.lastWrittenBy) { 16170 //// _myConsoleLog("Rebuild sessions"); 16171 //// _sessionTimerId = setTimeout(_initSessionList, 10000); 16172 ////} 16173 ////else { 16174 //// _sessionTimerId = null; 16175 ////callback(numOfSessionsPass2.sessions); 16176 ////} 16177 } 16178 if (!localStorage.getItem(_sessionKey)) { 16179 localStorage.setItem(_sessionKey, JSON.stringify(_logInfo)); 16180 } 16181 }, 16182 16183 16184 /** 16185 * init session list 16186 * @private 16187 */ 16188 _initSessionList = function () { 16189 _getAndSetNumberOfSessions(_logInfo); 16190 }, 16191 16192 /** 16193 * do the real store of log line 16194 * 16195 * @param line 16196 * @private 16197 */ 16198 _persistLine = function (line) { 16199 var key, logInfoStr; 16200 logInfoStr = localStorage.getItem(_sessionKey); 16201 if (logInfoStr === null) { 16202 return; 16203 } 16204 _logInfo = JSON.parse(logInfoStr); 16205 _logInfo.head = _logInfo.head + 1; 16206 key = _linePrefix + _logInfo.head; 16207 localStorage.setItem(key, line); 16208 //Save the size 16209 _logInfo.size = _logInfo.size + line.length; 16210 if (_logInfo.tail === 0) { 16211 _logInfo.tail = _logInfo.head; 16212 } 16213 16214 localStorage.setItem(_sessionKey, JSON.stringify(_logInfo)); 16215 _checkSizeAndPurge(); 16216 }, 16217 16218 /** 16219 * Insert a line into the localStorage. 16220 * 16221 * @param line line to be inserted 16222 * @private 16223 */ 16224 _insertLine = function (line) { 16225 //_myConsoleLog("_insertLine: [" + line + "]"); 16226 //Write the next line to localStorage 16227 try { 16228 //Persist the line 16229 _persistLine(line); 16230 } 16231 catch (err) { 16232 _myConsoleLog("error in _insertLine(), err="+err); 16233 //_insertLineHandleError(err); 16234 } 16235 }, 16236 16237 16238 /** 16239 * Clear the local storage 16240 * @private 16241 */ 16242 _clearLocalStorage = function() { 16243 localStorage.clear(); 16244 16245 }, 16246 16247 /** 16248 * Collect logs when onCollect called 16249 * 16250 * @param data 16251 * @private 16252 */ 16253 _collectMethod = function(data) { 16254 //Size of log should not exceed 1.5MB 16255 var info, maxLength = 1572864; 16256 16257 //add size buffer equal to the size of info to be added when publish 16258 info = navigator.userAgent + " "; 16259 info = escape(info); 16260 16261 //If log was empty previously, fade in buttons 16262 if (!_sendLogShown) { 16263 //call the fadeInSendLog() in Footer 16264 finesse.modules.Footer.sendLogAppear(); 16265 _sendLogShown = true; 16266 _logSize = info.length; 16267 } 16268 16269 //if local storage logging is enabled, then insert the log into local storage 16270 if (window.sessionStorage.getItem('enableLocalLog')==='true') { 16271 if (data) { 16272 if (data.length>0 && data.substring(0,1) === '\n') { 16273 _insertLine(data.substring(1)); 16274 } 16275 else { 16276 _insertLine(data); 16277 } 16278 } 16279 } 16280 16281 //escape all data to get accurate size (shindig will escape when it builds request) 16282 //escape 6 special chars for XML: &<>"'\n 16283 data = data.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/>/g, ">").replace(/</g, "<").replace(/\n/g, " "); 16284 data = escape(data+"\n"); 16285 16286 if (data.length < maxLength){ 16287 //make room for new data if log is exceeding max length 16288 while (_logSize + data.length > maxLength) { 16289 _logSize -= (_logArray.shift()).length; 16290 } 16291 } 16292 16293 //Else push the log into memory, increment the log size 16294 _logArray.push(data); 16295 16296 //inc the size accordingly 16297 _logSize+=data.length; 16298 16299 }; 16300 16301 return { 16302 16303 /** 16304 * @private 16305 * Initiate FinesseLogger. 16306 */ 16307 init: function () { 16308 ClientServices.subscribe("finesse.clientLogging.*", _collectMethod); 16309 _initLogging(); 16310 }, 16311 16312 /** 16313 * @private 16314 * Clear all items stored in localStorage. 16315 */ 16316 clear : function () { 16317 _clearLocalStorage(); 16318 }, 16319 16320 /** 16321 * @private 16322 * Initialize the local storage logging. 16323 */ 16324 initLocalLog: function () { 16325 _initLogging(); 16326 }, 16327 16328 /** 16329 * @private 16330 * Inserts a line into the localStorage. 16331 * @param line to insert 16332 */ 16333 localLog : function (line) { 16334 _insertLine(line); 16335 }, 16336 16337 /** 16338 * @ignore 16339 * Publish logs to server and clear the memory 16340 * 16341 * @param userObj 16342 * @param options 16343 * @param callBack 16344 */ 16345 publish: function(userObj, options, callBack) { 16346 // Avoid null references. 16347 options = options || {}; 16348 callBack = callBack || {}; 16349 16350 if (callBack.sending === "function") { 16351 callBack.sending(); 16352 } 16353 16354 //logs the basic version and machine info and escaped new line 16355 _logStr = navigator.userAgent + " "; 16356 16357 //join the logs to correct string format 16358 _logStr += unescape(_logArray.join("")); 16359 16360 //turning log string to JSON obj 16361 var logObj = { 16362 ClientLog: { 16363 logData : _logStr //_logStr 16364 } 16365 }, 16366 tmpOnAdd = (options.onAdd && typeof options.onAdd === "function")? options.onAdd : function(){}; 16367 /** @private */ 16368 options.onAdd = function(){ 16369 tmpOnAdd(); 16370 _logArray.length = 0; _logSize =0; 16371 _sendLogShown = false; 16372 }; 16373 //adding onLoad to the callbacks, this is the subscribe success case for the first time user subscribe to the client log node 16374 /** @private */ 16375 options.onLoad = function (clientLogObj) { 16376 clientLogObj.sendLogs(logObj,{ 16377 error: callBack.error 16378 }); 16379 }; 16380 16381 userObj.getClientLog(options); 16382 } 16383 }; 16384 }()); 16385 })); 16386 16387 /** 16388 * Contains a list of topics used for containerservices pubsub. 16389 * 16390 */ 16391 16392 /** 16393 * @class 16394 * Contains a list of topics with some utility functions. 16395 */ 16396 /** @private */ 16397 (function (factory) { 16398 16399 16400 // Define as an AMD module if possible 16401 if ( typeof define === 'function' && define.amd ) 16402 { 16403 define('containerservices/Topics',[], factory ); 16404 } 16405 16406 /* Define using browser globals otherwise 16407 * Prevent multiple instantiations if the script is loaded twice 16408 */ 16409 else 16410 { 16411 factory(); 16412 } 16413 16414 }(function () { 16415 16416 var Topics = (function () { 16417 16418 /** 16419 * The namespace prepended to all Finesse topics. 16420 */ 16421 this.namespace = "finesse.containerservices"; 16422 16423 /** 16424 * @private 16425 * Gets the full topic name with the ContainerServices namespace prepended. 16426 * @param {String} topic 16427 * The topic category. 16428 * @returns {String} 16429 * The full topic name with prepended namespace. 16430 */ 16431 var _getNSTopic = function (topic) { 16432 return this.namespace + "." + topic; 16433 }; 16434 16435 16436 16437 /** @scope finesse.containerservices.Topics */ 16438 return { 16439 /** 16440 * @private 16441 * request channel. */ 16442 REQUESTS: _getNSTopic("requests"), 16443 16444 /** 16445 * @private 16446 * reload gadget channel. */ 16447 RELOAD_GADGET: _getNSTopic("reloadGadget"), 16448 16449 /** 16450 * @private 16451 * Convert a Finesse REST URI to a OpenAjax compatible topic name. 16452 */ 16453 getTopic: function (restUri) { 16454 //The topic should not start with '/' else it will get replaced with 16455 //'.' which is invalid. 16456 //Thus, remove '/' if it is at the beginning of the string 16457 if (restUri.indexOf('/') === 0) { 16458 restUri = restUri.substr(1); 16459 } 16460 16461 //Replace every instance of "/" with ".". This is done to follow the 16462 //OpenAjaxHub topic name convention. 16463 return restUri.replace(/\//g, "."); 16464 } 16465 }; 16466 }()); 16467 16468 window.finesse = window.finesse || {}; 16469 window.finesse.containerservices = window.finesse.containerservices || {}; 16470 window.finesse.containerservices.Topics = Topics; 16471 16472 /** @namespace JavaScript class objects and methods to handle gadget container services.*/ 16473 finesse.containerservices = finesse.containerservices || {}; 16474 16475 return Topics; 16476 16477 })); 16478 16479 /** The following comment is to prevent jslint errors about 16480 * using variables before they are defined. 16481 */ 16482 /*global finesse*/ 16483 16484 /** 16485 * Per containerservices request, publish to the OpenAjax gadget pubsub infrastructure. 16486 * 16487 * @requires OpenAjax, finesse.containerservices.Topics 16488 */ 16489 16490 /** @private */ 16491 (function (factory) { 16492 16493 16494 // Define as an AMD module if possible 16495 if ( typeof define === 'function' && define.amd ) 16496 { 16497 define('containerservices/MasterPublisher',["utilities/Utilities", 16498 "containerservices/Topics"], factory ); 16499 } 16500 16501 /* Define using browser globals otherwise 16502 * Prevent multiple instantiations if the script is loaded twice 16503 */ 16504 else 16505 { 16506 factory(finesse.utilities.Utilities, finesse.containerservices.Topics); 16507 } 16508 16509 }(function (Utilities, Topics) { 16510 16511 var MasterPublisher = function () { 16512 16513 var 16514 16515 /** 16516 * Reference to the gadget pubsub Hub instance. 16517 * @private 16518 */ 16519 _hub = gadgets.Hub, 16520 16521 /** 16522 * Reference to the Topics class. 16523 * @private 16524 */ 16525 _topics = Topics, 16526 16527 /** 16528 * Reference to conversion utilities class. 16529 * @private 16530 */ 16531 _utils = Utilities, 16532 16533 /** 16534 * References to ClientServices logger methods 16535 * @private 16536 */ 16537 _logger = { 16538 log: finesse.clientservices.ClientServices.log 16539 }, 16540 16541 /** 16542 * The types of possible request types supported when listening to the 16543 * requests channel. Each request type could result in different operations. 16544 * @private 16545 */ 16546 _REQTYPES = { 16547 ACTIVETAB: "ActiveTabReq", 16548 SET_ACTIVETAB: "SetActiveTabReq", 16549 RELOAD_GADGET: "ReloadGadgetReq" 16550 }, 16551 16552 /** 16553 * Handles client requests made to the request topic. The type of the 16554 * request is described in the "type" property within the data payload. Each 16555 * type can result in a different operation. 16556 * @param {String} topic 16557 * The topic which data was published to. 16558 * @param {Object} data 16559 * The data containing requests information published by clients. 16560 * @param {String} data.type 16561 * The type of the request. Supported: "ActiveTabReq", "SetActiveTabReq", "ReloadGadgetReq" 16562 * @param {Object} data.data 16563 * May contain data relevant for the particular requests. 16564 * @param {String} [data.invokeID] 16565 * The ID used to identify the request with the response. The invoke ID 16566 * will be included in the data in the publish to the topic. It is the 16567 * responsibility of the client to correlate the published data to the 16568 * request made by using the invoke ID. 16569 * @private 16570 */ 16571 _clientRequestHandler = function (topic, data) { 16572 16573 //Ensure a valid data object with "type" and "data" properties. 16574 if (typeof data === "object" && 16575 typeof data.type === "string" && 16576 typeof data.data === "object") { 16577 switch (data.type) { 16578 case _REQTYPES.ACTIVETAB: 16579 _hub.publish("finesse.containerservices.activeTab", finesse.container.Tabs.getActiveTab()); 16580 break; 16581 case _REQTYPES.SET_ACTIVETAB: 16582 if (typeof data.data.id === "string") { 16583 _logger.log("Handling request to activate tab: " + data.data.id); 16584 if (!finesse.container.Tabs.activateTab(data.data.id)) { 16585 _logger.log("No tab found with id: " + data.data.id); 16586 } 16587 } 16588 break; 16589 case _REQTYPES.RELOAD_GADGET: 16590 _hub.publish("finesse.containerservices.reloadGadget", data.data); 16591 break; 16592 default: 16593 break; 16594 } 16595 } 16596 }; 16597 16598 (function () { 16599 16600 //Listen to a request channel to respond to any requests made by other 16601 //clients because the Master may have access to useful information. 16602 _hub.subscribe(_topics.REQUESTS, _clientRequestHandler); 16603 }()); 16604 16605 //BEGIN TEST CODE// 16606 /** 16607 * Test code added to expose private functions that are used by unit test 16608 * framework. This section of code is removed during the build process 16609 * before packaging production code. The [begin|end]TestSection are used 16610 * by the build to identify the section to strip. 16611 * @ignore 16612 */ 16613 this.beginTestSection = 0; 16614 16615 /** 16616 * @ignore 16617 */ 16618 this.getTestObject = function () { 16619 //Load mock dependencies. 16620 var _mock = new MockControl(); 16621 _hub = _mock.createMock(gadgets.Hub); 16622 16623 return { 16624 //Expose mock dependencies 16625 mock: _mock, 16626 hub: _hub, 16627 16628 //Expose internal private functions 16629 reqtypes: _REQTYPES, 16630 16631 clientRequestHandler: _clientRequestHandler 16632 16633 }; 16634 }; 16635 16636 16637 /** 16638 * @ignore 16639 */ 16640 this.endTestSection = 0; 16641 //END TEST CODE// 16642 }; 16643 16644 window.finesse = window.finesse || {}; 16645 window.finesse.containerservices = window.finesse.containerservices || {}; 16646 window.finesse.containerservices.MasterPublisher = MasterPublisher; 16647 16648 return MasterPublisher; 16649 })); 16650 16651 /** 16652 * JavaScript representation of the Finesse WorkflowActionEvent object. 16653 * 16654 * @requires finesse.FinesseBase 16655 */ 16656 16657 /** The following comment is to prevent jslint errors about 16658 * using variables before they are defined. 16659 */ 16660 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */ 16661 /** @private */ 16662 (function (factory) { 16663 16664 16665 // Define as an AMD module if possible 16666 if ( typeof define === 'function' && define.amd ) 16667 { 16668 define('containerservices/WorkflowActionEvent', ["FinesseBase"], factory ); 16669 } 16670 /* Define using browser globals otherwise 16671 * Prevent multiple instantiations if the script is loaded twice 16672 */ 16673 else 16674 { 16675 factory(finesse.FinesseBase); 16676 } 16677 }(function (FinesseBase) { 16678 var WorkflowActionEvent = FinesseBase.extend(/** @lends finesse.containerservices.WorkflowActionEvent.prototype */{ 16679 /** 16680 * Reference to the WorkflowActionEvent name 16681 * This will be set by setWorkflowActionEvent 16682 * @private 16683 */ 16684 _name: null, 16685 16686 /** 16687 * Reference to the WorkflowActionEvent type 16688 * This will be set by setWorkflowActionEvent 16689 * @private 16690 */ 16691 _type: null, 16692 16693 /** 16694 * Reference to the WorkflowActionEvent handledBy value 16695 * This will be set by setWorkflowActionEvent 16696 * @private 16697 */ 16698 _handledBy: null, 16699 16700 /** 16701 * Reference to the WorkflowActionEvent params array 16702 * This will be set by setWorkflowActionEvent 16703 * @private 16704 */ 16705 _params: [], 16706 16707 /** 16708 * Reference to the WorkflowActionEvent actionVariables array 16709 * This will be set by setWorkflowActionEvent 16710 * @private 16711 */ 16712 _actionVariables: [], 16713 16714 /** 16715 * @class 16716 * JavaScript representation of a WorkflowActionEvent object. 16717 * The WorkflowActionEvent object is delivered as the payload of 16718 * a WorkflowAction callback. This can be subscribed to by using 16719 * {@link finesse.containerservices.ContainerServices#addHandler} with a 16720 * topic of {@link finesse.containerservices.ContainerServices.Topics#WORKFLOW_ACTION_EVENT}. 16721 * Gadgets should key on events with a handleBy value of "OTHER". 16722 * 16723 * @constructs 16724 **/ 16725 init: function () { 16726 this._super(); 16727 }, 16728 16729 /** 16730 * Validate that the passed in object is a WorkflowActionEvent object 16731 * and sets the variables if it is 16732 * @param maybeWorkflowActionEvent A possible WorkflowActionEvent object to be evaluated and set if 16733 * it validates successfully. 16734 * @returns {Boolean} Whether it is valid or not. 16735 * @private 16736 */ 16737 setWorkflowActionEvent: function(maybeWorkflowActionEvent) { 16738 var returnValue; 16739 16740 if (maybeWorkflowActionEvent.hasOwnProperty("name") === true && 16741 maybeWorkflowActionEvent.hasOwnProperty("type") === true && 16742 maybeWorkflowActionEvent.hasOwnProperty("handledBy") === true && 16743 maybeWorkflowActionEvent.hasOwnProperty("params") === true && 16744 maybeWorkflowActionEvent.hasOwnProperty("actionVariables") === true) { 16745 this._name = maybeWorkflowActionEvent.name; 16746 this._type = maybeWorkflowActionEvent.type; 16747 this._handledBy = maybeWorkflowActionEvent.handledBy; 16748 this._params = maybeWorkflowActionEvent.params; 16749 this._actionVariables = maybeWorkflowActionEvent.actionVariables; 16750 returnValue = true; 16751 } else { 16752 returnValue = false; 16753 } 16754 16755 return returnValue; 16756 }, 16757 16758 /** 16759 * Getter for the WorkflowActionEvent name. 16760 * @returns {String} The name of the WorkflowAction. 16761 */ 16762 getName: function () { 16763 // escape nulls to empty string 16764 return this._name || ""; 16765 }, 16766 16767 /** 16768 * Getter for the WorkflowActionEvent type. 16769 * @returns {String} The type of the WorkflowAction (BROWSER_POP, HTTP_REQUEST). 16770 */ 16771 getType: function () { 16772 // escape nulls to empty string 16773 return this._type || ""; 16774 }, 16775 16776 /** 16777 * Getter for the WorkflowActionEvent handledBy value. Gadgets should look for 16778 * events with a handleBy of "OTHER". 16779 * @see finesse.containerservices.WorkflowActionEvent.HandledBy 16780 * @returns {String} The handledBy value of the WorkflowAction that is a value of {@link finesse.containerservices.WorkflowActionEvent.HandledBy}. 16781 */ 16782 getHandledBy: function () { 16783 // escape nulls to empty string 16784 return this._handledBy || ""; 16785 }, 16786 16787 16788 /** 16789 * Getter for the WorkflowActionEvent Params map. 16790 * @returns {Object} key = param name, value = Object{name, value, expandedValue} 16791 * BROWSER_POP<ul> 16792 * <li>windowName : Name of window to pop into, or blank to always open new window. 16793 * <li>path : URL to open.</ul> 16794 * HTTP_REQUEST<ul> 16795 * <li>method : "PUT" or "POST". 16796 * <li>location : "FINESSE" or "OTHER". 16797 * <li>contentType : MIME type of request body, if applicable, e.g. "text/plain". 16798 * <li>path : Request URL. 16799 * <li>body : Request content for POST requests.</ul> 16800 */ 16801 getParams: function () { 16802 var map = {}, 16803 params = this._params, 16804 i, 16805 param; 16806 16807 if (params === null || params.length === 0) { 16808 return map; 16809 } 16810 16811 for (i = 0; i < params.length; i += 1) { 16812 param = params[i]; 16813 // escape nulls to empty string 16814 param.name = param.name || ""; 16815 param.value = param.value || ""; 16816 param.expandedValue = param.expandedValue || ""; 16817 map[param.name] = param; 16818 } 16819 16820 return map; 16821 }, 16822 16823 /** 16824 * Getter for the WorkflowActionEvent ActionVariables map 16825 * @returns {Object} key = action variable name, value = Object{name, type, node, testValue, actualValue} 16826 */ 16827 getActionVariables: function() { 16828 var map = {}, 16829 actionVariables = this._actionVariables, 16830 i, 16831 actionVariable; 16832 16833 if (actionVariables === null || actionVariables.length === 0) { 16834 return map; 16835 } 16836 16837 for (i = 0; i < actionVariables.length; i += 1) { 16838 actionVariable = actionVariables[i]; 16839 // escape nulls to empty string 16840 actionVariable.name = actionVariable.name || ""; 16841 actionVariable.type = actionVariable.type || ""; 16842 actionVariable.node = actionVariable.node || ""; 16843 actionVariable.testValue = actionVariable.testValue || ""; 16844 actionVariable.actualValue = actionVariable.actualValue || ""; 16845 map[actionVariable.name] = actionVariable; 16846 } 16847 16848 return map; 16849 } 16850 }); 16851 16852 16853 WorkflowActionEvent.HandledBy = /** @lends finesse.containerservices.WorkflowActionEvent.HandledBy.prototype */ { 16854 /** 16855 * This specifies that Finesse will handle this WorkflowActionEvent. A 3rd Party can do additional processing 16856 * with the action, but first and foremost Finesse will handle this WorkflowAction. 16857 */ 16858 FINESSE: "FINESSE", 16859 16860 /** 16861 * This specifies that a 3rd Party will handle this WorkflowActionEvent. Finesse's Workflow Engine Executor will 16862 * ignore this action and expects Gadget Developers to take action. 16863 */ 16864 OTHER: "OTHER", 16865 16866 /** 16867 * @class This is the set of possible HandledBy values used for WorkflowActionEvent from ContainerServices. This 16868 * is provided from the {@link finesse.containerservices.WorkflowActionEvent#getHandledBy} method. 16869 * @constructs 16870 */ 16871 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 16872 }; 16873 16874 window.finesse = window.finesse || {}; 16875 window.finesse.containerservices = window.finesse.containerservices || {}; 16876 window.finesse.containerservices.WorkflowActionEvent = WorkflowActionEvent; 16877 16878 return WorkflowActionEvent; 16879 })); 16880 16881 /** 16882 * JavaScript representation of the Finesse TimerTickEvent 16883 * 16884 * @requires finesse.FinesseBase 16885 */ 16886 16887 /** The following comment is to prevent jslint errors about 16888 * using variables before they are defined. 16889 */ 16890 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */ 16891 /** @private */ 16892 (function (factory) { 16893 16894 16895 // Define as an AMD module if possible 16896 if ( typeof define === 'function' && define.amd ) 16897 { 16898 define('containerservices/TimerTickEvent', ["FinesseBase"], factory ); 16899 } 16900 /* Define using browser globals otherwise 16901 * Prevent multiple instantiations if the script is loaded twice 16902 */ 16903 else 16904 { 16905 factory(finesse.FinesseBase); 16906 } 16907 }(function (FinesseBase) { 16908 var TimerTickEvent = FinesseBase.extend(/** @lends finesse.containerservices.TimerTickEvent.prototype */{ 16909 /** 16910 * date the TimerTickEvent was queued 16911 * @private 16912 */ 16913 _dateQueued: null, 16914 16915 /** 16916 * the frequency of the timer tick (in miiliseconds) 16917 * @private 16918 */ 16919 _tickFrequency: 1000, 16920 16921 /** 16922 * @class 16923 * JavaScript representation of a TimerTickEvent object. 16924 * The TimerTickEvent object is delivered as the payload of 16925 * a TimerTickEvent callback. This can be subscribed to by using 16926 * {@link finesse.containerservices.ContainerServices#addHandler} with a 16927 * topic of {@link finesse.containerservices.ContainerServices.Topics#TIMER_TICK_EVENT}. 16928 * 16929 * @constructs 16930 **/ 16931 init: function (tickFrequency, dateQueued) { 16932 this._super(); 16933 16934 this._tickFrequency = tickFrequency; 16935 this._dateQueued = dateQueued; 16936 }, 16937 16938 /** 16939 * Get the "tickFrequency" field 16940 * @param {int} which is the "TickFrequency" field 16941 * @private 16942 */ 16943 getTickFrequency: function () { 16944 return this._tickFrequency; 16945 }, 16946 16947 /** 16948 * Getter for the TimerTickEvent "DateQueued" field. 16949 * @returns {Date} which is a Date object when the TimerTickEvent was queued 16950 */ 16951 getDateQueued: function () { 16952 return this._dateQueued; 16953 } 16954 16955 }); 16956 16957 window.finesse = window.finesse || {}; 16958 window.finesse.containerservices = window.finesse.containerservices || {}; 16959 window.finesse.containerservices.TimerTickEvent = TimerTickEvent; 16960 16961 return TimerTickEvent; 16962 })); 16963 16964 /** 16965 * JavaScript representation of the Finesse GadgetViewChangedEvent object. 16966 * 16967 * @requires finesse.FinesseBase 16968 */ 16969 16970 /** The following comment is to prevent jslint errors about 16971 * using variables before they are defined. 16972 */ 16973 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */ 16974 /** @private */ 16975 (function (factory) { 16976 16977 16978 // Define as an AMD module if possible 16979 if ( typeof define === 'function' && define.amd ) 16980 { 16981 define('containerservices/GadgetViewChangedEvent', ["FinesseBase"], factory ); 16982 } 16983 /* Define using browser globals otherwise 16984 * Prevent multiple instantiations if the script is loaded twice 16985 */ 16986 else 16987 { 16988 factory(finesse.FinesseBase); 16989 } 16990 }(function (FinesseBase) { 16991 var GadgetViewChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.GadgetViewChangedEvent.prototype */{ 16992 /** 16993 * Reference to the gadget id 16994 * @private 16995 */ 16996 _gadgetId: null, 16997 16998 /** 16999 * Reference to the tab id 17000 * @private 17001 */ 17002 _tabId: null, 17003 17004 /** 17005 * Reference to the maxAvailableHeight 17006 * @private 17007 */ 17008 _maxAvailableHeight: null, 17009 17010 /** 17011 * Reference to the view 17012 * E.g. 'default' or 'canvas' 17013 * @private 17014 */ 17015 _view: null, 17016 17017 /** 17018 * @class 17019 * JavaScript representation of a GadgetViewChangedEvent object. 17020 * The GadgetViewChangedEvent object is delivered as the payload of 17021 * a GadgetViewChangedEvent callback. This can be subscribed to by using 17022 * {@link finesse.containerservices.ContainerServices#addHandler} with a 17023 * topic of {@link finesse.containerservices.ContainerServices.Topics#GADGET_VIEW_CHANGED_EVENT}. 17024 * 17025 * @constructs 17026 **/ 17027 init: function (gadgetId, tabId, maxAvailableHeight, view) { 17028 this._super(); 17029 17030 this._gadgetId = gadgetId; 17031 this._tabId = tabId; 17032 this._maxAvailableHeight = maxAvailableHeight; 17033 this._view = view; 17034 }, 17035 17036 /** 17037 * Getter for the gadget id. 17038 * @returns {String} The identifier for the gadget changing view. 17039 */ 17040 getGadgetId: function () { 17041 // escape nulls to empty string 17042 return this._gadgetId || ""; 17043 }, 17044 17045 /** 17046 * Getter for the maximum available height. 17047 * @returns {String} The maximum available height for the gadget's view. 17048 */ 17049 getMaxAvailableHeight: function () { 17050 // escape nulls to empty string 17051 return this._maxAvailableHeight || ""; 17052 }, 17053 17054 /** 17055 * Getter for the tab id. 17056 * @returns {String} The identifier for the tab where the gadget changing view resides. 17057 */ 17058 getTabId: function () { 17059 // escape nulls to empty string 17060 return this._tabId || ""; 17061 }, 17062 17063 /** 17064 * Getter for the view. 17065 * @returns {String} The view type the gadget is changing to. 17066 */ 17067 getView: function () { 17068 // escape nulls to empty string 17069 return this._view || ""; 17070 } 17071 }); 17072 17073 window.finesse = window.finesse || {}; 17074 window.finesse.containerservices = window.finesse.containerservices || {}; 17075 window.finesse.containerservices.GadgetViewChangedEvent = GadgetViewChangedEvent; 17076 17077 return GadgetViewChangedEvent; 17078 })); 17079 17080 /** 17081 * JavaScript representation of the Finesse MaxAvailableHeightChangedEvent object. 17082 * 17083 * @requires finesse.FinesseBase 17084 */ 17085 17086 /** The following comment is to prevent jslint errors about 17087 * using variables before they are defined. 17088 */ 17089 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */ 17090 /** @private */ 17091 (function (factory) { 17092 17093 17094 // Define as an AMD module if possible 17095 if ( typeof define === 'function' && define.amd ) 17096 { 17097 define('containerservices/MaxAvailableHeightChangedEvent', ["FinesseBase"], factory ); 17098 } 17099 /* Define using browser globals otherwise 17100 * Prevent multiple instantiations if the script is loaded twice 17101 */ 17102 else 17103 { 17104 factory(finesse.FinesseBase); 17105 } 17106 }(function (FinesseBase) { 17107 var MaxAvailableHeightChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.MaxAvailableHeightChangedEvent.prototype */{ 17108 17109 /** 17110 * Reference to the maxAvailableHeight 17111 * @private 17112 */ 17113 _maxAvailableHeight: null, 17114 17115 /** 17116 * @class 17117 * JavaScript representation of a MaxAvailableHeightChangedEvent object. 17118 * The MaxAvailableHeightChangedEvent object is delivered as the payload of 17119 * a MaxAvailableHeightChangedEvent callback. This can be subscribed to by using 17120 * {@link finesse.containerservices.ContainerServices#addHandler} with a 17121 * topic of {@link finesse.containerservices.ContainerServices.Topics#MAX_AVAILABLE_HEIGHT_CHANGED_EVENT}. 17122 * 17123 * @constructs 17124 **/ 17125 init: function (maxAvailableHeight) { 17126 this._super(); 17127 17128 this._maxAvailableHeight = maxAvailableHeight; 17129 }, 17130 17131 /** 17132 * Getter for the maximum available height. 17133 * @returns {String} The maximum available height for a gadget in canvas view 17134 */ 17135 getMaxAvailableHeight: function () { 17136 // escape nulls to empty string 17137 return this._maxAvailableHeight || ""; 17138 } 17139 }); 17140 17141 window.finesse = window.finesse || {}; 17142 window.finesse.containerservices = window.finesse.containerservices || {}; 17143 window.finesse.containerservices.MaxAvailableHeightChangedEvent = MaxAvailableHeightChangedEvent; 17144 17145 return MaxAvailableHeightChangedEvent; 17146 })); 17147 17148 /** 17149 * Exposes a set of API wrappers that will hide the dirty work of 17150 * constructing Finesse API requests and consuming Finesse events. 17151 * 17152 * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities 17153 */ 17154 17155 /** The following comment is to prevent jslint errors about using variables before they are defined. */ 17156 /*global window:true, gadgets:true, publisher:true, define:true, finesse:true, _tabTracker:true, _workflowActionEventTracker:true, _masterReloader:true, frameElement:true, $:true, parent:true, MockControl:true, _getNotifierReference:true, _gadgetViewChanged:true, _maxAvailableHeightChanged:true */ 17157 /*jslint nomen: true, unparam: true, sloppy: true, white: true */ 17158 /** @private */ 17159 (function (factory) { 17160 17161 17162 // Define as an AMD module if possible 17163 if ( typeof define === 'function' && define.amd ) 17164 { 17165 define('containerservices/ContainerServices',["utilities/Utilities", 17166 "restservices/Notifier", 17167 "containerservices/Topics", 17168 "containerservices/MasterPublisher", 17169 "containerservices/WorkflowActionEvent", 17170 "containerservices/TimerTickEvent", 17171 "containerservices/GadgetViewChangedEvent", 17172 "containerservices/MaxAvailableHeightChangedEvent"], factory ); 17173 } 17174 17175 /* Define using browser globals otherwise 17176 * Prevent multiple instantiations if the script is loaded twice 17177 */ 17178 else 17179 { 17180 factory(finesse.utilities.Utilities, finesse.restservices.Notifier, finesse.containerservices.Topics, finesse.containerservices.MasterPublisher, finesse.containerservices.WorkflowActionEvent); 17181 } 17182 17183 }(function (Utilities, Notifier, Topics, MasterPublisher, WorkflowActionEvent) { 17184 17185 var ContainerServices = ( function () { /** @lends finesse.containerservices.ContainerServices.prototype */ 17186 17187 var 17188 17189 /** 17190 * Shortcut reference to the Utilities singleton 17191 * This will be set by init() 17192 * @private 17193 */ 17194 _util, 17195 17196 /** 17197 * Shortcut reference to the gadget pubsub Hub instance. 17198 * This will be set by init() 17199 * @private 17200 */ 17201 _hub, 17202 17203 /** 17204 * Boolean whether this instance is master or not 17205 * @private 17206 */ 17207 _master = false, 17208 17209 /** 17210 * Whether the Client Services have been initiated yet. 17211 * @private 17212 */ 17213 _inited = false, 17214 17215 /** 17216 * References to ClientServices logger methods 17217 * @private 17218 */ 17219 _logger = { 17220 log: finesse.clientservices.ClientServices.log 17221 }, 17222 17223 /** 17224 * Stores the list of subscription IDs for all subscriptions so that it 17225 * could be retrieve for unsubscriptions. 17226 * @private 17227 */ 17228 _subscriptionID = {}, 17229 17230 /** 17231 * Reference to the gadget's parent container 17232 * @private 17233 */ 17234 _container, 17235 17236 /** 17237 * Reference to the MasterPublisher 17238 * @private 17239 */ 17240 _publisher, 17241 17242 /** 17243 * Object that will contain the Notifiers 17244 * @private 17245 */ 17246 _notifiers = {}, 17247 17248 /** 17249 * Reference to the tabId that is associated with the gadget 17250 * @private 17251 */ 17252 _myTab = null, 17253 17254 /** 17255 * Reference to the visibility of current gadget 17256 * @private 17257 */ 17258 _visible = false, 17259 17260 /** 17261 * Shortcut reference to the Topics class. 17262 * This will be set by init() 17263 * @private 17264 */ 17265 _topics, 17266 17267 /** 17268 * Associates a topic name with the private handler function. 17269 * Adding a new topic requires that you add this association here 17270 * in to keep addHandler generic. 17271 * @param {String} topic : Specifies the callback to retrieve 17272 * @return {Function} The callback function associated with the topic param. 17273 * @private 17274 */ 17275 _topicCallback = function (topic) { 17276 var callback, notifier; 17277 switch (topic) 17278 { 17279 case finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB: 17280 callback = _tabTracker; 17281 break; 17282 case finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT: 17283 callback = _workflowActionEventTracker; 17284 break; 17285 case finesse.containerservices.ContainerServices.Topics.RELOAD_GADGET_EVENT: 17286 callback = _masterReloader; 17287 break; 17288 case finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT: 17289 callback = _gadgetViewChanged; 17290 break; 17291 case finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT: 17292 callback = _maxAvailableHeightChanged; 17293 break; 17294 default: 17295 callback = function (param) { 17296 var data = null; 17297 17298 notifier = _getNotifierReference(topic); 17299 17300 if (arguments.length === 1) { 17301 data = param; 17302 } else { 17303 data = arguments; 17304 } 17305 notifier.notifyListeners(data); 17306 }; 17307 } 17308 return callback; 17309 }, 17310 17311 /** 17312 * Ensure that ClientServices have been inited. 17313 * @private 17314 */ 17315 _isInited = function () { 17316 if (!_inited) { 17317 throw new Error("ContainerServices needs to be inited."); 17318 } 17319 }, 17320 17321 /** 17322 * Retrieves a Notifier reference to a particular topic, and creates one if it doesn't exist. 17323 * @param {String} topic : Specifies the notifier to retrieve 17324 * @return {Notifier} The notifier object. 17325 * @private 17326 */ 17327 _getNotifierReference = function (topic) { 17328 if (!_notifiers.hasOwnProperty(topic)) 17329 { 17330 _notifiers[topic] = new Notifier(); 17331 } 17332 17333 return _notifiers[topic]; 17334 }, 17335 17336 /** 17337 * Utility function to make a subscription to a particular topic. Only one 17338 * callback function is registered to a particular topic at any time. 17339 * @param {String} topic 17340 * The full topic name. The topic name should follow the OpenAjax 17341 * convention using dot notation (ex: finesse.api.User.1000). 17342 * @param {Function} callback 17343 * The function that should be invoked with the data when an event 17344 * is delivered to the specific topic. 17345 * @returns {Boolean} 17346 * True if the subscription was made successfully and the callback was 17347 * been registered. False if the subscription already exist, the 17348 * callback was not overwritten. 17349 * @private 17350 */ 17351 _subscribe = function (topic, callback) { 17352 _isInited(); 17353 17354 //Ensure that the same subscription isn't made twice. 17355 if (!_subscriptionID[topic]) { 17356 //Store the subscription ID using the topic name as the key. 17357 _subscriptionID[topic] = _hub.subscribe(topic, 17358 //Invoke the callback just with the data object. 17359 function (topic, data) { 17360 callback(data); 17361 }); 17362 return true; 17363 } 17364 return false; 17365 }, 17366 17367 /** 17368 * Unsubscribe from a particular topic. 17369 * @param {String} topic : The full topic name. 17370 * @private 17371 */ 17372 _unsubscribe = function (topic) { 17373 _isInited(); 17374 17375 //Unsubscribe from the topic using the subscription ID recorded when 17376 //the subscription was made, then delete the ID from data structure. 17377 _hub.unsubscribe(_subscriptionID[topic]); 17378 delete _subscriptionID[topic]; 17379 }, 17380 17381 /** 17382 * Get my tab id. 17383 * @returns {String} tabid : The tabid of this container/gadget. 17384 * @private 17385 */ 17386 _getMyTab = function () { 17387 if (_myTab === null) 17388 { 17389 try { 17390 _myTab = $(frameElement).closest("div.tab-panel").attr("id").replace("panel_", ""); 17391 } catch (err) { 17392 _logger.log("Error accessing current tab: " + err.message); 17393 _myTab = null; 17394 } 17395 } 17396 return _myTab; 17397 }, 17398 17399 /** 17400 * Callback function that is called when an activeTab message is posted to the Hub. 17401 * Notifies listener functions if this tab is the one that was just made active. 17402 * @param {String} tabId : The tabId which was just made visible. 17403 * @private 17404 */ 17405 _tabTracker = function(tabId) { 17406 if (tabId === _getMyTab()) { 17407 if(!_visible) { 17408 _visible = true; 17409 _notifiers[finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB].notifyListeners(this); 17410 } 17411 } else { 17412 _visible = false; 17413 } 17414 }, 17415 17416 /** 17417 * Make a request to set a particular tab active. This 17418 * method should be called after {@link finesse.containerservices.ContainerServices#addHandler} 17419 * to ensure the gadget gets properly initialized. 17420 * @param {String} tabId 17421 * The tabId (not the label text) of the tab to make active. If the id is invalid, no action will occur. 17422 * @private 17423 */ 17424 _activateTab = function ( tabId ) { 17425 _logger.log("Sending request to activate tab: " + tabId); 17426 if(_hub){ 17427 var data = { 17428 type: "SetActiveTabReq", 17429 data: { id: tabId }, 17430 invokeID: (new Date()).getTime() 17431 }; 17432 _hub.publish(_topics.REQUESTS, data); 17433 } else { 17434 throw new Error("Hub is not defined."); 17435 } 17436 17437 }, 17438 17439 /** 17440 * Callback function that is called when a gadget view changed message is posted to the Hub. 17441 * @private 17442 */ 17443 _gadgetViewChanged = function (data) { 17444 if (data) { 17445 var gadgetViewChangedEvent = new finesse.containerservices.GadgetViewChangedEvent( 17446 data.gadgetId, 17447 data.tabId, 17448 data.maxAvailableHeight, 17449 data.view); 17450 17451 _notifiers[finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT].notifyListeners(gadgetViewChangedEvent); 17452 } 17453 }, 17454 17455 /** 17456 * Callback function that is called when a max available height changed message is posted to the Hub. 17457 * @private 17458 */ 17459 _maxAvailableHeightChanged = function (data) { 17460 if (data) { 17461 var maxAvailableHeightChangedEvent = new finesse.containerservices.MaxAvailableHeightChangedEvent( 17462 data.maxAvailableHeight); 17463 17464 _notifiers[finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT].notifyListeners(maxAvailableHeightChangedEvent); 17465 } 17466 }, 17467 17468 /** 17469 * Callback function that is called when a workflowActionEvent message is posted to the Hub. 17470 * Notifies listener functions if the posted object can be converted to a proper WorkflowActionEvent object. 17471 * @param {String} workflowActionEvent : The workflowActionEvent that was posted to the Hub 17472 * @private 17473 */ 17474 _workflowActionEventTracker = function(workflowActionEvent) { 17475 var vWorkflowActionEvent = new finesse.containerservices.WorkflowActionEvent(); 17476 17477 if (vWorkflowActionEvent.setWorkflowActionEvent(workflowActionEvent)) { 17478 _notifiers[finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT].notifyListeners(vWorkflowActionEvent); 17479 } 17480 // else 17481 // { 17482 //?console.log("Error in ContainerServices : _workflowActionEventTracker - could not map published HUB object to WorkflowActionEvent"); 17483 // } 17484 17485 }, 17486 17487 /** 17488 * Callback function that is called when a reloadGadget event message is posted to the Hub. 17489 * 17490 * Grabs the id of the gadget we want to reload from the data and reload it! 17491 * 17492 * @param {String} topic 17493 * which topic the event came on (unused) 17494 * @param {Object} data 17495 * the data published with the event 17496 * @private 17497 */ 17498 _masterReloader = function (topic, data) { 17499 var gadgetId = data.gadgetId; 17500 if (gadgetId) { 17501 _container.reloadGadget(gadgetId); 17502 } 17503 }, 17504 17505 /** 17506 * Pulls the gadget id from the url parameters 17507 * @return {String} id of the gadget 17508 * @private 17509 */ 17510 _findMyGadgetId = function () { 17511 if (gadgets && gadgets.util && gadgets.util.getUrlParameters()) { 17512 return gadgets.util.getUrlParameters().mid; 17513 } 17514 }; 17515 17516 return { 17517 /** 17518 * @class 17519 * This class provides container-level services for gadget developers, exposing container events by 17520 * calling a set of exposed functions. Gadgets can utilize the container dialogs and 17521 * event handling (add/remove). 17522 * @example 17523 * containerServices = finesse.containerservices.ContainerServices.init(); 17524 * containerServices.addHandler( 17525 * finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, 17526 * function() { 17527 * clientLogs.log("Gadget is now visible"); // log to Finesse logger 17528 * // automatically adjust the height of the gadget to show the html 17529 * gadgets.window.adjustHeight(); 17530 * }); 17531 * containerServices.makeActiveTabReq(); 17532 * 17533 * @constructs 17534 */ 17535 _fakeConstuctor: function () { 17536 /* This is here so we can document init() as a method rather than as a constructor. */ 17537 }, 17538 17539 /** 17540 * Initialize ContainerServices for use in gadget. 17541 * @param {Boolean} [master=false] Do not use this parameter from your gadget. 17542 * @returns ContainerServices instance. 17543 */ 17544 init: function (master) { 17545 if (!_inited) { 17546 _inited = true; 17547 // Set shortcuts 17548 _util = Utilities; 17549 17550 //init the hub only when it's available 17551 if(gadgets.Hub) { 17552 _hub = gadgets.Hub; 17553 } 17554 17555 if(Topics) { 17556 _topics = Topics; 17557 } 17558 17559 if (master) { 17560 _master = true; 17561 _container = finesse.container.Container; 17562 _publisher = new MasterPublisher(); 17563 17564 // subscribe for reloading gadget events 17565 // we only want the master ContainerServices handling these events 17566 _hub.subscribe(_topics.RELOAD_GADGET, _topicCallback(_topics.RELOAD_GADGET)); 17567 } else { 17568 _container = parent.finesse.container.Container; 17569 } 17570 } 17571 17572 this.makeActiveTabReq(); 17573 17574 //Return the CS object for object chaining. 17575 return this; 17576 }, 17577 17578 /** 17579 * Shows the jQuery UI Dialog with the specified parameters. The following are the 17580 * default parameters: <ul> 17581 * <li> Title of "Cisco Finesse".</li> 17582 * <li>Message of "A generic error has occured".</li> 17583 * <li>The only button, "Ok", closes the dialog.</li> 17584 * <li>Modal (blocks other dialogs).</li> 17585 * <li>Not draggable.</li> 17586 * <li>Fixed size.</li></ul> 17587 * @param {Object} options 17588 * An object containing additional options for the dialog. 17589 * @param {String/Boolean} options.title 17590 * Title to use. undefined defaults to "Cisco Finesse". false to hide 17591 * @param {Function} options.close 17592 * A function to invoke when the dialog is closed. 17593 * @param {String} options.message 17594 * The message to display in the dialog. 17595 * Defaults to "A generic error has occurred." 17596 * @param {Boolean} options.isBlocking 17597 * Flag indicating whether this dialog will block other dialogs from being shown (Modal). 17598 * @returns {jQuery} JQuery wrapped object of the dialog DOM element. 17599 * @see finesse.containerservices.ContainerServices#hideDialog 17600 */ 17601 showDialog: function(options) { 17602 if ((_container.showDialog !== undefined) && (_container.showDialog !== this.showDialog)) { 17603 return _container.showDialog(options); 17604 } 17605 }, 17606 17607 /** 17608 * Hides the jQuery UI Dialog. 17609 * @returns {jQuery} jQuery wrapped object of the dialog DOM element 17610 * @see finesse.containerservices.ContainerServices#showDialog 17611 */ 17612 hideDialog: function() { 17613 if ((_container.hideDialog !== undefined) && (_container.hideDialog !== this.hideDialog)) { 17614 return _container.hideDialog(); 17615 } 17616 }, 17617 17618 /** 17619 * Reloads the current gadget. 17620 * For use from within a gadget only. 17621 */ 17622 reloadMyGadget: function () { 17623 var topic, gadgetId, data; 17624 17625 if (!_master) { 17626 // first unsubscribe this gadget from all topics on the hub 17627 for (topic in _notifiers) { 17628 if (_notifiers.hasOwnProperty(topic)) { 17629 _unsubscribe(topic); 17630 delete _notifiers[topic]; 17631 } 17632 } 17633 17634 // send an asynch request to the hub to tell the master container 17635 // services that we want to refresh this gadget 17636 gadgetId = _findMyGadgetId(); 17637 data = { 17638 type: "ReloadGadgetReq", 17639 data: {gadgetId: gadgetId}, 17640 invokeID: (new Date()).getTime() 17641 }; 17642 _hub.publish(_topics.REQUESTS, data); 17643 } 17644 }, 17645 17646 /** 17647 * Updates the url for this gadget and then reload it. 17648 * 17649 * This allows the gadget to be reloaded from a different location 17650 * than what is uploaded to the current server. For example, this 17651 * would be useful for 3rd party gadgets to implement their own failover 17652 * mechanisms. 17653 * 17654 * For use from within a gadget only. 17655 * 17656 * @param {String} url 17657 * url from which to reload gadget 17658 */ 17659 reloadMyGadgetFromUrl: function (url) { 17660 if (!_master) { 17661 var gadgetId = _findMyGadgetId(); 17662 17663 // update the url in the container 17664 _container.modifyGadgetUrl(gadgetId, url); 17665 17666 // reload it 17667 this.reloadMyGadget(); 17668 } 17669 }, 17670 17671 /** 17672 * Adds a handler for one of the supported topics provided by ContainerServices. The callbacks provided 17673 * will be invoked when that topic is notified. 17674 * @param {String} topic 17675 * The Hub topic to which we are listening. 17676 * @param {Function} callback 17677 * The callback function to invoke. 17678 * @see finesse.containerservices.ContainerServices.Topics 17679 * @see finesse.containerservices.ContainerServices#removeHandler 17680 */ 17681 addHandler: function (topic, callback) { 17682 _isInited(); 17683 var notifier = null; 17684 17685 try { 17686 // For backwards compatibility... 17687 if (topic === "tabVisible") { 17688 if (window.console && typeof window.console.log === "function") { 17689 window.console.log("WARNING - Using tabVisible as topic. This is deprecated. Use finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB now!"); 17690 } 17691 17692 topic = finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB; 17693 } 17694 17695 // Add the callback to the notifier. 17696 _util.validateHandler(callback); 17697 17698 notifier = _getNotifierReference(topic); 17699 17700 notifier.addListener(callback); 17701 17702 // Subscribe to the topic. _subscribe ensures that a topic is only subscribed to once, 17703 // so attempt to subscribe each time a handler is added. This ensures that a topic is subscribed 17704 // to only when necessary. 17705 _subscribe(topic, _topicCallback(topic)); 17706 17707 } catch (err) { 17708 throw new Error("addHandler(): " + err); 17709 } 17710 }, 17711 17712 /** 17713 * Removes a previously-added handler for one of the supported topics. 17714 * @param {String} topic 17715 * The Hub topic from which we are removing the callback. 17716 * @param {Function} callback 17717 * The name of the callback function to remove. 17718 * @see finesse.containerservices.ContainerServices.Topics 17719 * @see finesse.containerservices.ContainerServices#addHandler 17720 */ 17721 removeHandler: function(topic, callback) { 17722 var notifier = null; 17723 17724 try { 17725 _util.validateHandler(callback); 17726 17727 notifier = _getNotifierReference(topic); 17728 17729 notifier.removeListener(callback); 17730 } catch (err) { 17731 throw new Error("removeHandler(): " + err); 17732 } 17733 }, 17734 17735 /** 17736 * Returns the visibility of current gadget. Note that this 17737 * will not be set until after the initialization of the gadget. 17738 * @return {Boolean} The visibility of current gadget. 17739 */ 17740 tabVisible: function(){ 17741 return _visible; 17742 }, 17743 17744 /** 17745 * Make a request to check the current tab. The 17746 * activeTab event will be invoked if on the active tab. This 17747 * method should be called after {@link finesse.containerservices.ContainerServices#addHandler} 17748 * to ensure the gadget gets properly initialized. 17749 */ 17750 makeActiveTabReq : function () { 17751 if(_hub){ 17752 var data = { 17753 type: "ActiveTabReq", 17754 data: {}, 17755 invokeID: (new Date()).getTime() 17756 }; 17757 _hub.publish(_topics.REQUESTS, data); 17758 } else { 17759 throw new Error("Hub is not defined."); 17760 } 17761 17762 }, 17763 17764 /** 17765 * Make a request to set a particular tab active. This 17766 * method should be called after {@link finesse.containerservices.ContainerServices#addHandler} 17767 * to ensure the gadget gets properly initialized. 17768 * @param {String} tabId 17769 * The tabId (not the label text) of the tab to make active. If the id is invalid, no action will occur. 17770 */ 17771 activateTab : function (tabId) { 17772 _activateTab(tabId); 17773 }, 17774 17775 /** 17776 * Make a request to set this container's tab active. This 17777 * method should be called after {@link finesse.containerservices.ContainerServices#addHandler} 17778 * to ensure the gadget gets properly initialized. 17779 */ 17780 activateMyTab : function () { 17781 _activateTab( _getMyTab() ); 17782 }, 17783 17784 /** 17785 * Get the tabId of my container/gadget. 17786 * @returns {String} tabid : The tabid of this container/gadget. 17787 */ 17788 getMyTabId : function () { 17789 return _getMyTab(); 17790 }, 17791 17792 /** 17793 * Gets the id of the gadget. 17794 * @returns {number} the id of the gadget 17795 */ 17796 getMyGadgetId : function () { 17797 return _findMyGadgetId(); 17798 }, 17799 17800 //BEGIN TEST CODE// 17801 /** 17802 * Test code added to expose private functions that are used by unit test 17803 * framework. This section of code is removed during the build process 17804 * before packaging production code. The [begin|end]TestSection are used 17805 * by the build to identify the section to strip. 17806 * @ignore 17807 */ 17808 beginTestSection : 0, 17809 17810 /** 17811 * @ignore 17812 */ 17813 getTestObject: function () { 17814 //Load mock dependencies. 17815 var _mock = new MockControl(); 17816 _util = _mock.createMock(Utilities); 17817 _hub = _mock.createMock(gadgets.Hub); 17818 _inited = true; 17819 return { 17820 //Expose mock dependencies 17821 mock: _mock, 17822 hub: _hub, 17823 util: _util, 17824 addHandler: this.addHandler, 17825 removeHandler: this.removeHandler 17826 }; 17827 }, 17828 17829 /** 17830 * @ignore 17831 */ 17832 endTestSection: 0 17833 //END TEST CODE// 17834 }; 17835 }()); 17836 17837 ContainerServices.Topics = /** @lends finesse.containerservices.ContainerServices.Topics.prototype */ { 17838 /** 17839 * Topic for subscribing to be notified when the active tab changes. 17840 * The provided callback will be invoked when the tab that the gadget 17841 * that subscribes with this becomes active. To ensure code is called 17842 * when the gadget is already on the active tab use the 17843 * {@link finesse.containerservices.ContainerServices#makeActiveTabReq} 17844 * method. 17845 */ 17846 ACTIVE_TAB: "finesse.containerservices.activeTab", 17847 17848 /** 17849 * Topic for WorkflowAction events traffic. 17850 * The provided callback will be invoked when a WorkflowAction needs 17851 * to be handled. The callback will be passed a {@link finesse.containerservices.WorkflowActionEvent} 17852 * that can be used to interrogate the WorkflowAction and determine to use or not. 17853 */ 17854 WORKFLOW_ACTION_EVENT: "finesse.containerservices.workflowActionEvent", 17855 17856 /** 17857 * Topic for Timer Tick event. 17858 * The provided callback will be invoked when this event is fired. 17859 * The callback will be passed a {@link finesse.containerservices.TimerTickEvent}. 17860 */ 17861 TIMER_TICK_EVENT : "finesse.containerservices.timerTickEvent", 17862 17863 /** 17864 * Topic for Reload Gadget events traffic. 17865 * Only the master ContainerServices instance will handle this event. 17866 */ 17867 RELOAD_GADGET_EVENT: "finesse.containerservices.reloadGadget", 17868 17869 /** 17870 * Topic for listening to gadget view changed events. 17871 * The provided callback will be invoked when a gadget changes view. 17872 * The callback will be passed a {@link finesse.containerservices.GadgetViewChangedEvent}. 17873 */ 17874 GADGET_VIEW_CHANGED_EVENT: "finesse.containerservices.gadgetViewChangedEvent", 17875 17876 /** 17877 * Topic for listening to max available height changed events. 17878 * The provided callback will be invoked when the maximum height available to a maximized gadget changes. 17879 * This event is only meant for maximized gadgets and will not be published unless a maximized gadget exists. 17880 * The callback will be passed a {@link finesse.containerservices.MaxAvailableHeightChangedEvent}. 17881 */ 17882 MAX_AVAILABLE_HEIGHT_CHANGED_EVENT: "finesse.containerservices.maxAvailableHeightChangedEvent", 17883 17884 /** 17885 * @class This is the set of Topics used for subscribing for events from ContainerServices. 17886 * Use {@link finesse.containerservices.ContainerServices#addHandler} to subscribe to the topic. 17887 * 17888 * @constructs 17889 */ 17890 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 17891 }; 17892 17893 window.finesse = window.finesse || {}; 17894 window.finesse.containerservices = window.finesse.containerservices || {}; 17895 window.finesse.containerservices.ContainerServices = ContainerServices; 17896 17897 return ContainerServices; 17898 })); 17899 17900 /** 17901 * This "interface" is just a way to easily jsdoc the Object callback handlers. 17902 * 17903 * @requires finesse.clientservices.ClientServices 17904 * @requires Class 17905 */ 17906 /** @private */ 17907 (function (factory) { 17908 17909 17910 // Define as an AMD module if possible 17911 if ( typeof define === 'function' && define.amd ) 17912 { 17913 define('interfaces/RestObjectHandlers', ["FinesseBase", 17914 "utilities/Utilities", 17915 "restservices/Notifier", 17916 "clientservices/ClientServices", 17917 "clientservices/Topics"], factory ); 17918 } 17919 /* Define using browser globals otherwise 17920 * Prevent multiple instantiations if the script is loaded twice 17921 */ 17922 else 17923 { 17924 factory(finesse.FinesseBase, finesse.utilities.Utilities, 17925 finesse.restservices.Notifier, 17926 finesse.clientservices.ClientServices, 17927 finesse.clientservices.Topics); 17928 } 17929 }(function () { 17930 17931 var RestObjectHandlers = ( function () { /** @lends finesse.interfaces.RestObjectHandlers.prototype */ 17932 17933 return { 17934 17935 /** 17936 * @class 17937 * This "interface" defines REST Object callback handlers, passed as an argument to 17938 * Object getter methods in cases where the Object is going to be created. 17939 * 17940 * @param {Object} [handlers] 17941 * An object containing callback handlers for instantiation and runtime 17942 * Callback to invoke upon successful instantiation, passes in REST object. 17943 * @param {Function} [handlers.onLoad(this)] 17944 * Callback to invoke upon loading the data for the first time. 17945 * @param {Function} [handlers.onChange(this)] 17946 * Callback to invoke upon successful update object (PUT) 17947 * @param {Function} [handlers.onAdd(this)] 17948 * Callback to invoke upon successful update to add object (POST) 17949 * @param {Function} [handlers.onDelete(this)] 17950 * Callback to invoke upon successful update to delete object (DELETE) 17951 * @param {Function} [handlers.onError(rsp)] 17952 * Callback to invoke on update error (refresh or event) 17953 * as passed by finesse.restservices.RestBase.restRequest()<br> 17954 * {<br> 17955 * status: {Number} The HTTP status code returned<br> 17956 * content: {String} Raw string of response<br> 17957 * object: {Object} Parsed object of response<br> 17958 * error: {Object} Wrapped exception that was caught<br> 17959 * error.errorType: {String} Type of error that was caught<br> 17960 * error.errorMessage: {String} Message associated with error<br> 17961 * }<br> 17962 * <br> 17963 * Note that RestCollections have two additional callback handlers:<br> 17964 * <br> 17965 * @param {Function} [handlers.onCollectionAdd(this)]: when an object is added to this collection 17966 * @param {Function} [handlers.onCollectionDelete(this)]: when an object is removed from this collection 17967 17968 * @constructs 17969 */ 17970 _fakeConstuctor: function () { 17971 /* This is here to enable jsdoc to document this as a class. */ 17972 } 17973 }; 17974 }()); 17975 17976 window.finesse = window.finesse || {}; 17977 window.finesse.interfaces = window.finesse.interfaces || {}; 17978 window.finesse.interfaces.RestObjectHandlers = RestObjectHandlers; 17979 17980 return RestObjectHandlers; 17981 17982 })); 17983 17984 17985 /** 17986 * This "interface" is just a way to easily jsdoc the REST request handlers. 17987 * 17988 * @requires finesse.clientservices.ClientServices 17989 * @requires Class 17990 */ 17991 /** @private */ 17992 (function (factory) { 17993 17994 17995 // Define as an AMD module if possible 17996 if ( typeof define === 'function' && define.amd ) 17997 { 17998 define('interfaces/RequestHandlers', ["FinesseBase", 17999 "utilities/Utilities", 18000 "restservices/Notifier", 18001 "clientservices/ClientServices", 18002 "clientservices/Topics"], factory ); 18003 } 18004 /* Define using browser globals otherwise 18005 * Prevent multiple instantiations if the script is loaded twice 18006 */ 18007 else 18008 { 18009 factory(finesse.FinesseBase, finesse.utilities.Utilities, 18010 finesse.restservices.Notifier, 18011 finesse.clientservices.ClientServices, 18012 finesse.clientservices.Topics); 18013 } 18014 }(function () { 18015 18016 var RequestHandlers = ( function () { /** @lends finesse.interfaces.RequestHandlers.prototype */ 18017 18018 return { 18019 18020 /** 18021 * @class 18022 * This "interface" defines REST Object callback handlers, passed as an argument to 18023 * Object getter methods in cases where the Object is going to be created. 18024 * 18025 * @param {Object} handlers 18026 * An object containing the following (optional) handlers for the request:<ul> 18027 * <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following 18028 * response object as its only parameter:<ul> 18029 * <li><b>status:</b> {Number} The HTTP status code returned</li> 18030 * <li><b>content:</b> {String} Raw string of response</li> 18031 * <li><b>object:</b> {Object} Parsed object of response</li></ul> 18032 * <li><b>error(rsp):</b> An error callback function for an unsuccessful request to be invoked with the 18033 * error response object as its only parameter:<ul> 18034 * <li><b>status:</b> {Number} The HTTP status code returned</li> 18035 * <li><b>content:</b> {String} Raw string of response</li> 18036 * <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li> 18037 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 18038 * <li><b>errorType:</b> {String} Type of error that was caught</li> 18039 * <li><b>errorMessage:</b> {String} Message associated with error</li> 18040 * </ul></li> 18041 * </ul> 18042 18043 * @constructs 18044 */ 18045 _fakeConstuctor: function () { 18046 /* This is here to enable jsdoc to document this as a class. */ 18047 } 18048 }; 18049 }()); 18050 18051 window.finesse = window.finesse || {}; 18052 window.finesse.interfaces = window.finesse.interfaces || {}; 18053 window.finesse.interfaces.RequestHandlers = RequestHandlers; 18054 18055 finesse = finesse || {}; 18056 /** @namespace These interfaces are just a convenience for documenting common parameter structures. */ 18057 finesse.interfaces = finesse.interfaces || {}; 18058 18059 return RequestHandlers; 18060 18061 })); 18062 18063 18064 (function (factory) { 18065 18066 18067 // Define as an AMD module if possible 18068 if ( typeof define === 'function' && define.amd ) 18069 { 18070 define('finesse-desktop', [], factory ); 18071 } 18072 /* Define using browser globals otherwise 18073 * Prevent multiple instantiations if the script is loaded twice 18074 */ 18075 else 18076 { 18077 factory(); 18078 } 18079 }(function () { 18080 if (typeof require === 'function') { 18081 (function () { 18082 /* 18083 The 'paths' section lets you define keys to refer to libraries 18084 so you don't have to to refer to them by their full paths everywhere. 18085 18086 This removes the need to refer to 3rd-party libraries using the '3rdparty' path, 18087 and the need to refer to patched libaries using the '.patched' suffix. 18088 18089 These keys are usable inside all require() calls inside each gadget module. 18090 */ 18091 require.config({ 18092 paths : { 18093 'Math.uuid' : '../thirdparty/util/Math.uuid', 18094 'iso8601' : '../thirdparty/util/iso8601' 18095 }, 18096 18097 /* 18098 This 'shim' section lets you specify a dependency tree for all libaries that are NOT AMD modules. 18099 (AMD modules always contain a 'define()' call. For more information about AMD: https://github.com/amdjs/amdjs-api/wiki/AMD) 18100 18101 ** DO NOT list any AMD modules in this 'shim' section. ** 18102 18103 The format is 'libraryKey': ['dependencyKey1', 'dependencyKey2' ...]. Both keys can refer to any JS module 18104 or one of the 'path' keys listed above. 18105 18106 FOR EXAMPLE: 18107 dataTables depends on jQuery, but all of the dataTables plugins depend on dataTables. 18108 We can refer to 'dataTables' and 'dataTables.scroller' as keys on the left side 18109 since those are aliases defined in the 'paths' section above. 18110 18111 NOTE: Most jQuery plugins ARE NOT AMD modules, so when adding a new jQuery plugin that is not an AMD module, 18112 make sure it has jquery listed as a dependency here (similar to all of the boostrap-* plugins, jquery.cookie, etc.) 18113 18114 Official documentation on RequireJS shim configuration: http://requirejs.org/docs/api.html#config-shim 18115 */ 18116 shim : { 18117 'Math.uuid' : { 18118 exports : 'Math' 18119 } 18120 } 18121 }); 18122 18123 //All modules in this list (and their dependencies) will be concatenated into one file to be used in the container and gadgets 18124 require(['restservices/Users', 18125 'restservices/Teams', 18126 'restservices/SystemInfo', 18127 'utilities/I18n', 18128 'utilities/Logger', 18129 'utilities/SaxParser', 18130 'cslogger/ClientLogger', 18131 'cslogger/FinesseLogger', 18132 'containerservices/ContainerServices', 18133 'interfaces/RestObjectHandlers', 18134 'interfaces/RequestHandlers'], function() { 18135 // Do nothing 18136 }); 18137 }.bind(this)()); 18138 } 18139 18140 // If being used in a gadget, stuff the auth string into the gadget prefs 18141 if (gadgets.Prefs) { 18142 var _prefs = new gadgets.Prefs(), 18143 authString = finesse.utilities.Utilities.getUserAuthString(); 18144 _prefs.set("authorization", authString); 18145 } 18146 })); 18147