1 /** 2 * Cisco Finesse - JavaScript Library 3 * Version 11.0(1) 4 * Cisco Systems, Inc. 5 * http://www.cisco.com/ 6 * 7 * Portions created or assigned to Cisco Systems, Inc. are 8 * Copyright (c) 2015 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 (function (root, factory) { 30 if (typeof define === 'function' && define.amd) { 31 define('finesse', [], factory); 32 } else { 33 root.finesse = factory(); 34 } 35 }(this, function () { 36 /** 37 * @license almond 0.2.9 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved. 38 * Available via the MIT or new BSD license. 39 * see: http://github.com/jrburke/almond for details 40 */ 41 //Going sloppy to avoid 'use strict' string cost, but strict practices should 42 //be followed. 43 /*jslint sloppy: true */ 44 /*global setTimeout: false */ 45 46 var requirejs, require, define; 47 (function (undef) { 48 var main, req, makeMap, handlers, 49 defined = {}, 50 waiting = {}, 51 config = {}, 52 defining = {}, 53 hasOwn = Object.prototype.hasOwnProperty, 54 aps = [].slice, 55 jsSuffixRegExp = /\.js$/; 56 57 function hasProp(obj, prop) { 58 return hasOwn.call(obj, prop); 59 } 60 61 /** 62 * Given a relative module name, like ./something, normalize it to 63 * a real name that can be mapped to a path. 64 * @param {String} name the relative name 65 * @param {String} baseName a real name that the name arg is relative 66 * to. 67 * @returns {String} normalized name 68 */ 69 function normalize(name, baseName) { 70 var nameParts, nameSegment, mapValue, foundMap, lastIndex, 71 foundI, foundStarMap, starI, i, j, part, 72 baseParts = baseName && baseName.split("/"), 73 map = config.map, 74 starMap = (map && map['*']) || {}; 75 76 //Adjust any relative paths. 77 if (name && name.charAt(0) === ".") { 78 //If have a base name, try to normalize against it, 79 //otherwise, assume it is a top-level require that will 80 //be relative to baseUrl in the end. 81 if (baseName) { 82 //Convert baseName to array, and lop off the last part, 83 //so that . matches that "directory" and not name of the baseName's 84 //module. For instance, baseName of "one/two/three", maps to 85 //"one/two/three.js", but we want the directory, "one/two" for 86 //this normalization. 87 baseParts = baseParts.slice(0, baseParts.length - 1); 88 name = name.split('/'); 89 lastIndex = name.length - 1; 90 91 // Node .js allowance: 92 if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { 93 name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); 94 } 95 96 name = baseParts.concat(name); 97 98 //start trimDots 99 for (i = 0; i < name.length; i += 1) { 100 part = name[i]; 101 if (part === ".") { 102 name.splice(i, 1); 103 i -= 1; 104 } else if (part === "..") { 105 if (i === 1 && (name[2] === '..' || name[0] === '..')) { 106 //End of the line. Keep at least one non-dot 107 //path segment at the front so it can be mapped 108 //correctly to disk. Otherwise, there is likely 109 //no path mapping for a path starting with '..'. 110 //This can still fail, but catches the most reasonable 111 //uses of .. 112 break; 113 } else if (i > 0) { 114 name.splice(i - 1, 2); 115 i -= 2; 116 } 117 } 118 } 119 //end trimDots 120 121 name = name.join("/"); 122 } else if (name.indexOf('./') === 0) { 123 // No baseName, so this is ID is resolved relative 124 // to baseUrl, pull off the leading dot. 125 name = name.substring(2); 126 } 127 } 128 129 //Apply map config if available. 130 if ((baseParts || starMap) && map) { 131 nameParts = name.split('/'); 132 133 for (i = nameParts.length; i > 0; i -= 1) { 134 nameSegment = nameParts.slice(0, i).join("/"); 135 136 if (baseParts) { 137 //Find the longest baseName segment match in the config. 138 //So, do joins on the biggest to smallest lengths of baseParts. 139 for (j = baseParts.length; j > 0; j -= 1) { 140 mapValue = map[baseParts.slice(0, j).join('/')]; 141 142 //baseName segment has config, find if it has one for 143 //this name. 144 if (mapValue) { 145 mapValue = mapValue[nameSegment]; 146 if (mapValue) { 147 //Match, update name to the new value. 148 foundMap = mapValue; 149 foundI = i; 150 break; 151 } 152 } 153 } 154 } 155 156 if (foundMap) { 157 break; 158 } 159 160 //Check for a star map match, but just hold on to it, 161 //if there is a shorter segment match later in a matching 162 //config, then favor over this star map. 163 if (!foundStarMap && starMap && starMap[nameSegment]) { 164 foundStarMap = starMap[nameSegment]; 165 starI = i; 166 } 167 } 168 169 if (!foundMap && foundStarMap) { 170 foundMap = foundStarMap; 171 foundI = starI; 172 } 173 174 if (foundMap) { 175 nameParts.splice(0, foundI, foundMap); 176 name = nameParts.join('/'); 177 } 178 } 179 180 return name; 181 } 182 183 function makeRequire(relName, forceSync) { 184 return function () { 185 //A version of a require function that passes a moduleName 186 //value for items that may need to 187 //look up paths relative to the moduleName 188 return req.apply(undef, aps.call(arguments, 0).concat([relName, forceSync])); 189 }; 190 } 191 192 function makeNormalize(relName) { 193 return function (name) { 194 return normalize(name, relName); 195 }; 196 } 197 198 function makeLoad(depName) { 199 return function (value) { 200 defined[depName] = value; 201 }; 202 } 203 204 function callDep(name) { 205 if (hasProp(waiting, name)) { 206 var args = waiting[name]; 207 delete waiting[name]; 208 defining[name] = true; 209 main.apply(undef, args); 210 } 211 212 if (!hasProp(defined, name) && !hasProp(defining, name)) { 213 throw new Error('No ' + name); 214 } 215 return defined[name]; 216 } 217 218 //Turns a plugin!resource to [plugin, resource] 219 //with the plugin being undefined if the name 220 //did not have a plugin prefix. 221 function splitPrefix(name) { 222 var prefix, 223 index = name ? name.indexOf('!') : -1; 224 if (index > -1) { 225 prefix = name.substring(0, index); 226 name = name.substring(index + 1, name.length); 227 } 228 return [prefix, name]; 229 } 230 231 /** 232 * Makes a name map, normalizing the name, and using a plugin 233 * for normalization if necessary. Grabs a ref to plugin 234 * too, as an optimization. 235 */ 236 makeMap = function (name, relName) { 237 var plugin, 238 parts = splitPrefix(name), 239 prefix = parts[0]; 240 241 name = parts[1]; 242 243 if (prefix) { 244 prefix = normalize(prefix, relName); 245 plugin = callDep(prefix); 246 } 247 248 //Normalize according 249 if (prefix) { 250 if (plugin && plugin.normalize) { 251 name = plugin.normalize(name, makeNormalize(relName)); 252 } else { 253 name = normalize(name, relName); 254 } 255 } else { 256 name = normalize(name, relName); 257 parts = splitPrefix(name); 258 prefix = parts[0]; 259 name = parts[1]; 260 if (prefix) { 261 plugin = callDep(prefix); 262 } 263 } 264 265 //Using ridiculous property names for space reasons 266 return { 267 f: prefix ? prefix + '!' + name : name, //fullName 268 n: name, 269 pr: prefix, 270 p: plugin 271 }; 272 }; 273 274 function makeConfig(name) { 275 return function () { 276 return (config && config.config && config.config[name]) || {}; 277 }; 278 } 279 280 handlers = { 281 require: function (name) { 282 return makeRequire(name); 283 }, 284 exports: function (name) { 285 var e = defined[name]; 286 if (typeof e !== 'undefined') { 287 return e; 288 } else { 289 return (defined[name] = {}); 290 } 291 }, 292 module: function (name) { 293 return { 294 id: name, 295 uri: '', 296 exports: defined[name], 297 config: makeConfig(name) 298 }; 299 } 300 }; 301 302 main = function (name, deps, callback, relName) { 303 var cjsModule, depName, ret, map, i, 304 args = [], 305 callbackType = typeof callback, 306 usingExports; 307 308 //Use name if no relName 309 relName = relName || name; 310 311 //Call the callback to define the module, if necessary. 312 if (callbackType === 'undefined' || callbackType === 'function') { 313 //Pull out the defined dependencies and pass the ordered 314 //values to the callback. 315 //Default to [require, exports, module] if no deps 316 deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps; 317 for (i = 0; i < deps.length; i += 1) { 318 map = makeMap(deps[i], relName); 319 depName = map.f; 320 321 //Fast path CommonJS standard dependencies. 322 if (depName === "require") { 323 args[i] = handlers.require(name); 324 } else if (depName === "exports") { 325 //CommonJS module spec 1.1 326 args[i] = handlers.exports(name); 327 usingExports = true; 328 } else if (depName === "module") { 329 //CommonJS module spec 1.1 330 cjsModule = args[i] = handlers.module(name); 331 } else if (hasProp(defined, depName) || 332 hasProp(waiting, depName) || 333 hasProp(defining, depName)) { 334 args[i] = callDep(depName); 335 } else if (map.p) { 336 map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {}); 337 args[i] = defined[depName]; 338 } else { 339 throw new Error(name + ' missing ' + depName); 340 } 341 } 342 343 ret = callback ? callback.apply(defined[name], args) : undefined; 344 345 if (name) { 346 //If setting exports via "module" is in play, 347 //favor that over return value and exports. After that, 348 //favor a non-undefined return value over exports use. 349 if (cjsModule && cjsModule.exports !== undef && 350 cjsModule.exports !== defined[name]) { 351 defined[name] = cjsModule.exports; 352 } else if (ret !== undef || !usingExports) { 353 //Use the return value from the function. 354 defined[name] = ret; 355 } 356 } 357 } else if (name) { 358 //May just be an object definition for the module. Only 359 //worry about defining if have a module name. 360 defined[name] = callback; 361 } 362 }; 363 364 requirejs = require = req = function (deps, callback, relName, forceSync, alt) { 365 if (typeof deps === "string") { 366 if (handlers[deps]) { 367 //callback in this case is really relName 368 return handlers[deps](callback); 369 } 370 //Just return the module wanted. In this scenario, the 371 //deps arg is the module name, and second arg (if passed) 372 //is just the relName. 373 //Normalize module name, if it contains . or .. 374 return callDep(makeMap(deps, callback).f); 375 } else if (!deps.splice) { 376 //deps is a config object, not an array. 377 config = deps; 378 if (config.deps) { 379 req(config.deps, config.callback); 380 } 381 if (!callback) { 382 return; 383 } 384 385 if (callback.splice) { 386 //callback is an array, which means it is a dependency list. 387 //Adjust args if there are dependencies 388 deps = callback; 389 callback = relName; 390 relName = null; 391 } else { 392 deps = undef; 393 } 394 } 395 396 //Support require(['a']) 397 callback = callback || function () {}; 398 399 //If relName is a function, it is an errback handler, 400 //so remove it. 401 if (typeof relName === 'function') { 402 relName = forceSync; 403 forceSync = alt; 404 } 405 406 //Simulate async callback; 407 if (forceSync) { 408 main(undef, deps, callback, relName); 409 } else { 410 //Using a non-zero value because of concern for what old browsers 411 //do, and latest browsers "upgrade" to 4 if lower value is used: 412 //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout: 413 //If want a value immediately, use require('id') instead -- something 414 //that works in almond on the global level, but not guaranteed and 415 //unlikely to work in other AMD implementations. 416 setTimeout(function () { 417 main(undef, deps, callback, relName); 418 }, 4); 419 } 420 421 return req; 422 }; 423 424 /** 425 * Just drops the config on the floor, but returns req in case 426 * the config return value is used. 427 */ 428 req.config = function (cfg) { 429 return req(cfg); 430 }; 431 432 /** 433 * Expose module registry for debugging and tooling 434 */ 435 requirejs._defined = defined; 436 437 define = function (name, deps, callback) { 438 439 //This module may not have dependencies 440 if (!deps.splice) { 441 //deps is not an array, so probably means 442 //an object literal or factory function for 443 //the value. Adjust args. 444 callback = deps; 445 deps = []; 446 } 447 448 if (!hasProp(defined, name) && !hasProp(waiting, name)) { 449 waiting[name] = [name, deps, callback]; 450 } 451 }; 452 453 define.amd = { 454 jQuery: true 455 }; 456 }()); 457 define("../thirdparty/almond", function(){}); 458 459 /* Simple JavaScript Inheritance 460 * By John Resig http://ejohn.org/ 461 * MIT Licensed. 462 */ 463 // Inspired by base2 and Prototype 464 define('../thirdparty/Class',[], function () { 465 var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/; 466 // The base Class implementation (does nothing) 467 /** @private */ 468 Class = function(){}; 469 470 // Create a new Class that inherits from this class 471 /** @private */ 472 Class.extend = function(prop) { 473 var _super = this.prototype; 474 475 // Instantiate a base class (but only create the instance, 476 // don't run the init constructor) 477 initializing = true; 478 var prototype = new this(); 479 initializing = false; 480 481 // Copy the properties over onto the new prototype 482 for (var name in prop) { 483 // Check if we're overwriting an existing function 484 prototype[name] = typeof prop[name] == "function" && 485 typeof _super[name] == "function" && fnTest.test(prop[name]) ? 486 (function(name, fn){ 487 return function() { 488 var tmp = this._super; 489 490 // Add a new ._super() method that is the same method 491 // but on the super-class 492 this._super = _super[name]; 493 494 // The method only need to be bound temporarily, so we 495 // remove it when we're done executing 496 var ret = fn.apply(this, arguments); 497 this._super = tmp; 498 499 return ret; 500 }; 501 })(name, prop[name]) : 502 prop[name]; 503 } 504 505 // The dummy class constructor 506 /** @private */ 507 function Class() { 508 // All construction is actually done in the init method 509 if ( !initializing && this.init ) 510 this.init.apply(this, arguments); 511 } 512 513 // Populate our constructed prototype object 514 Class.prototype = prototype; 515 516 // Enforce the constructor to be what we expect 517 Class.prototype.constructor = Class; 518 519 // And make this class extendable 520 Class.extend = arguments.callee; 521 522 return Class; 523 }; 524 return Class; 525 }); 526 527 /** 528 * JavaScript base object that all finesse objects should inherit 529 * from because it encapsulates and provides the common functionality. 530 * 531 * Note: This javascript class requires the "inhert.js" to be included 532 * (which simplifies the class inheritance). 533 * 534 * 535 * @requires finesse.utilities.Logger 536 */ 537 538 /** The following comment is to prevent jslint errors about 539 * using variables before they are defined. 540 */ 541 /*global Class */ 542 define('FinesseBase', ["../thirdparty/Class"], function (Class) { 543 var FinesseBase = Class.extend({ 544 init: function () { 545 } 546 }); 547 548 window.finesse = window.finesse || {}; 549 window.finesse.FinesseBase = FinesseBase; 550 551 return FinesseBase; 552 }); 553 554 /** 555 * A collection of conversion utilities. 556 * Last modified 07-06-2011, Cisco Systems 557 * 558 */ 559 /** @private */ 560 define('utilities/../../thirdparty/util/converter',[], function () { 561 /** 562 * @class 563 * Contains a collection of utility functions. 564 * @private 565 */ 566 Converter = (function () { 567 return { 568 /* This work is licensed under Creative Commons GNU LGPL License. 569 570 License: http://creativecommons.org/licenses/LGPL/2.1/ 571 Version: 0.9 572 Author: Stefan Goessner/2006 573 Web: http://goessner.net/ 574 575 2013-09-16 Modified to remove use of XmlNode.innerHTML in the innerXml function by Cisco Systems, Inc. 576 */ 577 xml2json: function (xml, tab) { 578 var X = { 579 toObj: function (xml) { 580 var o = {}; 581 if (xml.nodeType === 1) { 582 // element node .. 583 if (xml.attributes.length) 584 // element with attributes .. 585 for (var i = 0; i < xml.attributes.length; i++) 586 o["@" + xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue || "").toString(); 587 if (xml.firstChild) { 588 // element has child nodes .. 589 var textChild = 0, 590 cdataChild = 0, 591 hasElementChild = false; 592 for (var n = xml.firstChild; n; n = n.nextSibling) { 593 if (n.nodeType == 1) hasElementChild = true; 594 else if (n.nodeType == 3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++; 595 // non-whitespace text 596 else if (n.nodeType == 4) cdataChild++; 597 // cdata section node 598 } 599 if (hasElementChild) { 600 if (textChild < 2 && cdataChild < 2) { 601 // structured element with evtl. a single text or/and cdata node .. 602 X.removeWhite(xml); 603 for (var n = xml.firstChild; n; n = n.nextSibling) { 604 if (n.nodeType == 3) 605 // text node 606 o["#text"] = X.escape(n.nodeValue); 607 else if (n.nodeType == 4) 608 // cdata node 609 o["#cdata"] = X.escape(n.nodeValue); 610 else if (o[n.nodeName]) { 611 // multiple occurence of element .. 612 if (o[n.nodeName] instanceof Array) 613 o[n.nodeName][o[n.nodeName].length] = X.toObj(n); 614 else 615 o[n.nodeName] = [o[n.nodeName], X.toObj(n)]; 616 } 617 else 618 // first occurence of element.. 619 o[n.nodeName] = X.toObj(n); 620 } 621 } 622 else { 623 // mixed content 624 if (!xml.attributes.length) 625 o = X.escape(X.innerXml(xml)); 626 else 627 o["#text"] = X.escape(X.innerXml(xml)); 628 } 629 } 630 else if (textChild) { 631 // pure text 632 if (!xml.attributes.length) 633 o = X.escape(X.innerXml(xml)); 634 else 635 o["#text"] = X.escape(X.innerXml(xml)); 636 } 637 else if (cdataChild) { 638 // cdata 639 if (cdataChild > 1) 640 o = X.escape(X.innerXml(xml)); 641 else 642 for (var n = xml.firstChild; n; n = n.nextSibling) 643 o["#cdata"] = X.escape(n.nodeValue); 644 } 645 } 646 if (!xml.attributes.length && !xml.firstChild) o = null; 647 } 648 else if (xml.nodeType == 9) { 649 // document.node 650 o = X.toObj(xml.documentElement); 651 } 652 else 653 throw ("unhandled node type: " + xml.nodeType); 654 return o; 655 }, 656 toJson: function(o, name, ind) { 657 var json = name ? ("\"" + name + "\"") : ""; 658 if (o instanceof Array) { 659 for (var i = 0, n = o.length; i < n; i++) 660 o[i] = X.toJson(o[i], "", ind + "\t"); 661 json += (name ? ":[": "[") + (o.length > 1 ? ("\n" + ind + "\t" + o.join(",\n" + ind + "\t") + "\n" + ind) : o.join("")) + "]"; 662 } 663 else if (o == null) 664 json += (name && ":") + "null"; 665 else if (typeof(o) == "object") { 666 var arr = []; 667 for (var m in o) 668 arr[arr.length] = X.toJson(o[m], m, ind + "\t"); 669 json += (name ? ":{": "{") + (arr.length > 1 ? ("\n" + ind + "\t" + arr.join(",\n" + ind + "\t") + "\n" + ind) : arr.join("")) + "}"; 670 } 671 else if (typeof(o) == "string") 672 json += (name && ":") + "\"" + o.toString() + "\""; 673 else 674 json += (name && ":") + o.toString(); 675 return json; 676 }, 677 innerXml: function(node) { 678 var s = ""; 679 var asXml = function(n) { 680 var s = ""; 681 if (n.nodeType == 1) { 682 s += "<" + n.nodeName; 683 for (var i = 0; i < n.attributes.length; i++) 684 s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\""; 685 if (n.firstChild) { 686 s += ">"; 687 for (var c = n.firstChild; c; c = c.nextSibling) 688 s += asXml(c); 689 s += "</" + n.nodeName + ">"; 690 } 691 else 692 s += "/>"; 693 } 694 else if (n.nodeType == 3) 695 s += n.nodeValue; 696 else if (n.nodeType == 4) 697 s += "<![CDATA[" + n.nodeValue + "]]>"; 698 return s; 699 }; 700 for (var c = node.firstChild; c; c = c.nextSibling) 701 s += asXml(c); 702 return s; 703 }, 704 escape: function(txt) { 705 return txt.replace(/[\\]/g, "\\\\") 706 .replace(/[\"]/g, '\\"') 707 .replace(/[\n]/g, '\\n') 708 .replace(/[\r]/g, '\\r'); 709 }, 710 removeWhite: function(e) { 711 e.normalize(); 712 for (var n = e.firstChild; n;) { 713 if (n.nodeType == 3) { 714 // text node 715 if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) { 716 // pure whitespace text node 717 var nxt = n.nextSibling; 718 e.removeChild(n); 719 n = nxt; 720 } 721 else 722 n = n.nextSibling; 723 } 724 else if (n.nodeType == 1) { 725 // element node 726 X.removeWhite(n); 727 n = n.nextSibling; 728 } 729 else 730 // any other node 731 n = n.nextSibling; 732 } 733 return e; 734 } 735 }; 736 if (xml.nodeType == 9) 737 // document node 738 xml = xml.documentElement; 739 var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t"); 740 return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}"; 741 }, 742 743 /* This work is licensed under Creative Commons GNU LGPL License. 744 745 License: http://creativecommons.org/licenses/LGPL/2.1/ 746 Version: 0.9 747 Author: Stefan Goessner/2006 748 Web: http://goessner.net/ 749 */ 750 json2xml: function(o, tab) { 751 var toXml = function(v, name, ind) { 752 var xml = ""; 753 if (v instanceof Array) { 754 for (var i = 0, n = v.length; i < n; i++) 755 xml += ind + toXml(v[i], name, ind + "\t") + "\n"; 756 } 757 else if (typeof(v) == "object") { 758 var hasChild = false; 759 xml += ind + "<" + name; 760 for (var m in v) { 761 if (m.charAt(0) == "@") 762 xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\""; 763 else 764 hasChild = true; 765 } 766 xml += hasChild ? ">": "/>"; 767 if (hasChild) { 768 for (var m in v) { 769 if (m == "#text") 770 xml += v[m]; 771 else if (m == "#cdata") 772 xml += "<![CDATA[" + v[m] + "]]>"; 773 else if (m.charAt(0) != "@") 774 xml += toXml(v[m], m, ind + "\t"); 775 } 776 xml += (xml.charAt(xml.length - 1) == "\n" ? ind: "") + "</" + name + ">"; 777 } 778 } 779 else { 780 xml += ind + "<" + name + ">" + v.toString() + "</" + name + ">"; 781 } 782 return xml; 783 }, 784 xml = ""; 785 for (var m in o) 786 xml += toXml(o[m], m, ""); 787 return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, ""); 788 } 789 }; 790 })(); 791 792 window.finesse = window.finesse || {}; 793 window.finesse.Converter = Converter; 794 795 return Converter; 796 }); 797 798 /** 799 * SaxParser.js: provides a simple SAX parser 800 * 801 * NONVALIDATING - this will not validate whether you have valid XML or not. It will simply report what it finds. 802 * Only supports elements, attributes, and text. No comments, cdata, processing instructions, etc. 803 */ 804 805 /** 806 * @requires 807 * @ignore 808 */ 809 // Add SaxParser to the finesse.utilities namespace 810 define('utilities/SaxParser',[], function () { 811 var SaxParser = { 812 parse: function(xml, callback) { 813 // Event callbacks 814 /** @private */ 815 var triggerEvent = function (type, data) { 816 callback.call(null, type, data); 817 }, 818 /** @private */ 819 triggerStartElement = function (name) { 820 triggerEvent("StartElement", name); 821 }, 822 /** @private */ 823 triggerEndElement = function (name) { 824 triggerEvent("EndElement", name); 825 }, 826 /** @private */ 827 triggerAttribute = function (name, value) { 828 triggerEvent("Attribute", { "name": name, "value": value }); 829 }, 830 /** @private */ 831 triggerText = function (text) { 832 triggerEvent("Text", text); 833 }, 834 835 // Parsing 836 cursor = 0, 837 xmlLength = xml.length, 838 whitespaceRegex = /^[ \t\r\n]*$/, 839 /** @private */ 840 isWhitespace = function (text) { 841 return whitespaceRegex.test(text); 842 }, 843 /** @private */ 844 moveToNonWhitespace = function () { 845 while (isWhitespace(xml.charAt(cursor))) { 846 cursor += 1; 847 } 848 }, 849 /** @private */ 850 parseAttribute = function () { 851 var nameBuffer = [], 852 valueBuffer = [], 853 valueIsQuoted = false, 854 cursorChar = ""; 855 856 nameBuffer.push(xml.charAt(cursor)); 857 858 // Get the name 859 cursor += 1; 860 while (cursor < xmlLength) { 861 cursorChar = xml.charAt(cursor); 862 if (isWhitespace(cursorChar) || cursorChar === "=") { 863 // Move on to gathering value 864 break; 865 } 866 else { 867 nameBuffer.push(cursorChar); 868 } 869 cursor += 1; 870 } 871 872 // Skip the equals sign and any whitespace 873 moveToNonWhitespace(); 874 if (cursorChar === "=") { 875 cursor += 1; 876 } else { 877 throw new Error("Did not find = following attribute name at " + cursor); 878 } 879 moveToNonWhitespace(); 880 881 // Get the value 882 valueIsQuoted = cursor !== xmlLength - 1 ? xml.charAt(cursor) === "\"": false; 883 if (valueIsQuoted) { 884 cursor += 1; 885 while (cursor < xmlLength) { 886 cursorChar = xml.charAt(cursor); 887 if (cursorChar === "\"") { 888 // Found the closing quote, so end value 889 triggerAttribute(nameBuffer.join(""), valueBuffer.join("")); 890 break; 891 } 892 else { 893 valueBuffer.push(cursorChar); 894 } 895 cursor += 1; 896 } 897 } 898 else { 899 throw new Error("Found unquoted attribute value at " + cursor); 900 } 901 }, 902 /** @private */ 903 parseEndElement = function () { 904 var elementNameBuffer = [], 905 cursorChar = ""; 906 cursor += 2; 907 while (cursor < xmlLength) { 908 cursorChar = xml.charAt(cursor); 909 if (cursorChar === ">") { 910 triggerEndElement(elementNameBuffer.join("")); 911 break; 912 } 913 else { 914 elementNameBuffer.push(cursorChar); 915 } 916 cursor += 1; 917 } 918 }, 919 /** @private */ 920 parseReference = function() { 921 var type, 922 TYPE_DEC_CHAR_REF = 1, 923 TYPE_HEX_CHAR_REF = 2, 924 TYPE_ENTITY_REF = 3, 925 buffer = ""; 926 cursor += 1; 927 // Determine the type of reference. 928 if (xml.charAt(cursor) === "#") { 929 cursor += 1; 930 if (xml.charAt(cursor) === "x") { 931 type = TYPE_HEX_CHAR_REF; 932 cursor += 1; 933 } else { 934 type = TYPE_DEC_CHAR_REF; 935 } 936 } else { 937 type = TYPE_ENTITY_REF; 938 } 939 // Read the reference into a buffer. 940 while (xml.charAt(cursor) !== ";") { 941 buffer += xml.charAt(cursor); 942 cursor += 1; 943 if (cursor >= xmlLength) { 944 throw new Error("Unterminated XML reference: " + buffer); 945 } 946 } 947 // Convert the reference to the appropriate character. 948 switch (type) { 949 case TYPE_DEC_CHAR_REF: 950 return String.fromCharCode(parseInt(buffer, 10)); 951 case TYPE_HEX_CHAR_REF: 952 return String.fromCharCode(parseInt(buffer, 16)); 953 case TYPE_ENTITY_REF: 954 switch (buffer) { 955 case "amp": 956 return "&"; 957 case "lt": 958 return "<"; 959 case "gt": 960 return ">"; 961 case "apos": 962 return "'"; 963 case "quot": 964 return "\""; 965 default: 966 throw new Error("Invalid XML entity reference: " + buffer); 967 } 968 // break; (currently unreachable) 969 } 970 }, 971 /** @private */ 972 parseElement = function () { 973 var elementNameBuffer = [], 974 textBuffer = [], 975 cursorChar = "", 976 whitespace = false; 977 978 // Get element name 979 cursor += 1; 980 while (cursor < xmlLength) { 981 cursorChar = xml.charAt(cursor); 982 whitespace = isWhitespace(cursorChar); 983 if (!whitespace && cursorChar !== "/" && cursorChar !== ">") { 984 elementNameBuffer.push(cursorChar); 985 } 986 else { 987 elementNameBuffer = elementNameBuffer.join(""); 988 triggerStartElement(elementNameBuffer); 989 break; 990 } 991 cursor += 1; 992 } 993 994 // Get attributes 995 if (whitespace) { 996 while (cursor < xmlLength) { 997 moveToNonWhitespace(); 998 cursorChar = xml.charAt(cursor); 999 if (cursorChar !== "/" && cursorChar !== ">") { 1000 // Start of attribute 1001 parseAttribute(); 1002 } 1003 cursorChar = xml.charAt(cursor); 1004 if (cursorChar === "/" || cursorChar === ">") { 1005 break; 1006 } 1007 else { 1008 cursor += 1; 1009 } 1010 } 1011 } 1012 1013 // End tag if "/>" was found, 1014 // otherwise we're at the end of the start tag and have to parse into it 1015 if (cursorChar === "/") { 1016 if (cursor !== xmlLength - 1 && xml.charAt(cursor + 1) === ">") { 1017 cursor += 1; 1018 triggerEndElement(elementNameBuffer); 1019 } 1020 } 1021 else { 1022 // cursor is on ">", so parse into element content. Assume text until we find a "<", 1023 // which could be a child element or the current element's end tag. We do not support 1024 // mixed content of text and elements as siblings unless the text is only whitespace. 1025 // Text cannot contain <, >, ", or &. They should be <, >, ", & respectively. 1026 cursor += 1; 1027 while (cursor < xmlLength) { 1028 cursorChar = xml.charAt(cursor); 1029 if (cursorChar === "<") { 1030 // Determine if end tag or element 1031 if (cursor !== xmlLength - 1 && xml.charAt(cursor + 1) === "/") { 1032 // At end tag 1033 textBuffer = textBuffer.join(""); 1034 if (!isWhitespace(textBuffer)) { 1035 triggerText(textBuffer); 1036 } 1037 parseEndElement(); 1038 break; 1039 } 1040 else { 1041 // At start tag 1042 textBuffer = textBuffer.join(""); 1043 if (!isWhitespace(textBuffer)) { 1044 triggerText(textBuffer); 1045 } 1046 parseElement(); 1047 textBuffer = []; 1048 } 1049 } else if (cursorChar === "&") { 1050 textBuffer.push(parseReference()); 1051 } 1052 else { 1053 textBuffer.push(cursorChar); 1054 } 1055 cursor += 1; 1056 } 1057 } 1058 }, 1059 /** @private */ 1060 skipXmlDeclaration = function() { 1061 if (xml.substr(0, 5) === "<?xml" && isWhitespace(xml.charAt(5))) { 1062 cursor = xml.indexOf(">") + 1; 1063 } 1064 moveToNonWhitespace(); 1065 }; 1066 1067 // Launch. 1068 skipXmlDeclaration(); 1069 parseElement(); 1070 } 1071 }; 1072 1073 window.finesse = window.finesse || {}; 1074 window.finesse.utilities = window.finesse.utilities || {}; 1075 window.finesse.utilities.SaxParser = SaxParser; 1076 1077 return SaxParser; 1078 }); 1079 1080 /** 1081 * Date.parse with progressive enhancement for ISO 8601 <https://github.com/csnover/js-iso8601> 1082 * ?? 2011 Colin Snover <http://zetafleet.com> 1083 * Released under MIT license. 1084 */ 1085 define('iso8601',[], function () { 1086 (function (Date, undefined) { 1087 var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ]; 1088 /** @private **/ 1089 Date.parse = function (date) { 1090 var timestamp, struct, minutesOffset = 0; 1091 1092 // ES5 ??15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string 1093 // before falling back to any implementation-specific date parsing, so that???s what we do, even if native 1094 // implementations could be faster 1095 // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ?? 10 tzHH 11 tzmm 1096 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))) { 1097 // avoid NaN timestamps caused by ???undefined??? values being passed to Date.UTC 1098 for (var i = 0, k; (k = numericKeys[i]); ++i) { 1099 struct[k] = +struct[k] || 0; 1100 } 1101 1102 // allow undefined days and months 1103 struct[2] = (+struct[2] || 1) - 1; 1104 struct[3] = +struct[3] || 1; 1105 1106 if (struct[8] !== 'Z' && struct[9] !== undefined) { 1107 minutesOffset = struct[10] * 60 + struct[11]; 1108 1109 if (struct[9] === '+') { 1110 minutesOffset = 0 - minutesOffset; 1111 } 1112 } 1113 1114 timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]); 1115 } 1116 else { 1117 timestamp = origParse ? origParse(date) : NaN; 1118 } 1119 1120 return timestamp; 1121 }; 1122 }(Date)); 1123 }); 1124 1125 /*! 1126 Math.uuid.js (v1.4) 1127 http://www.broofa.com 1128 mailto:robert@broofa.com 1129 1130 Copyright (c) 2010 Robert Kieffer 1131 Dual licensed under the MIT and GPL licenses. 1132 */ 1133 1134 /* 1135 * Generate a random uuid. 1136 * 1137 * USAGE: Math.uuid(length, radix) 1138 * length - the desired number of characters 1139 * radix - the number of allowable values for each character. 1140 * 1141 * EXAMPLES: 1142 * // No arguments - returns RFC4122, version 4 ID 1143 * >>> Math.uuid() 1144 * "92329D39-6F5C-4520-ABFC-AAB64544E172" 1145 * 1146 * // One argument - returns ID of the specified length 1147 * >>> Math.uuid(15) // 15 character ID (default base=62) 1148 * "VcydxgltxrVZSTV" 1149 * 1150 * // Two arguments - returns ID of the specified length, and radix. (Radix must be <= 62) 1151 * >>> Math.uuid(8, 2) // 8 character ID (base=2) 1152 * "01001010" 1153 * >>> Math.uuid(8, 10) // 8 character ID (base=10) 1154 * "47473046" 1155 * >>> Math.uuid(8, 16) // 8 character ID (base=16) 1156 * "098F4D35" 1157 */ 1158 define('Math.uuid',[], function () { 1159 (function() { 1160 // Private array of chars to use 1161 var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); 1162 1163 /** @private **/ 1164 Math.uuid = function (len, radix) { 1165 var chars = CHARS, uuid = [], i; 1166 radix = radix || chars.length; 1167 1168 if (len) { 1169 // Compact form 1170 for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix]; 1171 } else { 1172 // rfc4122, version 4 form 1173 var r; 1174 1175 // rfc4122 requires these characters 1176 uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; 1177 uuid[14] = '4'; 1178 1179 // Fill in random data. At i==19 set the high bits of clock sequence as 1180 // per rfc4122, sec. 4.1.5 1181 for (i = 0; i < 36; i++) { 1182 if (!uuid[i]) { 1183 r = 0 | Math.random()*16; 1184 uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; 1185 } 1186 } 1187 } 1188 1189 return uuid.join(''); 1190 }; 1191 1192 // A more performant, but slightly bulkier, RFC4122v4 solution. We boost performance 1193 // by minimizing calls to random() 1194 /** @private **/ 1195 Math.uuidFast = function() { 1196 var chars = CHARS, uuid = new Array(36), rnd=0, r; 1197 for (var i = 0; i < 36; i++) { 1198 if (i==8 || i==13 || i==18 || i==23) { 1199 uuid[i] = '-'; 1200 } else if (i==14) { 1201 uuid[i] = '4'; 1202 } else { 1203 if (rnd <= 0x02) rnd = 0x2000000 + (Math.random()*0x1000000)|0; 1204 r = rnd & 0xf; 1205 rnd = rnd >> 4; 1206 uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; 1207 } 1208 } 1209 return uuid.join(''); 1210 }; 1211 1212 // A more compact, but less performant, RFC4122v4 solution: 1213 /** @private **/ 1214 Math.uuidCompact = function() { 1215 return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { 1216 var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); 1217 return v.toString(16); 1218 }); 1219 }; 1220 })(); 1221 }); 1222 1223 /** 1224 * The following comment prevents JSLint errors concerning undefined global variables. 1225 * It tells JSLint that these identifiers are defined elsewhere. 1226 */ 1227 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true, plusplus: true, unparam: true, forin: true */ 1228 1229 /** The following comment is to prevent jslint errors about 1230 * using variables before they are defined. 1231 */ 1232 /*global $, _prefs,_uiMsg,ciscowidgets,dojo,finesse,gadgets,hostUrl, Handlebars */ 1233 1234 /** 1235 * A collection of utility functions. 1236 * 1237 * @requires finesse.Converter 1238 */ 1239 define('utilities/Utilities',[ 1240 "../../thirdparty/util/converter", 1241 "utilities/SaxParser", 1242 "iso8601", 1243 "Math.uuid" 1244 ], 1245 function (Converter, SaxParser) { 1246 var Utilities = /** @lends finesse.utilities.Utilities */ { 1247 1248 /** 1249 * @class 1250 * A PhoneBook is a list of Contacts available to a User for quick dial. 1251 * 1252 * @augments finesse.restservices.RestBase 1253 * @see finesse.restservices.Contacts 1254 * @constructs 1255 */ 1256 _fakeConstuctor: function () { 1257 /* This is here for jsdocs. */ 1258 }, 1259 1260 /** 1261 * @private 1262 * Retrieves the specified item from window.localStorage 1263 * @param {String} key 1264 * The key of the item to retrieve 1265 * @returns {String} 1266 * The string with the value of the retrieved item; returns 1267 * what the browser would return if not found (typically null or undefined) 1268 * Returns false if window.localStorage feature is not even found. 1269 */ 1270 getDOMStoreItem: function (key) { 1271 var store = window.localStorage; 1272 if (store) { 1273 return store.getItem(key); 1274 } 1275 }, 1276 1277 /** 1278 * @private 1279 * Sets an item into window.localStorage 1280 * @param {String} key 1281 * The key for the item to set 1282 * @param {String} value 1283 * The value to set 1284 * @returns {Boolean} 1285 * True if successful, false if window.localStorage is 1286 * not even found. 1287 */ 1288 setDOMStoreItem: function (key, value) { 1289 var store = window.localStorage; 1290 if (store) { 1291 store.setItem(key, value); 1292 return true; 1293 } 1294 return false; 1295 }, 1296 1297 /** 1298 * @private 1299 * Removes a particular item from window.localStorage 1300 * @param {String} key 1301 * The key of the item to remove 1302 * @returns {Boolean} 1303 * True if successful, false if not 1304 * Returns false if window.localStorage feature is not even found. 1305 */ 1306 removeDOMStoreItem: function (key) { 1307 var store = window.localStorage; 1308 if (store) { 1309 store.removeItem(key); 1310 return true; 1311 } 1312 return false; 1313 }, 1314 1315 /** 1316 * @private 1317 * Dumps all the contents of window.localStorage 1318 * @returns {Boolean} 1319 * True if successful, false if not. 1320 * Returns false if window.localStorage feature is not even found. 1321 */ 1322 clearDOMStore: function () { 1323 var store = window.localStorage; 1324 if (store) { 1325 store.clear(); 1326 return true; 1327 } 1328 return false; 1329 }, 1330 1331 /** 1332 * @private 1333 * Creates a message listener for window.postMessage messages. 1334 * @param {Function} callback 1335 * The callback that will be invoked with the message. The callback 1336 * is responsible for any security checks. 1337 * @param {String} [origin] 1338 * The origin to check against for security. Allows all messages 1339 * if no origin is provided. 1340 * @returns {Function} 1341 * The callback function used to register with the message listener. 1342 * This is different than the one provided as a parameter because 1343 * the function is overloaded with origin checks. 1344 * @throws {Error} If the callback provided is not a function. 1345 */ 1346 receiveMessage: function (callback, origin) { 1347 if (typeof callback !== "function") { 1348 throw new Error("Callback is not a function."); 1349 } 1350 1351 //Create a function closure to perform origin check. 1352 /** @private */ 1353 var cb = function (e) { 1354 // If an origin check is requested (provided), we'll only invoke the callback if it passes 1355 if (typeof origin !== "string" || (typeof origin === "string" && typeof e.origin === "string" && e.origin.toLowerCase() === origin.toLowerCase())) { 1356 callback(e); 1357 } 1358 }; 1359 1360 if (window.addEventListener) { //Firefox, Opera, Chrome, Safari 1361 window.addEventListener("message", cb, false); 1362 } else { //Internet Explorer 1363 window.attachEvent("onmessage", cb); 1364 } 1365 1366 //Return callback used to register with listener so that invoker 1367 //could use it to remove. 1368 return cb; 1369 }, 1370 1371 /** 1372 * @private 1373 * Sends a message to a target frame using window.postMessage. 1374 * @param {Function} message 1375 * Message to be sent to target frame. 1376 * @param {Object} [target="parent"] 1377 * An object reference to the target frame. Default us the parent. 1378 * @param {String} [targetOrigin="*"] 1379 * The URL of the frame this frame is sending the message to. 1380 */ 1381 sendMessage: function (message, target, targetOrigin) { 1382 //Default to any target URL if none is specified. 1383 targetOrigin = targetOrigin || "*"; 1384 1385 //Default to parent target if none is specified. 1386 target = target || parent; 1387 1388 //Ensure postMessage is supported by browser before invoking. 1389 if (window.postMessage) { 1390 target.postMessage(message, targetOrigin); 1391 } 1392 }, 1393 1394 /** 1395 * Returns the passed in handler, if it is a function. 1396 * @param {Function} handler 1397 * The handler to validate 1398 * @returns {Function} 1399 * The provided handler if it is valid 1400 * @throws Error 1401 * If the handler provided is invalid 1402 */ 1403 validateHandler: function (handler) { 1404 if (handler === undefined || typeof handler === "function") { 1405 return handler; 1406 } else { 1407 throw new Error("handler must be a function"); 1408 } 1409 }, 1410 1411 /** 1412 * @private 1413 * Tries to get extract the AWS error code from a 1414 * finesse.clientservices.ClientServices parsed error response object. 1415 * @param {Object} rsp 1416 * The handler to validate 1417 * @returns {String} 1418 * The error code, HTTP status code, or undefined 1419 */ 1420 getErrCode: function (rsp) { 1421 try { // Best effort to get the error code 1422 return rsp.object.ApiErrors.ApiError.ErrorType; 1423 } catch (e) { // Second best effort to get the HTTP Status code 1424 if (rsp && rsp.status) { 1425 return "HTTP " + rsp.status; 1426 } 1427 } // Otherwise, don't return anything (undefined) 1428 }, 1429 1430 /** 1431 * @private 1432 * Tries to get extract the AWS error data from a 1433 * finesse.clientservices.ClientServices parsed error response object. 1434 * @param {Object} rsp 1435 * The handler to validate 1436 * @returns {String} 1437 * The error data, HTTP status code, or undefined 1438 */ 1439 getErrData: function (rsp) { 1440 try { // Best effort to get the error data 1441 return rsp.object.ApiErrors.ApiError.ErrorData; 1442 } catch (e) { // Second best effort to get the HTTP Status code 1443 if (rsp && rsp.status) { 1444 return "HTTP " + rsp.status; 1445 } 1446 } // Otherwise, don't return anything (undefined) 1447 }, 1448 1449 /** 1450 * @private 1451 * Tries to get extract the AWS overrideable boolean from a 1452 * finesse.clientservices.ClientServices parsed error response object. 1453 * @param {Object} rsp 1454 * The handler to validate 1455 * @returns {String} 1456 * The overrideable boolean, HTTP status code, or undefined 1457 */ 1458 getErrOverrideable: function (rsp) { 1459 try { // Best effort to get the override boolean 1460 return rsp.object.ApiErrors.ApiError.Overrideable; 1461 } catch (e) { // Second best effort to get the HTTP Status code 1462 if (rsp && rsp.status) { 1463 return "HTTP " + rsp.status; 1464 } 1465 } // Otherwise, don't return anything (undefined) 1466 }, 1467 1468 /** 1469 * Trims leading and trailing whitespace from a string. 1470 * @param {String} str 1471 * The string to trim 1472 * @returns {String} 1473 * The trimmed string 1474 */ 1475 trim: function (str) { 1476 return str.replace(/^\s*/, "").replace(/\s*$/, ""); 1477 }, 1478 1479 /** 1480 * Utility method for getting the current time in milliseconds. 1481 * @returns {String} 1482 * The current time in milliseconds 1483 */ 1484 currentTimeMillis : function () { 1485 return (new Date()).getTime(); 1486 }, 1487 1488 /** 1489 * Gets the current drift (between client and server) 1490 * 1491 * @returns {integer} which is the current drift (last calculated; 0 if we have not calculated yet) 1492 */ 1493 getCurrentDrift : function () { 1494 var drift; 1495 1496 //Get the current client drift from localStorage 1497 drift = window.sessionStorage.getItem("clientTimestampDrift"); 1498 if (drift) { 1499 drift = parseInt(drift, 10); 1500 if (isNaN(drift)) { 1501 drift = 0; 1502 } 1503 } 1504 return drift; 1505 }, 1506 1507 /** 1508 * Converts the specified clientTime to server time by adjusting by the current drift. 1509 * 1510 * @param clientTime is the time in milliseconds 1511 * @returns {int} serverTime in milliseconds 1512 */ 1513 convertToServerTimeMillis : function(clientTime) { 1514 var drift = this.getCurrentDrift(); 1515 return (clientTime + drift); 1516 }, 1517 1518 /** 1519 * Utility method for getting the current time, 1520 * adjusted by the calculated "drift" to closely 1521 * approximate the server time. This is used 1522 * when calculating durations based on a server 1523 * timestamp, which otherwise can produce unexpected 1524 * results if the times on client and server are 1525 * off. 1526 * 1527 * @returns {String} 1528 * The current server time in milliseconds 1529 */ 1530 currentServerTimeMillis : function () { 1531 var drift = this.getCurrentDrift(); 1532 return (new Date()).getTime() + drift; 1533 }, 1534 1535 /** 1536 * Given a specified timeInMs, this method will builds a string which displays minutes and seconds. 1537 * 1538 * @param timeInMs is the time in milliseconds 1539 * @returns {String} which corresponds to minutes and seconds (e.g. 11:23) 1540 */ 1541 buildTimeString : function (timeInMs) { 1542 var min, sec, timeStr = "00:00"; 1543 1544 if (timeInMs && timeInMs !== "-1") { 1545 // calculate minutes, and seconds 1546 min = this.pad(Math.floor(timeInMs / 60000)); 1547 sec = this.pad(Math.floor((timeInMs % 60000) / 1000)); 1548 1549 // construct MM:SS time string 1550 timeStr = min + ":" + sec; 1551 } 1552 return timeStr; 1553 }, 1554 1555 /** 1556 * Given a specified timeInMs, this method will builds a string which displays minutes and seconds (and optionally hours) 1557 * 1558 * @param timeInMs is the time in milliseconds 1559 * @returns {String} which corresponds to hours, minutes and seconds (e.g. 01:11:23 or 11:23) 1560 */ 1561 buildTimeStringWithOptionalHours: function (timeInMs) { 1562 var hour, min, sec, timeStr = "00:00", optionalHour = "", timeInSecs; 1563 1564 if (timeInMs && timeInMs !== "-1") { 1565 timeInSecs = timeInMs / 1000; 1566 1567 // calculate {hours}, minutes, and seconds 1568 hour = this.pad(Math.floor(timeInSecs / 3600)); 1569 min = this.pad(Math.floor((timeInSecs % 3600) / 60)); 1570 sec = this.pad(Math.floor((timeInSecs % 3600) % 60)); 1571 1572 //Optionally add the hour if we have hours 1573 if (hour > 0) { 1574 optionalHour = hour + ":"; 1575 } 1576 1577 // construct MM:SS time string (or optionally HH:MM:SS) 1578 timeStr = optionalHour + min + ":" + sec; 1579 } 1580 return timeStr; 1581 }, 1582 1583 1584 /** 1585 * Builds a string which specifies the amount of time user has been in this state (e.g. 11:23). 1586 * 1587 * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift) 1588 * @param stateStartTimeInMs is integer argument which specifies time call entered current state 1589 * @returns {String} which is the elapsed time (MINUTES:SECONDS) 1590 * 1591 */ 1592 buildElapsedTimeString : function (adjustedServerTimeInMs, stateStartTimeInMs) { 1593 var result, delta; 1594 1595 result = "--:--"; 1596 if (stateStartTimeInMs !== 0) { 1597 delta = adjustedServerTimeInMs - stateStartTimeInMs; 1598 1599 if (delta > 0) { 1600 result = this.buildTimeString(delta); 1601 } 1602 } 1603 return result; 1604 }, 1605 1606 /** 1607 * 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). 1608 * 1609 * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift) 1610 * @param startTimeInMs is integer argument which specifies the start time 1611 * @returns {String} which is the elapsed time (MINUTES:SECONDS) or (HOURS:MINUTES:SECONDS) 1612 * 1613 */ 1614 buildElapsedTimeStringWithOptionalHours : function (adjustedServerTimeInMs, stateStartTimeInMs) { 1615 var result, delta; 1616 1617 result = "--:--"; 1618 if (stateStartTimeInMs !== 0) { 1619 delta = adjustedServerTimeInMs - stateStartTimeInMs; 1620 1621 if (delta > 0) { 1622 result = this.buildTimeStringWithOptionalHours(delta); 1623 } 1624 } 1625 return result; 1626 }, 1627 1628 1629 /** 1630 * Builds a string which displays the total call time in minutes and seconds. 1631 * 1632 * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift) 1633 * @param callStartTimeInMs is integer argument which specifies time the call started 1634 * @returns {String} which is the elapsed time [MINUTES:SECONDS] 1635 */ 1636 buildTotalTimeString : function (adjustedServerTimeInMs, callStartTimeInMs) { 1637 return this.buildElapsedTimeString(adjustedServerTimeInMs, callStartTimeInMs); 1638 }, 1639 1640 /** 1641 * Builds a string which displays the hold time in minutes and seconds. 1642 * 1643 * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift) 1644 * @param holdStartTimeInMs is integer argument which specifies time the hold started 1645 * @returns {String} which is the elapsed time [MINUTES:SECONDS] 1646 */ 1647 buildHoldTimeString : function (adjustedServerTimeInMs, holdStartTimeInMs) { 1648 return this.buildElapsedTimeString(adjustedServerTimeInMs, holdStartTimeInMs); 1649 }, 1650 1651 /** 1652 * Builds a string which displays the elapsed time the call has been in wrap up. 1653 * 1654 * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift) 1655 * @param wrapupStartTimeInMs is integer argument which specifies time call entered wrapup state 1656 * @returns {String} which is the elapsed wrapup time 1657 * 1658 */ 1659 buildWrapupTimeString : function (adjustedServerTimeInMs, wrapupStartTimeInMs) { 1660 return this.buildElapsedTimeString(adjustedServerTimeInMs, wrapupStartTimeInMs); 1661 }, 1662 1663 /** 1664 * Extracts a time from the timeStr. Note: The timeStr could be empty. In this case, the time returned will be 0. 1665 * @param timeStr is a time string in ISO8601 format (note: could be empty) 1666 * @returns {long} is the time 1667 */ 1668 extractTime : function (timeStr) { 1669 var result = 0, theDate; 1670 if (timeStr === "") { 1671 result = 0; 1672 } else if (timeStr === null) { 1673 result = 0; 1674 } else { 1675 theDate = this.parseDateStringISO8601(timeStr); 1676 result = theDate.getTime(); 1677 } 1678 return result; 1679 }, 1680 1681 /** 1682 * @private 1683 * Generates an RFC1422v4-compliant UUID using pesudorandom numbers. 1684 * @returns {String} 1685 * An RFC1422v4-compliant UUID using pesudorandom numbers. 1686 **/ 1687 generateUUID: function () { 1688 return Math.uuidCompact(); 1689 }, 1690 1691 /** @private */ 1692 xml2json: finesse.Converter.xml2json, 1693 1694 1695 /** 1696 * @private 1697 * Utility method to get the JSON parser either from gadgets.json 1698 * or from window.JSON (which will be initialized by CUIC if 1699 * browser doesn't support 1700 */ 1701 getJSONParser: function() { 1702 var _container = window.gadgets || {}, 1703 parser = _container.json || window.JSON; 1704 return parser; 1705 }, 1706 1707 /** 1708 * @private 1709 * Utility method to convert a javascript object to XML. 1710 * @param {Object} object 1711 * The object to convert to XML. 1712 * @param {Boolean} escapeFlag 1713 * If escapeFlag evaluates to true: 1714 * - XML escaping is done on the element values. 1715 * - Attributes, #cdata, and #text is not supported. 1716 * - The XML is unformatted (no whitespace between elements). 1717 * If escapeFlag evaluates to false: 1718 * - Element values are written 'as is' (no escaping). 1719 * - Attributes, #cdata, and #text is supported. 1720 * - The XML is formatted. 1721 * @returns The XML string. 1722 */ 1723 json2xml: function (object, escapeFlag) { 1724 var xml; 1725 if (escapeFlag) { 1726 xml = this._json2xmlWithEscape(object); 1727 } 1728 else { 1729 xml = finesse.Converter.json2xml(object, '\t'); 1730 } 1731 return xml; 1732 }, 1733 1734 /** 1735 * @private 1736 * Utility method to convert XML string into javascript object. 1737 */ 1738 xml2JsObj : function (event) { 1739 var parser = this.getJSONParser(); 1740 return parser.parse(finesse.Converter.xml2json(jQuery.parseXML(event), "")); 1741 }, 1742 1743 /** 1744 * @private 1745 * Utility method to convert an XML string to a javascript object. 1746 * @desc This function calls to the SAX parser and responds to callbacks 1747 * received from the parser. Entity translation is not handled here. 1748 * @param {String} xml 1749 * The XML to parse. 1750 * @returns The javascript object. 1751 */ 1752 xml2js: function (xml) { 1753 var STATES = { 1754 INVALID: 0, 1755 NEW_NODE: 1, 1756 ATTRIBUTE_NODE: 2, 1757 TEXT_NODE: 3, 1758 END_NODE: 4 1759 }, 1760 state = STATES.INVALID, 1761 rootObj = {}, 1762 newObj, 1763 objStack = [rootObj], 1764 nodeName = "", 1765 1766 /** 1767 * @private 1768 * Adds a property to the current top JSO. 1769 * @desc This is also where we make considerations for arrays. 1770 * @param {String} name 1771 * The name of the property to add. 1772 * @param (String) value 1773 * The value of the property to add. 1774 */ 1775 addProperty = function (name, value) { 1776 var current = objStack[objStack.length - 1]; 1777 if(current.hasOwnProperty(name) && current[name] instanceof Array){ 1778 current[name].push(value); 1779 }else if(current.hasOwnProperty(name)){ 1780 current[name] = [current[name], value]; 1781 }else{ 1782 current[name] = value; 1783 } 1784 }, 1785 1786 /** 1787 * @private 1788 * The callback passed to the SAX parser which processes events from 1789 * the SAX parser in order to construct the resulting JSO. 1790 * @param (String) type 1791 * The type of event received. 1792 * @param (String) data 1793 * The data received from the SAX parser. The contents of this 1794 * parameter vary based on the type of event. 1795 */ 1796 xmlFound = function (type, data) { 1797 switch (type) { 1798 case "StartElement": 1799 // Because different node types have different expectations 1800 // of parenting, we don't push another JSO until we know 1801 // what content we're getting 1802 1803 // If we're already in the new node state, we're running 1804 // into a child node. There won't be any text here, so 1805 // create another JSO 1806 if(state === STATES.NEW_NODE){ 1807 newObj = {}; 1808 addProperty(nodeName, newObj); 1809 objStack.push(newObj); 1810 } 1811 state = STATES.NEW_NODE; 1812 nodeName = data; 1813 break; 1814 case "EndElement": 1815 // If we're in the new node state, we've found no content 1816 // set the tag property to null 1817 if(state === STATES.NEW_NODE){ 1818 addProperty(nodeName, null); 1819 }else if(state === STATES.END_NODE){ 1820 objStack.pop(); 1821 } 1822 state = STATES.END_NODE; 1823 break; 1824 case "Attribute": 1825 // If were in the new node state, no JSO has yet been created 1826 // for this node, create one 1827 if(state === STATES.NEW_NODE){ 1828 newObj = {}; 1829 addProperty(nodeName, newObj); 1830 objStack.push(newObj); 1831 } 1832 // Attributes are differentiated from child elements by a 1833 // preceding "@" in the property name 1834 addProperty("@" + data.name, data.value); 1835 state = STATES.ATTRIBUTE_NODE; 1836 break; 1837 case "Text": 1838 // In order to maintain backwards compatibility, when no 1839 // attributes are assigned to a tag, its text contents are 1840 // assigned directly to the tag property instead of a JSO. 1841 1842 // If we're in the attribute node state, then the JSO for 1843 // this tag was already created when the attribute was 1844 // assigned, differentiate this property from a child 1845 // element by naming it "#text" 1846 if(state === STATES.ATTRIBUTE_NODE){ 1847 addProperty("#text", data); 1848 }else{ 1849 addProperty(nodeName, data); 1850 } 1851 state = STATES.TEXT_NODE; 1852 break; 1853 } 1854 }; 1855 SaxParser.parse(xml, xmlFound); 1856 return rootObj; 1857 }, 1858 1859 /** 1860 * @private 1861 * Traverses a plain-old-javascript-object recursively and outputs its XML representation. 1862 * @param {Object} obj 1863 * The javascript object to be converted into XML. 1864 * @returns {String} The XML representation of the object. 1865 */ 1866 js2xml: function (obj) { 1867 var xml = "", i, elem; 1868 1869 if (obj !== null) { 1870 if (obj.constructor === Object) { 1871 for (elem in obj) { 1872 if (obj[elem] === null || typeof(obj[elem]) === 'undefined') { 1873 xml += '<' + elem + '/>'; 1874 } else if (obj[elem].constructor === Array) { 1875 for (i = 0; i < obj[elem].length; i++) { 1876 xml += '<' + elem + '>' + this.js2xml(obj[elem][i]) + '</' + elem + '>'; 1877 } 1878 } else if (elem[0] !== '@') { 1879 if (this.js2xmlObjIsEmpty(obj[elem])) { 1880 xml += '<' + elem + this.js2xmlAtt(obj[elem]) + '/>'; 1881 } else if (elem === "#text") { 1882 xml += obj[elem]; 1883 } else { 1884 xml += '<' + elem + this.js2xmlAtt(obj[elem]) + '>' + this.js2xml(obj[elem]) + '</' + elem + '>'; 1885 } 1886 } 1887 } 1888 } else { 1889 xml = obj; 1890 } 1891 } 1892 1893 return xml; 1894 }, 1895 1896 /** 1897 * @private 1898 * Utility method called exclusively by js2xml() to find xml attributes. 1899 * @desc Traverses children one layer deep of a javascript object to "look ahead" 1900 * for properties flagged as such (with '@'). 1901 * @param {Object} obj 1902 * The obj to traverse. 1903 * @returns {String} Any attributes formatted for xml, if any. 1904 */ 1905 js2xmlAtt: function (obj) { 1906 var elem; 1907 1908 if (obj !== null) { 1909 if (obj.constructor === Object) { 1910 for (elem in obj) { 1911 if (obj[elem] !== null && typeof(obj[elem]) !== "undefined" && obj[elem].constructor !== Array) { 1912 if (elem[0] === '@'){ 1913 return ' ' + elem.substring(1) + '="' + obj[elem] + '"'; 1914 } 1915 } 1916 } 1917 } 1918 } 1919 1920 return ''; 1921 }, 1922 1923 /** 1924 * @private 1925 * Utility method called exclusively by js2xml() to determine if 1926 * a node has any children, with special logic for ignoring attributes. 1927 * @desc Attempts to traverse the elements in the object while ignoring attributes. 1928 * @param {Object} obj 1929 * The obj to traverse. 1930 * @returns {Boolean} whether or not the JS object is "empty" 1931 */ 1932 js2xmlObjIsEmpty: function (obj) { 1933 var elem; 1934 1935 if (obj !== null) { 1936 if (obj.constructor === Object) { 1937 for (elem in obj) { 1938 if (obj[elem] !== null) { 1939 if (obj[elem].constructor === Array){ 1940 return false; 1941 } 1942 1943 if (elem[0] !== '@'){ 1944 return false; 1945 } 1946 } else { 1947 return false; 1948 } 1949 } 1950 } else { 1951 return false; 1952 } 1953 } 1954 1955 return true; 1956 }, 1957 1958 /** 1959 * Encodes the given string into base64. 1960 *<br> 1961 * <b>NOTE:</b> {input} is assumed to be UTF-8; only the first 1962 * 8 bits of each input element are significant. 1963 * 1964 * @param {String} input 1965 * The string to convert to base64. 1966 * @returns {String} 1967 * The converted string. 1968 */ 1969 b64Encode: function (input) { 1970 var output = "", idx, data, 1971 table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 1972 1973 for (idx = 0; idx < input.length; idx += 3) { 1974 data = input.charCodeAt(idx) << 16 | 1975 input.charCodeAt(idx + 1) << 8 | 1976 input.charCodeAt(idx + 2); 1977 1978 //assume the first 12 bits are valid 1979 output += table.charAt((data >>> 18) & 0x003f) + 1980 table.charAt((data >>> 12) & 0x003f); 1981 output += ((idx + 1) < input.length) ? 1982 table.charAt((data >>> 6) & 0x003f) : 1983 "="; 1984 output += ((idx + 2) < input.length) ? 1985 table.charAt(data & 0x003f) : 1986 "="; 1987 } 1988 1989 return output; 1990 }, 1991 1992 /** 1993 * Decodes the given base64 string. 1994 * <br> 1995 * <b>NOTE:</b> output is assumed to be UTF-8; only the first 1996 * 8 bits of each output element are significant. 1997 * 1998 * @param {String} input 1999 * The base64 encoded string 2000 * @returns {String} 2001 * Decoded string 2002 */ 2003 b64Decode: function (input) { 2004 var output = "", idx, h, data, 2005 table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 2006 2007 for (idx = 0; idx < input.length; idx += 4) { 2008 h = [ 2009 table.indexOf(input.charAt(idx)), 2010 table.indexOf(input.charAt(idx + 1)), 2011 table.indexOf(input.charAt(idx + 2)), 2012 table.indexOf(input.charAt(idx + 3)) 2013 ]; 2014 2015 data = (h[0] << 18) | (h[1] << 12) | (h[2] << 6) | h[3]; 2016 if (input.charAt(idx + 2) === '=') { 2017 data = String.fromCharCode( 2018 (data >>> 16) & 0x00ff 2019 ); 2020 } else if (input.charAt(idx + 3) === '=') { 2021 data = String.fromCharCode( 2022 (data >>> 16) & 0x00ff, 2023 (data >>> 8) & 0x00ff 2024 ); 2025 } else { 2026 data = String.fromCharCode( 2027 (data >>> 16) & 0x00ff, 2028 (data >>> 8) & 0x00ff, 2029 data & 0x00ff 2030 ); 2031 } 2032 output += data; 2033 } 2034 2035 return output; 2036 }, 2037 2038 /** 2039 * @private 2040 * Extracts the username and the password from the Base64 encoded string. 2041 * @params {String} 2042 * A base64 encoded string containing credentials that (when decoded) 2043 * are colon delimited. 2044 * @returns {Object} 2045 * An object with the following structure: 2046 * {id:string, password:string} 2047 */ 2048 getCredentials: function (authorization) { 2049 var credObj = {}, 2050 credStr = this.b64Decode(authorization), 2051 colonIndx = credStr.indexOf(":"); 2052 2053 //Check to ensure that string is colon delimited. 2054 if (colonIndx === -1) { 2055 throw new Error("String is not colon delimited."); 2056 } 2057 2058 //Extract ID and password. 2059 credObj.id = credStr.substring(0, colonIndx); 2060 credObj.password = credStr.substring(colonIndx + 1); 2061 return credObj; 2062 }, 2063 2064 /** 2065 * Takes a string and removes any spaces within the string. 2066 * @param {String} string 2067 * The string to remove spaces from 2068 * @returns {String} 2069 * The string without spaces 2070 */ 2071 removeSpaces: function (string) { 2072 return string.split(' ').join(''); 2073 }, 2074 2075 /** 2076 * Escapes spaces as encoded " " characters so they can 2077 * be safely rendered by jQuery.text(string) in all browsers. 2078 * 2079 * (Although IE behaves as expected, Firefox collapses spaces if this function is not used.) 2080 * 2081 * @param text 2082 * The string whose spaces should be escaped 2083 * 2084 * @returns 2085 * The string with spaces escaped 2086 */ 2087 escapeSpaces: function (string) { 2088 return string.replace(/\s/g, '\u00a0'); 2089 }, 2090 2091 /** 2092 * Adds a span styled to line break at word edges around the string passed in. 2093 * @param str String to be wrapped in word-breaking style. 2094 * @private 2095 */ 2096 addWordWrapping : function (str) { 2097 return '<span style="word-wrap: break-word;">' + str + '</span>'; 2098 }, 2099 2100 /** 2101 * Takes an Object and determines whether it is an Array or not. 2102 * @param {Object} obj 2103 * The Object in question 2104 * @returns {Boolean} 2105 * true if the object is an Array, else false. 2106 */ 2107 isArray: function (obj) { 2108 return obj.constructor.toString().indexOf("Array") !== -1; 2109 }, 2110 2111 /** 2112 * @private 2113 * Takes a data object and returns an array extracted 2114 * @param {Object} data 2115 * JSON payload 2116 * 2117 * @returns {array} 2118 * extracted array 2119 */ 2120 getArray: function (data) { 2121 if (this.isArray(data)) { 2122 //Return if already an array. 2123 return data; 2124 } else { 2125 //Create an array, iterate through object, and push to array. This 2126 //should only occur with one object, and therefore one obj in array. 2127 var arr = []; 2128 arr.push(data); 2129 return arr; 2130 } 2131 }, 2132 2133 /** 2134 * @private 2135 * Extracts the ID for an entity given the Finesse REST URI. The ID is 2136 * assumed to be the last element in the URI (after the last "/"). 2137 * @param {String} uri 2138 * The Finesse REST URI to extract the ID from. 2139 * @returns {String} 2140 * The ID extracted from the REST URI. 2141 */ 2142 getId: function (uri) { 2143 if (!uri) { 2144 return ""; 2145 } 2146 var strLoc = uri.lastIndexOf("/"); 2147 return uri.slice(strLoc + 1); 2148 }, 2149 2150 /** 2151 * Compares two objects for equality. 2152 * @param {Object} obj1 2153 * First of two objects to compare. 2154 * @param {Object} obj2 2155 * Second of two objects to compare. 2156 */ 2157 getEquals: function (objA, objB) { 2158 var key; 2159 2160 for (key in objA) { 2161 if (objA.hasOwnProperty(key)) { 2162 if (!objA[key]) { 2163 objA[key] = ""; 2164 } 2165 2166 if (typeof objB[key] === 'undefined') { 2167 return false; 2168 } 2169 if (typeof objB[key] === 'object') { 2170 if (!objB[key].equals(objA[key])) { 2171 return false; 2172 } 2173 } 2174 if (objB[key] !== objA[key]) { 2175 return false; 2176 } 2177 } 2178 } 2179 return true; 2180 }, 2181 2182 /** 2183 * Regular expressions used in translating HTML and XML entities 2184 */ 2185 ampRegEx : new RegExp('&', 'gi'), 2186 ampEntityRefRegEx : new RegExp('&', 'gi'), 2187 ltRegEx : new RegExp('<', 'gi'), 2188 ltEntityRefRegEx : new RegExp('<', 'gi'), 2189 gtRegEx : new RegExp('>', 'gi'), 2190 gtEntityRefRegEx : new RegExp('>', 'gi'), 2191 xmlSpecialCharRegEx: new RegExp('[&<>"\']', 'g'), 2192 entityRefRegEx: new RegExp('&[^;]+(?:;|$)', 'g'), 2193 2194 /** 2195 * Translates between special characters and HTML entities 2196 * 2197 * @param text 2198 * The text to translate 2199 * 2200 * @param makeEntityRefs 2201 * If true, encode special characters as HTML entities; if 2202 * false, decode HTML entities back to special characters 2203 * 2204 * @private 2205 */ 2206 translateHTMLEntities: function (text, makeEntityRefs) { 2207 if (typeof(text) !== "undefined" && text !== null && text !== "") { 2208 if (makeEntityRefs) { 2209 text = text.replace(this.ampRegEx, '&'); 2210 text = text.replace(this.ltRegEx, '<'); 2211 text = text.replace(this.gtRegEx, '>'); 2212 } else { 2213 text = text.replace(this.gtEntityRefRegEx, '>'); 2214 text = text.replace(this.ltEntityRefRegEx, '<'); 2215 text = text.replace(this.ampEntityRefRegEx, '&'); 2216 } 2217 } 2218 2219 return text; 2220 }, 2221 2222 /** 2223 * Translates between special characters and XML entities 2224 * 2225 * @param text 2226 * The text to translate 2227 * 2228 * @param makeEntityRefs 2229 * If true, encode special characters as XML entities; if 2230 * false, decode XML entities back to special characters 2231 * 2232 * @private 2233 */ 2234 translateXMLEntities: function (text, makeEntityRefs) { 2235 /** @private */ 2236 var escape = function (character) { 2237 switch (character) { 2238 case "&": 2239 return "&"; 2240 case "<": 2241 return "<"; 2242 case ">": 2243 return ">"; 2244 case "'": 2245 return "'"; 2246 case "\"": 2247 return """; 2248 default: 2249 return character; 2250 } 2251 }, 2252 /** @private */ 2253 unescape = function (entity) { 2254 switch (entity) { 2255 case "&": 2256 return "&"; 2257 case "<": 2258 return "<"; 2259 case ">": 2260 return ">"; 2261 case "'": 2262 return "'"; 2263 case """: 2264 return "\""; 2265 default: 2266 if (entity.charAt(1) === "#" && entity.charAt(entity.length - 1) === ";") { 2267 if (entity.charAt(2) === "x") { 2268 return String.fromCharCode(parseInt(entity.slice(3, -1), 16)); 2269 } else { 2270 return String.fromCharCode(parseInt(entity.slice(2, -1), 10)); 2271 } 2272 } else { 2273 throw new Error("Invalid XML entity: " + entity); 2274 } 2275 } 2276 }; 2277 2278 if (typeof(text) !== "undefined" && text !== null && text !== "") { 2279 if (makeEntityRefs) { 2280 text = text.replace(this.xmlSpecialCharRegEx, escape); 2281 } else { 2282 text = text.replace(this.entityRefRegEx, unescape); 2283 } 2284 } 2285 2286 return text; 2287 }, 2288 2289 /** 2290 * @private 2291 * Utility method to pad the number with a leading 0 for single digits 2292 * @param (Number) num 2293 * the number to pad 2294 */ 2295 pad : function (num) { 2296 if (num < 10) { 2297 return "0" + num; 2298 } 2299 2300 return String(num); 2301 }, 2302 2303 /** 2304 * Pad with zeros based on a padWidth. 2305 * 2306 * @param num 2307 * @param padWidth 2308 * @returns {String} with padded zeros (based on padWidth) 2309 */ 2310 padWithWidth : function (num, padWidth) { 2311 var value, index, result; 2312 2313 result = ""; 2314 for(index=padWidth;index>1;index--) 2315 { 2316 value = Math.pow(10, index-1); 2317 2318 if (num < value) { 2319 result = result + "0"; 2320 } 2321 } 2322 result = result + num; 2323 2324 return String(result); 2325 }, 2326 2327 /** 2328 * Converts a date to an ISO date string. 2329 * 2330 * @param aDate 2331 * @returns {String} in ISO date format 2332 * 2333 * Note: Some browsers don't support this method (e.g. IE8). 2334 */ 2335 convertDateToISODateString : function (aDate) { 2336 var result; 2337 2338 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"; 2339 return result; 2340 }, 2341 2342 /** 2343 * Get the date in ISO date format. 2344 * 2345 * @param aDate is the date 2346 * @returns {String} date in ISO format 2347 * 2348 * Note: see convertDateToISODateString() above. 2349 */ 2350 dateToISOString : function (aDate) { 2351 var result; 2352 2353 try { 2354 result = aDate.toISOString(); 2355 } catch (e) { 2356 result = this.convertDateToISODateString(aDate); 2357 } 2358 return result; 2359 }, 2360 2361 /** 2362 * Parse string (which is formated as ISO8601 date) into Javascript Date object. 2363 * 2364 * @param s ISO8601 string 2365 * @return {Date} 2366 * Note: Some browsers don't support Date constructor which take ISO8601 date (e.g. IE 8). 2367 */ 2368 parseDateStringISO8601 : function (s) { 2369 var i, re = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:.(\d+))?(Z|[+\-]\d{2})(?::(\d{2}))?/, 2370 d = s.match(re); 2371 if( !d ) { 2372 return null; 2373 } 2374 for( i in d ) { 2375 d[i] = ~~d[i]; 2376 } 2377 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); 2378 }, 2379 2380 /** 2381 * Utility method to render a timestamp value (in seconds) into HH:MM:SS format. 2382 * @param {Number} time 2383 * The timestamp in ms to render 2384 * @returns {String} 2385 * Time string in HH:MM:SS format. 2386 */ 2387 getDisplayTime : function (time) { 2388 var hour, min, sec, timeStr = "00:00:00"; 2389 2390 if (time && time !== "-1") { 2391 // calculate hours, minutes, and seconds 2392 hour = this.pad(Math.floor(time / 3600)); 2393 min = this.pad(Math.floor((time % 3600) / 60)); 2394 sec = this.pad(Math.floor((time % 3600) % 60)); 2395 // construct HH:MM:SS time string 2396 timeStr = hour + ":" + min + ":" + sec; 2397 } 2398 2399 return timeStr; 2400 }, 2401 2402 /** 2403 * Checks if the string is null. If it is, return empty string; else return 2404 * the string itself. 2405 * 2406 * @param {String} str 2407 * The string to check 2408 * @return {String} 2409 * Empty string or string itself 2410 */ 2411 convertNullToEmptyString : function (str) { 2412 return str || ""; 2413 }, 2414 2415 /** 2416 * Utility method to render a timestamp string (of format 2417 * YYYY-MM-DDTHH:MM:SSZ) into a duration of HH:MM:SS format. 2418 * 2419 * @param {String} timestamp 2420 * The timestamp to render 2421 * @param {Date} [now] 2422 * Optional argument to provide the time from which to 2423 * calculate the duration instead of using the current time 2424 * @returns {String} 2425 * Duration string in HH:MM:SS format. 2426 */ 2427 convertTsToDuration : function (timestamp, now) { 2428 return this.convertTsToDurationWithFormat(timestamp, false, now); 2429 }, 2430 2431 /** 2432 * Utility method to render a timestamp string (of format 2433 * YYYY-MM-DDTHH:MM:SSZ) into a duration of HH:MM:SS format, 2434 * with optional -1 for null or negative times. 2435 * 2436 * @param {String} timestamp 2437 * The timestamp to render 2438 * @param {Boolean} forFormat 2439 * If True, if duration is null or negative, return -1 so that the duration can be formated 2440 * as needed in the Gadget. 2441 * @param {Date} [now] 2442 * Optional argument to provide the time from which to 2443 * calculate the duration instead of using the current time 2444 * @returns {String} 2445 * Duration string in HH:MM:SS format. 2446 */ 2447 convertTsToDurationWithFormat : function (timestamp, forFormat, now) { 2448 var startTimeInMs, nowInMs, durationInSec = "-1"; 2449 2450 // Calculate duration 2451 if (timestamp && typeof timestamp === "string") { 2452 // first check it '--' for a msg in grid 2453 if (timestamp === '--' || timestamp ==="" || timestamp === "-1") { 2454 return "-1"; 2455 } 2456 // else try convert string into a time 2457 startTimeInMs = Date.parse(timestamp); 2458 if (!isNaN(startTimeInMs)) { 2459 if (!now || !(now instanceof Date)) { 2460 nowInMs = this.currentServerTimeMillis(); 2461 } else { 2462 nowInMs = this.convertToServerTimeMillis(now.getTime()); 2463 } 2464 durationInSec = Math.floor((nowInMs - startTimeInMs) / 1000); 2465 // Since currentServerTime is not exact (lag between sending and receiving 2466 // messages will differ slightly), treat a slightly negative (less than 1 sec) 2467 // value as 0, to avoid "--" showing up when a state first changes. 2468 if (durationInSec === -1) { 2469 durationInSec = 0; 2470 } 2471 2472 if (durationInSec < 0) { 2473 if (forFormat) { 2474 return "-1"; 2475 } else { 2476 return this.getDisplayTime("-1"); 2477 } 2478 } 2479 } 2480 }else { 2481 if(forFormat){ 2482 return "-1"; 2483 } 2484 } 2485 return this.getDisplayTime(durationInSec); 2486 }, 2487 2488 /** 2489 * Takes a string (typically from window.location) and finds the value which corresponds to a name. For 2490 * example: http://www.company.com/?param1=value1¶m2=value2 2491 * 2492 * @param str is the string to search 2493 * @param name is the name to search for 2494 */ 2495 getParameterByName : function(str, name) { 2496 var regex, results; 2497 name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); 2498 regex = new RegExp("[\\?&]" + name + "=([^]*)"); 2499 results = regex.exec(str); 2500 return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); 2501 }, 2502 2503 /** 2504 * @private 2505 * Gets the user Authorization String from the window session. 2506 * @returns the Authorization String 2507 * 2508 */ 2509 getUserAuthString: function () { 2510 var authString = window.sessionStorage.getItem('userFinesseAuth'); 2511 return authString; 2512 }, 2513 2514 /** 2515 * @private 2516 * Adds a new cookie to the page with a default domain. 2517 * @param {String} key 2518 * the key to assign a value to 2519 * @param {String} value 2520 * the value to assign to the key 2521 * @param {Number} days 2522 * number of days (from current) until the cookie should expire 2523 */ 2524 addCookie : function (key, value, days) { 2525 var date, expires = "", 2526 cookie = key + "=" + escape(value); 2527 if (typeof days === "number") { 2528 date = new Date(); 2529 date.setTime(date.getTime() + (days * 24 * 3600 * 1000)); 2530 cookie += "; expires=" + date.toGMTString(); 2531 } 2532 document.cookie = cookie + "; path=/"; 2533 }, 2534 2535 /** 2536 * @private 2537 * Get the value of a cookie given a key. 2538 * @param {String} key 2539 * a key to lookup 2540 * @returns {String} 2541 * the value mapped to a key, null if key doesn't exist 2542 */ 2543 getCookie : function (key) { 2544 var i, pairs, pair; 2545 if (document.cookie) { 2546 pairs = document.cookie.split(";"); 2547 for (i = 0; i < pairs.length; i += 1) { 2548 pair = this.trim(pairs[i]).split("="); 2549 if (pair[0] === key) { 2550 return unescape(pair[1]); 2551 } 2552 } 2553 } 2554 return null; 2555 }, 2556 2557 /** 2558 * @private 2559 * Deletes the cookie mapped to specified key. 2560 * @param {String} key 2561 * the key to delete 2562 */ 2563 deleteCookie : function (key) { 2564 this.addCookie(key, "", -1); 2565 }, 2566 2567 /** 2568 * @private 2569 * Case insensitive sort for use with arrays or Dojox stores 2570 * @param {String} a 2571 * first value 2572 * @param {String} b 2573 * second value 2574 */ 2575 caseInsensitiveSort: function (a, b) { 2576 var ret = 0, emptyString = ""; 2577 a = a + emptyString; 2578 b = b + emptyString; 2579 a = a.toLowerCase(); 2580 b = b.toLowerCase(); 2581 if (a > b) { 2582 ret = 1; 2583 } 2584 if (a < b) { 2585 ret = -1; 2586 } 2587 return ret; 2588 }, 2589 2590 /** 2591 * @private 2592 * Calls the specified function to render the dojo wijit for a gadget when the gadget first becomes visible. 2593 * 2594 * The displayWjitFunc function will be called once and only once when the div for our wijit 2595 * becomes visible for the first time. This is necessary because some dojo wijits such as the grid 2596 * throw exceptions and do not render properly if they are created in a display:none div. 2597 * If our gadget is visisble the function will be called immediately. 2598 * If our gadget is not yet visisble, then it sets a timer and waits for it to become visible. 2599 * NOTE: The timer may seem inefficent, originally I tried connecting to the tab onclick handler, but 2600 * there is a problem with dojo.connnect to an iframe's parent node in Internet Explorer. 2601 * In Firefox the click handler works OK, but it happens before the node is actually visisble, so you 2602 * end up waiting for the node to become visisble anyway. 2603 * @displayWjitFunc: A function to be called once our gadget has become visisble for th first time. 2604 */ 2605 onGadgetFirstVisible: function (displayWjitFunc) { 2606 var i, q, frameId, gadgetNbr, gadgetTitleId, panelId, panelNode, link, iterval, once = false, active = false, tabId = "#finesse-tab-selector"; 2607 try { 2608 frameId = dojo.attr(window.frameElement, "id"); // Figure out what gadget number we are by looking at our frameset 2609 gadgetNbr = frameId.match(/\d+$/)[0]; // Strip the number off the end of the frame Id, that's our gadget number 2610 gadgetTitleId = "#finesse_gadget_" + gadgetNbr + "_title"; // Create a a gadget title id from the number 2611 2612 // Loop through all of the tab panels to find one that has our gadget id 2613 dojo.query('.tab-panel', window.parent.document).some(function (node, index, arr) { 2614 q = dojo.query(gadgetTitleId, node); // Look in this panel for our gadget id 2615 if (q.length > 0) { // You found it 2616 panelNode = node; 2617 panelId = dojo.attr(panelNode, "id"); // Get panel id e.g. panel_Workgroups 2618 active = dojo.hasClass(panelNode, "active"); 2619 tabId = "#tab_" + panelId.slice(6); // Turn it into a tab id e.g.tab_Workgroups 2620 return; 2621 } 2622 }); 2623 // If panel is already active - execute the function - we're done 2624 if (active) { 2625 //?console.log(frameId + " is visible display it"); 2626 setTimeout(displayWjitFunc); 2627 } 2628 // If its not visible - wait for the active class to show up. 2629 else { 2630 //?console.log(frameId + " (" + tabId + ") is NOT active wait for it"); 2631 iterval = setInterval(dojo.hitch(this, function () { 2632 if (dojo.hasClass(panelNode, "active")) { 2633 //?console.log(frameId + " (" + tabId + ") is visible display it"); 2634 clearInterval(iterval); 2635 setTimeout(displayWjitFunc); 2636 } 2637 }), 250); 2638 } 2639 } catch (err) { 2640 //?console.log("Could not figure out what tab " + frameId + " is in: " + err); 2641 } 2642 }, 2643 2644 /** 2645 * @private 2646 * Downloads the specified url using a hidden iframe. In order to cause the browser to download rather than render 2647 * in the hidden iframe, the server code must append the header "Content-Disposition" with a value of 2648 * "attachment; filename=\"<WhateverFileNameYouWant>\"". 2649 */ 2650 downloadFile : function (url) { 2651 var iframe = document.getElementById("download_iframe"); 2652 2653 if (!iframe) 2654 { 2655 iframe = document.createElement("iframe"); 2656 $(document.body).append(iframe); 2657 $(iframe).css("display", "none"); 2658 } 2659 2660 iframe.src = url; 2661 }, 2662 2663 /** 2664 * @private 2665 * bitMask has functions for testing whether bit flags specified by integers are set in the supplied value 2666 */ 2667 bitMask: { 2668 /** @private */ 2669 isSet: function (value, mask) { 2670 return (value & mask) === mask; 2671 }, 2672 /** 2673 * Returns true if all flags in the intArray are set on the specified value 2674 * @private 2675 */ 2676 all: function (value, intArray) { 2677 var i = intArray.length; 2678 if (typeof(i) === "undefined") 2679 { 2680 intArray = [intArray]; 2681 i = 1; 2682 } 2683 while ((i = i - 1) !== -1) 2684 { 2685 if (!this.isSet(value, intArray[i])) 2686 { 2687 return false; 2688 } 2689 } 2690 return true; 2691 }, 2692 /** 2693 * @private 2694 * Returns true if any flags in the intArray are set on the specified value 2695 */ 2696 any: function (value, intArray) { 2697 var i = intArray.length; 2698 if (typeof(i) === "undefined") 2699 { 2700 intArray = [intArray]; 2701 i = 1; 2702 } 2703 while ((i = i - 1) !== -1) 2704 { 2705 if (this.isSet(value, intArray[i])) 2706 { 2707 return true; 2708 } 2709 } 2710 return false; 2711 } 2712 }, 2713 2714 /** @private */ 2715 renderDojoGridOffScreen: function (grid) { 2716 var offscreenDiv = $("<div style='position: absolute; left: -5001px; width: 5000px;'></div>")[0]; 2717 $(document.body).append(offscreenDiv); 2718 grid.placeAt(offscreenDiv); 2719 grid.startup(); 2720 document.body.removeChild(offscreenDiv); 2721 return grid; 2722 }, 2723 2724 /** @private */ 2725 initializeSearchInput: function(searchInput, changeCallback, callbackDelay, callbackScope, placeholderText) { 2726 var timerId = null, 2727 theControl = typeof(searchInput) === "string" ? $("#" + searchInput) : $(searchInput), 2728 theInputControl = theControl.find("input"), 2729 theClearButton = theControl.find("a"), 2730 inputControlWidthWithClear = 204, 2731 inputControlWidthNoClear = 230, 2732 sPreviousInput = theInputControl.val(), 2733 /** @private **/ 2734 toggleClearButton = function(){ 2735 if (theInputControl.val() === "") { 2736 theClearButton.hide(); 2737 theControl.removeClass("input-append"); 2738 theInputControl.width(inputControlWidthNoClear); 2739 } else { 2740 theInputControl.width(inputControlWidthWithClear); 2741 theClearButton.show(); 2742 theControl.addClass("input-append"); 2743 } 2744 }; 2745 2746 // set placeholder text 2747 theInputControl.attr('placeholder', placeholderText); 2748 2749 theInputControl.unbind('keyup').bind('keyup', function() { 2750 if (sPreviousInput !== theInputControl.val()) { 2751 window.clearTimeout(timerId); 2752 sPreviousInput = theInputControl.val(); 2753 timerId = window.setTimeout(function() { 2754 changeCallback.call((callbackScope || window), theInputControl.val()); 2755 theInputControl[0].focus(); 2756 }, callbackDelay); 2757 } 2758 2759 toggleClearButton(); 2760 }); 2761 2762 theClearButton.bind('click', function() { 2763 theInputControl.val(''); 2764 changeCallback.call((callbackScope || window), ''); 2765 2766 toggleClearButton(); 2767 theInputControl[0].focus(); // jquery and dojo on the same page break jquery's focus() method 2768 }); 2769 2770 theInputControl.val(""); 2771 toggleClearButton(); 2772 }, 2773 2774 DataTables: { 2775 /** @private */ 2776 createDataTable: function (options, dataTableOptions) { 2777 var grid, 2778 table = $('<table cellpadding="0" cellspacing="0" border="0" class="finesse"><thead><tr></tr></thead></table>'), 2779 headerRow = table.find("tr"), 2780 defaultOptions = { 2781 "aaData": [], 2782 "bPaginate": false, 2783 "bLengthChange": false, 2784 "bFilter": false, 2785 "bInfo": false, 2786 "sScrollY": "176", 2787 "oLanguage": { 2788 "sEmptyTable": "", 2789 "sZeroRecords": "" 2790 } 2791 }, 2792 gridOptions = $.extend({}, defaultOptions, dataTableOptions), 2793 columnDefs = [], 2794 columnFormatter; 2795 2796 // Create a header cell for each column, and set up the datatable definition for the column 2797 $(options.columns).each(function (index, column) { 2798 headerRow.append($("<th></th>")); 2799 columnDefs[index] = { 2800 "mData": column.propertyName, 2801 "sTitle": column.columnHeader, 2802 "sWidth": column.width, 2803 "aTargets": [index], 2804 "bSortable": column.sortable, 2805 "bVisible": column.visible, 2806 "mRender": column.render 2807 }; 2808 if (typeof(column.renderFunction) === "function") 2809 { 2810 /** @ignore **/ 2811 columnDefs[index].mRender = /** @ignore **/ function (value, type, dataObject) { 2812 var returnValue; 2813 2814 //Apply column render logic to value before applying extra render function 2815 if (typeof(column.render) === "function") 2816 { 2817 value = column.render.call(value, value, value); 2818 } 2819 2820 if (typeof(type) === "string") 2821 { 2822 switch (type) 2823 { 2824 case "undefined": 2825 case "sort": 2826 returnValue = value; 2827 break; 2828 case "set": 2829 throw new Error("Unsupported set data in Finesse Grid"); 2830 case "filter": 2831 case "display": 2832 case "type": 2833 returnValue = column.renderFunction.call(dataObject, value, dataObject); 2834 break; 2835 default: 2836 break; 2837 } 2838 } 2839 else 2840 { 2841 throw new Error("type param not specified in Finesse DataTable mData"); 2842 } 2843 2844 return returnValue; 2845 }; 2846 } 2847 }); 2848 gridOptions.aoColumnDefs = columnDefs; 2849 2850 // Set the height 2851 if (typeof(options.bodyHeightPixels) !== "undefined" && options.bodyHeightPixels !== null) 2852 { 2853 gridOptions.sScrollY = options.bodyHeightPixels + "px"; 2854 } 2855 2856 // Place it into the DOM 2857 if (typeof(options.container) !== "undefined" && options.container !== null) 2858 { 2859 $(options.container).append(table); 2860 } 2861 2862 // Create the DataTable 2863 table.dataTable(gridOptions); 2864 2865 return table; 2866 } 2867 }, 2868 2869 /** 2870 * @private 2871 * Sets a dojo button to the specified disable state, removing it from 2872 * the tab order if disabling, and restoring it to the tab order if enabling. 2873 * @param {Object} dojoButton Reference to the dijit.form.Button object. This is not the DOM element. 2874 * @param {bool} disabled 2875 */ 2876 setDojoButtonDisabledAttribute: function (dojoButton, disabled) { 2877 var labelNode, 2878 tabIndex; 2879 2880 dojoButton.set("disabled", disabled); 2881 2882 // Remove the tabindex attribute on disabled buttons, store it, 2883 // and replace it when it becomes enabled again 2884 labelNode = $("#" + dojoButton.id + "_label"); 2885 if (disabled) 2886 { 2887 labelNode.data("finesse:dojoButton:tabIndex", labelNode.attr("tabindex")); 2888 labelNode.removeAttr("tabindex"); 2889 } 2890 else 2891 { 2892 tabIndex = labelNode.data("finesse:dojoButton:tabIndex"); 2893 if (typeof(tabIndex) === "string") 2894 { 2895 labelNode.attr("tabindex", Number(tabIndex)); 2896 } 2897 } 2898 }, 2899 2900 /** 2901 * @private 2902 * Use this utility to disable the tab stop for a Dojo Firebug iframe within a gadget. 2903 * 2904 * Dojo sometimes adds a hidden iframe for enabling a firebug lite console in older 2905 * browsers. Unfortunately, this adds an additional tab stop that impacts accessibility. 2906 */ 2907 disableTabStopForDojoFirebugIframe: function () { 2908 var iframe = $("iframe[src*='loadFirebugConsole']"); 2909 2910 if ((iframe.length) && (iframe.attr("tabIndex") !== "-1")) { 2911 iframe.attr('tabIndex', '-1'); 2912 } 2913 }, 2914 2915 /** 2916 * @private 2917 * Measures the given text using the supplied fontFamily and fontSize 2918 * @param {string} text text to measure 2919 * @param {string} fontFamily 2920 * @param {string} fontSize 2921 * @return {number} pixel width 2922 */ 2923 measureText: function (text, fontFamily, fontSize) { 2924 var width, 2925 element = $("<div></div>").text(text).css({ 2926 "fontSize": fontSize, 2927 "fontFamily": fontFamily 2928 }).addClass("offscreen").appendTo(document.body); 2929 2930 width = element.width(); 2931 element.remove(); 2932 2933 return width; 2934 }, 2935 2936 /** 2937 * Adjusts the gadget height. Shindig's gadgets.window.adjustHeight fails when 2938 * needing to resize down in IE. This gets around that by calculating the height 2939 * manually and passing it in. 2940 * @return {undefined} 2941 */ 2942 "adjustGadgetHeight": function () { 2943 var bScrollHeight = $("body").height() + 20; 2944 gadgets.window.adjustHeight(bScrollHeight); 2945 }, 2946 2947 /** 2948 * Private helper method for converting a javascript object to xml, where the values of the elements are 2949 * appropriately escaped for XML. 2950 * This is a simple implementation that does not implement cdata or attributes. It is also 'unformatted' in that 2951 * there is no whitespace between elements. 2952 * @param object The javascript object to convert to XML. 2953 * @returns The XML string. 2954 * @private 2955 */ 2956 _json2xmlWithEscape: function(object) { 2957 var that = this, 2958 xml = "", 2959 m, 2960 /** @private **/ 2961 toXmlHelper = function(value, name) { 2962 var xml = "", 2963 i, 2964 m; 2965 if (value instanceof Array) { 2966 for (i = 0; i < value.length; ++i) { 2967 xml += toXmlHelper(value[i], name); 2968 } 2969 } 2970 else if (typeof value === "object") { 2971 xml += "<" + name + ">"; 2972 for (m in value) { 2973 if (value.hasOwnProperty(m)) { 2974 xml += toXmlHelper(value[m], m); 2975 } 2976 } 2977 xml += "</" + name + ">"; 2978 } 2979 else { 2980 // is a leaf node 2981 xml += "<" + name + ">" + that.translateHTMLEntities(value.toString(), true) + 2982 "</" + name + ">"; 2983 } 2984 return xml; 2985 }; 2986 for (m in object) { 2987 if (object.hasOwnProperty(m)) { 2988 xml += toXmlHelper(object[m], m); 2989 } 2990 } 2991 return xml; 2992 }, 2993 2994 /** 2995 * Private method for returning a sanitized version of the user agent string. 2996 * @returns the user agent string, but sanitized! 2997 * @private 2998 */ 2999 getSanitizedUserAgentString: function () { 3000 return this.translateXMLEntities(navigator.userAgent, true); 3001 }, 3002 3003 /** 3004 * Use JQuery's implementation of Promises (Deferred) to execute code when 3005 * multiple async processes have finished. An example use: 3006 * 3007 * var asyncProcess1 = $.Deferred(), 3008 * asyncProcess2 = $.Deferred(); 3009 * 3010 * finesse.utilities.Utilities.whenAllDone(asyncProcess1, asyncProcess2) // WHEN both asyncProcess1 and asyncProcess2 are resolved or rejected ... 3011 * .then( 3012 * // First function passed to then() is called when all async processes are complete, regardless of errors 3013 * function () { 3014 * console.log("all processes completed"); 3015 * }, 3016 * // Second function passed to then() is called if any async processed threw an exception 3017 * function (failures) { // Array of failure messages 3018 * console.log("Number of failed async processes: " + failures.length); 3019 * }); 3020 * 3021 * Found at: 3022 * http://stackoverflow.com/a/15094263/1244030 3023 * 3024 * Pass in any number of $.Deferred instances. 3025 * @returns {Object} 3026 */ 3027 whenAllDone: function () { 3028 var deferreds = [], 3029 result = $.Deferred(); 3030 3031 $.each(arguments, function(i, current) { 3032 var currentDeferred = $.Deferred(); 3033 current.then(function() { 3034 currentDeferred.resolve(false, arguments); 3035 }, function() { 3036 currentDeferred.resolve(true, arguments); 3037 }); 3038 deferreds.push(currentDeferred); 3039 }); 3040 3041 $.when.apply($, deferreds).then(function() { 3042 var failures = [], 3043 successes = []; 3044 3045 $.each(arguments, function(i, args) { 3046 // If we resolved with `true` as the first parameter 3047 // we have a failure, a success otherwise 3048 var target = args[0] ? failures : successes, 3049 data = args[1]; 3050 // Push either all arguments or the only one 3051 target.push(data.length === 1 ? data[0] : args); 3052 }); 3053 3054 if(failures.length) { 3055 return result.reject.apply(result, failures); 3056 } 3057 3058 return result.resolve.apply(result, successes); 3059 }); 3060 3061 return result; 3062 } 3063 }; 3064 3065 window.finesse = window.finesse || {}; 3066 window.finesse.utilities = window.finesse.utilities || {}; 3067 window.finesse.utilities.Utilities = Utilities; 3068 3069 return Utilities; 3070 }); 3071 3072 /** The following comment is to prevent jslint errors about 3073 * using variables before they are defined. 3074 */ 3075 /*global finesse*/ 3076 3077 /** 3078 * Initiated by the Master to create a shared BOSH connection. 3079 * 3080 * @requires Utilities 3081 */ 3082 3083 /** 3084 * @class 3085 * Establishes a shared event connection by creating a communication tunnel 3086 * with the notification server and consume events which could be published. 3087 * Public functions are exposed to register to the connection status information 3088 * and events. 3089 * @constructor 3090 * @param {String} host 3091 * The host name/ip of the Finesse server. 3092 * @throws {Error} If required constructor parameter is missing. 3093 */ 3094 /** @private */ 3095 define('clientservices/MasterTunnel',["utilities/Utilities"], function (Utilities) { 3096 var MasterTunnel = function (host, scheme) { 3097 if (typeof host !== "string" || host.length === 0) { 3098 throw new Error("Required host parameter missing."); 3099 } 3100 3101 var 3102 3103 /** 3104 * Flag to indicate whether the tunnel frame is loaded. 3105 * @private 3106 */ 3107 _isTunnelLoaded = false, 3108 3109 /** 3110 * Short reference to the Finesse utility. 3111 * @private 3112 */ 3113 _util = Utilities, 3114 3115 /** 3116 * The URL with host and port to the Finesse server. 3117 * @private 3118 */ 3119 _tunnelOrigin, 3120 3121 /** 3122 * Location of the tunnel HTML URL. 3123 * @private 3124 */ 3125 _tunnelURL, 3126 3127 /** 3128 * The port on which to connect to the Finesse server to load the eventing resources. 3129 * @private 3130 */ 3131 _tunnelOriginPort, 3132 3133 /** 3134 * Flag to indicate whether we have processed the tunnel config yet. 3135 * @private 3136 */ 3137 _isTunnelConfigInit = false, 3138 3139 /** 3140 * The tunnel frame window object. 3141 * @private 3142 */ 3143 _tunnelFrame, 3144 3145 /** 3146 * The handler registered with the object to be invoked when an event is 3147 * delivered by the notification server. 3148 * @private 3149 */ 3150 _eventHandler, 3151 3152 /** 3153 * The handler registered with the object to be invoked when presence is 3154 * delivered by the notification server. 3155 * @private 3156 */ 3157 _presenceHandler, 3158 3159 /** 3160 * The handler registered with the object to be invoked when the BOSH 3161 * connection has changed states. The object will contain the "status" 3162 * property and a "resourceID" property only if "status" is "connected". 3163 * @private 3164 */ 3165 _connInfoHandler, 3166 3167 /** 3168 * The last connection status published by the JabberWerx library. 3169 * @private 3170 */ 3171 _statusCache, 3172 3173 /** 3174 * The last event sent by notification server. 3175 * @private 3176 */ 3177 _eventCache, 3178 3179 /** 3180 * The ID of the user logged into notification server. 3181 * @private 3182 */ 3183 _id, 3184 3185 /** 3186 * The domain of the XMPP server, representing the portion of the JID 3187 * following '@': userid@domain.com 3188 * @private 3189 */ 3190 _xmppDomain, 3191 3192 /** 3193 * The password of the user logged into notification server. 3194 * @private 3195 */ 3196 _password, 3197 3198 /** 3199 * The jid of the pubsub service on the XMPP server 3200 * @private 3201 */ 3202 _pubsubDomain, 3203 3204 /** 3205 * The resource to use for the BOSH connection. 3206 * @private 3207 */ 3208 _resource, 3209 3210 /** 3211 * The resource ID identifying the client device (that we receive from the server). 3212 * @private 3213 */ 3214 _resourceID, 3215 3216 /** 3217 * The different types of messages that could be sent to the parent frame. 3218 * The types here should be understood by the parent frame and used to 3219 * identify how the message is formatted. 3220 * @private 3221 */ 3222 _TYPES = { 3223 EVENT: 0, 3224 ID: 1, 3225 PASSWORD: 2, 3226 RESOURCEID: 3, 3227 STATUS: 4, 3228 XMPPDOMAIN: 5, 3229 PUBSUBDOMAIN: 6, 3230 SUBSCRIBE: 7, 3231 UNSUBSCRIBE: 8, 3232 PRESENCE: 9, 3233 CONNECT_REQ: 10 3234 }, 3235 3236 _handlers = { 3237 subscribe: {}, 3238 unsubscribe: {} 3239 }, 3240 3241 3242 /** 3243 * Create a connection info object. 3244 * @returns {Object} 3245 * A connection info object containing a "status" and "resourceID". 3246 * @private 3247 */ 3248 _createConnInfoObj = function () { 3249 return { 3250 status: _statusCache, 3251 resourceID: _resourceID 3252 }; 3253 }, 3254 3255 /** 3256 * Utility function which sends a message to the dynamic tunnel frame 3257 * event frame formatted as follows: "type|message". 3258 * @param {Number} type 3259 * The category type of the message. 3260 * @param {String} message 3261 * The message to be sent to the tunnel frame. 3262 * @private 3263 */ 3264 _sendMessage = function (type, message) { 3265 message = type + "|" + message; 3266 _util.sendMessage(message, _tunnelFrame, _tunnelOrigin); 3267 }, 3268 3269 /** 3270 * Utility to process the response of a subscribe request from 3271 * the tunnel frame, then invoking the stored callback handler 3272 * with the respective data (error, when applicable) 3273 * @param {String} data 3274 * The response in the format of "node[|error]" 3275 * @private 3276 */ 3277 _processSubscribeResponse = function (data) { 3278 var dataArray = data.split("|"), 3279 node = dataArray[0], 3280 err; 3281 3282 //Error is optionally the second item in the array 3283 if (dataArray.length) { 3284 err = dataArray[1]; 3285 } 3286 3287 // These response handlers are short lived and should be removed and cleaned up immediately after invocation. 3288 if (_handlers.subscribe[node]) { 3289 _handlers.subscribe[node](err); 3290 delete _handlers.subscribe[node]; 3291 } 3292 }, 3293 3294 /** 3295 * Utility to process the response of an unsubscribe request from 3296 * the tunnel frame, then invoking the stored callback handler 3297 * with the respective data (error, when applicable) 3298 * @param {String} data 3299 * The response in the format of "node[|error]" 3300 * @private 3301 */ 3302 _processUnsubscribeResponse = function (data) { 3303 var dataArray = data.split("|"), 3304 node = dataArray[0], 3305 err; 3306 3307 //Error is optionally the second item in the array 3308 if (dataArray.length) { 3309 err = dataArray[1]; 3310 } 3311 3312 // These response handlers are short lived and should be removed and cleaned up immediately after invocation. 3313 if (_handlers.unsubscribe[node]) { 3314 _handlers.unsubscribe[node](err); 3315 delete _handlers.unsubscribe[node]; 3316 } 3317 }, 3318 3319 /** 3320 * Handler for messages delivered by window.postMessage. Listens for events 3321 * published by the notification server, connection status published by 3322 * the JabberWerx library, and the resource ID created when the BOSH 3323 * connection has been established. 3324 * @param {Object} e 3325 * The message object as provided by the window.postMessage feature. 3326 * @private 3327 */ 3328 _messageHandler = function (e) { 3329 var 3330 3331 //Extract the message type and message data. The expected format is 3332 //"type|data" where type is a number represented by the TYPES object. 3333 delimPos = e.data.indexOf("|"), 3334 type = Number(e.data.substr(0, delimPos)), 3335 data = e.data.substr(delimPos + 1); 3336 3337 //Accepts messages and invoke the correct registered handlers. 3338 switch (type) { 3339 case _TYPES.EVENT: 3340 _eventCache = data; 3341 if (typeof _eventHandler === "function") { 3342 _eventHandler(data); 3343 } 3344 break; 3345 case _TYPES.STATUS: 3346 _statusCache = data; 3347 3348 //A "loaded" status means that the frame is ready to accept 3349 //credentials for establishing a BOSH connection. 3350 if (data === "loaded") { 3351 _isTunnelLoaded = true; 3352 if(_resource) { 3353 _sendMessage(_TYPES.RESOURCEID, _resource); 3354 } 3355 _sendMessage(_TYPES.ID, _id); 3356 _sendMessage(_TYPES.XMPPDOMAIN, _xmppDomain); 3357 _sendMessage(_TYPES.PASSWORD, _password); 3358 _sendMessage(_TYPES.PUBSUBDOMAIN, _pubsubDomain); 3359 } else if (typeof _connInfoHandler === "function") { 3360 _connInfoHandler(_createConnInfoObj()); 3361 } 3362 break; 3363 case _TYPES.RESOURCEID: 3364 _resourceID = data; 3365 break; 3366 case _TYPES.SUBSCRIBE: 3367 _processSubscribeResponse(data); 3368 break; 3369 case _TYPES.UNSUBSCRIBE: 3370 _processUnsubscribeResponse(data); 3371 break; 3372 case _TYPES.PRESENCE: 3373 if (typeof _presenceHandler === "function") { 3374 _presenceHandler(data); 3375 } 3376 break; 3377 default: 3378 break; 3379 } 3380 }, 3381 3382 /** 3383 * Initialize the tunnel config so that the url can be http or https with the appropriate port 3384 * @private 3385 */ 3386 _initTunnelConfig = function () { 3387 if (_isTunnelConfigInit === true) { 3388 return; 3389 } 3390 3391 //Initialize tunnel origin 3392 //Determine tunnel origin based on host and scheme 3393 _tunnelOriginPort = (scheme && scheme.indexOf("https") !== -1) ? "7443" : "7071"; 3394 if (scheme) { 3395 _tunnelOrigin = scheme + "://" + host + ":" + _tunnelOriginPort; 3396 } else { 3397 _tunnelOrigin = "http://" + host + ":" + _tunnelOriginPort; 3398 } 3399 _tunnelURL = _tunnelOrigin + "/tunnel/"; 3400 3401 _isTunnelConfigInit = true; 3402 }, 3403 3404 /** 3405 * Create the tunnel iframe which establishes the shared BOSH connection. 3406 * Messages are sent across frames using window.postMessage. 3407 * @private 3408 */ 3409 _createTunnel = function () { 3410 var tunnelID = ((self === parent) ? "tunnel-frame" : "autopilot-tunnel-frame"), 3411 iframe = document.createElement("iframe"); 3412 iframe.style.display = "none"; 3413 iframe.setAttribute("id", tunnelID); 3414 iframe.setAttribute("name", tunnelID); 3415 iframe.setAttribute("src", _tunnelURL); 3416 document.body.appendChild(iframe); 3417 _tunnelFrame = window.frames[tunnelID]; 3418 }; 3419 3420 /** 3421 * Sends a message via postmessage to the EventTunnel to attempt to connect to the XMPP server 3422 * @private 3423 */ 3424 this.makeConnectReq = function () { 3425 _sendMessage(_TYPES.PASSWORD, _password); 3426 }; 3427 3428 /** 3429 * @private 3430 * Returns the host of the Finesse server. 3431 * @returns {String} 3432 * The host specified during the creation of the object. 3433 */ 3434 this.getHost = function () { 3435 return host; 3436 }; 3437 3438 /** 3439 * @private 3440 * The resource ID of the user who is logged into the notification server. 3441 * @returns {String} 3442 * The resource ID generated by the notification server. 3443 */ 3444 this.getResourceID = function () { 3445 return _resourceID; 3446 }; 3447 3448 /** 3449 * @private 3450 * Indicates whether the tunnel frame is loaded. 3451 * @returns {Boolean} 3452 * True if the tunnel frame is loaded, false otherwise. 3453 */ 3454 this.isTunnelLoaded = function () { 3455 return _isTunnelLoaded; 3456 }; 3457 3458 /** 3459 * @private 3460 * The location of the tunnel HTML URL. 3461 * @returns {String} 3462 * The location of the tunnel HTML URL. 3463 */ 3464 this.getTunnelURL = function () { 3465 return _tunnelURL; 3466 }; 3467 3468 /** 3469 * @private 3470 * Tunnels a subscribe request to the eventing iframe. 3471 * @param {String} node 3472 * The node to subscribe to 3473 * @param {Function} handler 3474 * Handler to invoke upon success or failure 3475 */ 3476 this.subscribe = function (node, handler) { 3477 if (handler && typeof handler !== "function") { 3478 throw new Error("Parameter is not a function."); 3479 } 3480 _handlers.subscribe[node] = handler; 3481 _sendMessage(_TYPES.SUBSCRIBE, node); 3482 }; 3483 3484 /** 3485 * @private 3486 * Tunnels an unsubscribe request to the eventing iframe. 3487 * @param {String} node 3488 * The node to unsubscribe from 3489 * @param {Function} handler 3490 * Handler to invoke upon success or failure 3491 */ 3492 this.unsubscribe = function (node, handler) { 3493 if (handler && typeof handler !== "function") { 3494 throw new Error("Parameter is not a function."); 3495 } 3496 _handlers.unsubscribe[node] = handler; 3497 _sendMessage(_TYPES.UNSUBSCRIBE, node); 3498 }; 3499 3500 /** 3501 * @private 3502 * Registers a handler to be invoked when an event is delivered. Only one 3503 * is registered at a time. If there has already been an event that was 3504 * delivered, the handler will be invoked immediately. 3505 * @param {Function} handler 3506 * Invoked when an event is delivered through the event connection. 3507 */ 3508 this.registerEventHandler = function (handler) { 3509 if (typeof handler !== "function") { 3510 throw new Error("Parameter is not a function."); 3511 } 3512 _eventHandler = handler; 3513 if (_eventCache) { 3514 handler(_eventCache); 3515 } 3516 }; 3517 3518 /** 3519 * @private 3520 * Unregisters the event handler completely. 3521 */ 3522 this.unregisterEventHandler = function () { 3523 _eventHandler = undefined; 3524 }; 3525 3526 /** 3527 * @private 3528 * Registers a handler to be invoked when a presence event is delivered. Only one 3529 * is registered at a time. 3530 * @param {Function} handler 3531 * Invoked when a presence event is delivered through the event connection. 3532 */ 3533 this.registerPresenceHandler = function (handler) { 3534 if (typeof handler !== "function") { 3535 throw new Error("Parameter is not a function."); 3536 } 3537 _presenceHandler = handler; 3538 }; 3539 3540 /** 3541 * @private 3542 * Unregisters the presence event handler completely. 3543 */ 3544 this.unregisterPresenceHandler = function () { 3545 _presenceHandler = undefined; 3546 }; 3547 3548 /** 3549 * @private 3550 * Registers a handler to be invoked when a connection status changes. The 3551 * object passed will contain a "status" property, and a "resourceID" 3552 * property, which will contain the most current resource ID assigned to 3553 * the client. If there has already been an event that was delivered, the 3554 * handler will be invoked immediately. 3555 * @param {Function} handler 3556 * Invoked when a connection status changes. 3557 */ 3558 this.registerConnectionInfoHandler = function (handler) { 3559 if (typeof handler !== "function") { 3560 throw new Error("Parameter is not a function."); 3561 } 3562 _connInfoHandler = handler; 3563 if (_statusCache) { 3564 handler(_createConnInfoObj()); 3565 } 3566 }; 3567 3568 /** 3569 * @private 3570 * Unregisters the connection information handler. 3571 */ 3572 this.unregisterConnectionInfoHandler = function () { 3573 _connInfoHandler = undefined; 3574 }; 3575 3576 /** 3577 * @private 3578 * Start listening for events and create a event tunnel for the shared BOSH 3579 * connection. 3580 * @param {String} id 3581 * The ID of the user for the notification server. 3582 * @param {String} password 3583 * The password of the user for the notification server. 3584 * @param {String} xmppDomain 3585 * The XMPP domain of the notification server 3586 * @param {String} pubsubDomain 3587 * The location (JID) of the XEP-0060 PubSub service 3588 * @param {String} resource 3589 * The resource to connect to the notification servier with. 3590 */ 3591 this.init = function (id, password, xmppDomain, pubsubDomain, resource) { 3592 3593 if (typeof id !== "string" || typeof password !== "string" || typeof xmppDomain !== "string" || typeof pubsubDomain !== "string") { 3594 throw new Error("Invalid or missing required parameters."); 3595 } 3596 3597 _initTunnelConfig(); 3598 3599 _id = id; 3600 _password = password; 3601 _xmppDomain = xmppDomain; 3602 _pubsubDomain = pubsubDomain; 3603 _resource = resource; 3604 3605 //Attach a listener for messages sent from tunnel frame. 3606 _util.receiveMessage(_messageHandler, _tunnelOrigin); 3607 3608 //Create the tunnel iframe which will establish the shared connection. 3609 _createTunnel(); 3610 }; 3611 3612 //BEGIN TEST CODE// 3613 // /** 3614 // * Test code added to expose private functions that are used by unit test 3615 // * framework. This section of code is removed during the build process 3616 // * before packaging production code. The [begin|end]TestSection are used 3617 // * by the build to identify the section to strip. 3618 // * @ignore 3619 // */ 3620 // this.beginTestSection = 0; 3621 // 3622 // /** 3623 // * @ignore 3624 // */ 3625 // this.getTestObject = function () { 3626 // //Load mock dependencies. 3627 // var _mock = new MockControl(); 3628 // _util = _mock.createMock(finesse.utilities.Utilities); 3629 // 3630 // return { 3631 // //Expose mock dependencies 3632 // mock: _mock, 3633 // util: _util, 3634 // 3635 // //Expose internal private functions 3636 // types: _TYPES, 3637 // createConnInfoObj: _createConnInfoObj, 3638 // sendMessage: _sendMessage, 3639 // messageHandler: _messageHandler, 3640 // createTunnel: _createTunnel, 3641 // handlers: _handlers, 3642 // initTunnelConfig : _initTunnelConfig 3643 // }; 3644 // }; 3645 // 3646 // /** 3647 // * @ignore 3648 // */ 3649 // this.endTestSection = 0; 3650 // //END TEST CODE// 3651 }; 3652 3653 /** @namespace JavaScript class objects and methods to handle the subscription to Finesse events.*/ 3654 finesse.clientservices = finesse.clientservices || {}; 3655 3656 window.finesse = window.finesse || {}; 3657 window.finesse.clientservices = window.finesse.clientservices || {}; 3658 window.finesse.clientservices.MasterTunnel = MasterTunnel; 3659 3660 return MasterTunnel; 3661 3662 }); 3663 3664 /** 3665 * Contains a list of topics used for client side pubsub. 3666 * 3667 */ 3668 3669 /** @private */ 3670 define('clientservices/Topics',[], function () { 3671 3672 var Topics = (function () { 3673 3674 /** 3675 * @private 3676 * The namespace prepended to all Finesse topics. 3677 */ 3678 this.namespace = "finesse.info"; 3679 3680 /** 3681 * @private 3682 * Gets the full topic name with the Finesse namespace prepended. 3683 * @param {String} topic 3684 * The topic category. 3685 * @returns {String} 3686 * The full topic name with prepended namespace. 3687 */ 3688 var _getNSTopic = function (topic) { 3689 return this.namespace + "." + topic; 3690 }; 3691 3692 /** @scope finesse.clientservices.Topics */ 3693 return { 3694 /** 3695 * @private 3696 * Client side request channel. 3697 */ 3698 REQUESTS: _getNSTopic("requests"), 3699 3700 /** 3701 * @private 3702 * Client side response channel. 3703 */ 3704 RESPONSES: _getNSTopic("responses"), 3705 3706 /** 3707 * @private 3708 * Connection status. 3709 */ 3710 EVENTS_CONNECTION_INFO: _getNSTopic("connection"), 3711 3712 /** 3713 * @private 3714 * Presence channel 3715 */ 3716 PRESENCE: _getNSTopic("presence"), 3717 3718 /** 3719 * @private 3720 * Convert a Finesse REST URI to a OpenAjax compatible topic name. 3721 */ 3722 getTopic: function (restUri) { 3723 //The topic should not start with '/' else it will get replaced with 3724 //'.' which is invalid. 3725 //Thus, remove '/' if it is at the beginning of the string 3726 if (restUri.indexOf('/') === 0) { 3727 restUri = restUri.substr(1); 3728 } 3729 3730 //Replace every instance of "/" with ".". This is done to follow the 3731 //OpenAjaxHub topic name convention. 3732 return restUri.replace(/\//g, "."); 3733 } 3734 }; 3735 }()); 3736 window.finesse = window.finesse || {}; 3737 window.finesse.clientservices = window.finesse.clientservices || {}; 3738 /** @private */ 3739 window.finesse.clientservices.Topics = Topics; 3740 3741 return Topics; 3742 }); 3743 3744 /** The following comment is to prevent jslint errors about 3745 * using variables before they are defined. 3746 */ 3747 /*global finesse*/ 3748 3749 /** 3750 * Registers with the MasterTunnel to receive events, which it 3751 * could publish to the OpenAjax gadget pubsub infrastructure. 3752 * 3753 * @requires OpenAjax, finesse.clientservices.MasterTunnel, finesse.clientservices.Topics 3754 */ 3755 3756 /** @private */ 3757 define('clientservices/MasterPublisher',[ 3758 "clientservices/MasterTunnel", 3759 "clientservices/Topics", 3760 "utilities/Utilities" 3761 ], 3762 function (MasterTunnel, Topics, Utilities) { 3763 3764 var MasterPublisher = function (tunnel, hub) { 3765 if (!(tunnel instanceof MasterTunnel)) { 3766 throw new Error("Required tunnel object missing or invalid."); 3767 } 3768 3769 var 3770 3771 ClientServices = finesse.clientservices.ClientServices, 3772 3773 /** 3774 * Reference to the gadget pubsub Hub instance. 3775 * @private 3776 */ 3777 _hub = hub, 3778 3779 /** 3780 * Reference to the Topics class. 3781 * @private 3782 */ 3783 _topics = Topics, 3784 3785 /** 3786 * Reference to conversion utilities class. 3787 * @private 3788 */ 3789 _utils = Utilities, 3790 3791 /** 3792 * References to ClientServices logger methods 3793 * @private 3794 */ 3795 _logger = { 3796 log: ClientServices.log 3797 }, 3798 3799 /** 3800 * Store the passed in tunnel. 3801 * @private 3802 */ 3803 _tunnel = tunnel, 3804 3805 /** 3806 * Caches the connection info event so that it could be published if there 3807 * is a request for it. 3808 * @private 3809 */ 3810 _connInfoCache = {}, 3811 3812 /** 3813 * The types of possible request types supported when listening to the 3814 * requests channel. Each request type could result in different operations. 3815 * @private 3816 */ 3817 _REQTYPES = { 3818 CONNECTIONINFO: "ConnectionInfoReq", 3819 SUBSCRIBE: "SubscribeNodeReq", 3820 UNSUBSCRIBE: "UnsubscribeNodeReq", 3821 CONNECT: "ConnectionReq" 3822 }, 3823 3824 /** 3825 * Will store list of nodes that have OF subscriptions created 3826 * _nodesList[node][subscribing].reqIds[subid] 3827 * _nodesList[node][active].reqIds[subid] 3828 * _nodesList[node][unsubscribing].reqIds[subid] 3829 * _nodesList[node][holding].reqIds[subid] 3830 * @private 3831 */ 3832 _nodesList = {}, 3833 3834 /** 3835 * The states that a subscription can be in 3836 * @private 3837 */ 3838 _CHANNELSTATES = { 3839 UNINITIALIZED: "Uninitialized", 3840 PENDING: "Pending", 3841 OPERATIONAL: "Operational" 3842 }, 3843 3844 /** 3845 * Checks if the payload is JSON 3846 * @returns {Boolean} 3847 * @private 3848 */ 3849 _isJsonPayload = function(event) { 3850 var delimStart, delimEnd, retval = false; 3851 3852 try { 3853 delimStart = event.indexOf('{'); 3854 delimEnd = event.lastIndexOf('}'); 3855 3856 if ((delimStart !== -1 ) && (delimEnd === (event.length - 1))) { 3857 retval = true; //event contains JSON payload 3858 } 3859 } catch (err) { 3860 _logger.log("MasterPublisher._isJsonPayload() - Caught error: " + err); 3861 } 3862 return retval; 3863 }, 3864 3865 /** 3866 * Parses a JSON event and then publishes. 3867 * 3868 * @param {String} event 3869 * The full event payload. 3870 * @throws {Error} If the payload object is malformed. 3871 * @private 3872 */ 3873 _parseAndPublishJSONEvent = function(event) { 3874 var topic, eventObj, publishEvent, 3875 delimPos = event.indexOf("{"), 3876 node, parser, 3877 eventJson = event, 3878 returnObj = {node: null, data: null}; 3879 3880 try { 3881 //Extract and strip the node path from the message 3882 if (delimPos > 0) 3883 { 3884 //We need to decode the URI encoded node path 3885 //TODO: make sure this is kosher with OpenAjax topic naming 3886 node = decodeURI(event.substr(0, delimPos)); 3887 eventJson = event.substr(delimPos); 3888 3889 //Converting the node path to openAjaxhub topic 3890 topic = _topics.getTopic(node); 3891 3892 returnObj.node = node; 3893 returnObj.payload = eventJson; 3894 } 3895 else 3896 { 3897 _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] node is not given in postMessage: " + eventJson); 3898 throw new Error("node is not given in postMessage: " + eventJson); 3899 } 3900 3901 parser = _utils.getJSONParser(); 3902 3903 eventObj = parser.parse(eventJson); 3904 returnObj.data = eventObj; 3905 3906 } catch (err) { 3907 _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] Malformed event payload: " + err); 3908 throw new Error("Malformed event payload : " + err); 3909 } 3910 3911 _logger.log("MasterPublisher._parseAndPublishJSONEvent() - Received JSON event on node '" + node + "': " + eventJson); 3912 3913 publishEvent = {content : event, object : eventObj }; 3914 3915 //Publish event to proper event topic. 3916 if (topic && eventObj) { 3917 _hub.publish(topic, publishEvent); 3918 } 3919 }, 3920 3921 /** 3922 * Parses an XML event and then publishes. 3923 * 3924 * @param {String} event 3925 * The full event payload. 3926 * @throws {Error} If the payload object is malformed. 3927 * @private 3928 */ 3929 _parseAndPublishXMLEvent = function(event) { 3930 var topic, eventObj, publishEvent, restTopic, 3931 delimPos = event.indexOf("<"), 3932 node, 3933 eventXml = event; 3934 3935 try { 3936 //Extract and strip the node path from the message 3937 if (delimPos > 0) { 3938 //We need to decode the URI encoded node path 3939 //TODO: make sure this is kosher with OpenAjax topic naming 3940 node = decodeURI(event.substr(0, delimPos)); 3941 eventXml = event.substr(delimPos); 3942 //Converting the node path to openAjaxhub topic 3943 topic = _topics.getTopic(node); 3944 } else { 3945 _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] node is not given in postMessage: " + eventXml); 3946 throw new Error("node is not given in postMessage: " + eventXml); 3947 } 3948 3949 eventObj = _utils.xml2JsObj(eventXml); 3950 3951 } catch (err) { 3952 _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] Malformed event payload: " + err); 3953 throw new Error("Malformed event payload : " + err); 3954 } 3955 3956 _logger.log("MasterPublisher._parseAndPublishXMLEvent() - Received XML event on node '" + node + "': " + eventXml); 3957 3958 publishEvent = {content : event, object : eventObj }; 3959 3960 //Publish event to proper event topic. 3961 if (topic && eventObj) { 3962 _hub.publish(topic, publishEvent); 3963 } 3964 }, 3965 3966 /** 3967 * Publishes events to the appropriate topic. The topic name is determined 3968 * by fetching the source value from the event. 3969 * @param {String} event 3970 * The full event payload. 3971 * @throws {Error} If the payload object is malformed. 3972 * @private 3973 */ 3974 _eventHandler = function (event) { 3975 3976 //Handle JSON or XML events 3977 if (!_isJsonPayload(event)) 3978 { 3979 //XML 3980 _parseAndPublishXMLEvent(event); 3981 } 3982 else 3983 { 3984 //JSON 3985 _parseAndPublishJSONEvent(event); 3986 } 3987 }, 3988 3989 3990 /** 3991 * Handler for when presence events are sent through the MasterTunnel. 3992 * @returns {Object} 3993 * A presence xml event. 3994 * @private 3995 */ 3996 _presenceHandler = function (event) { 3997 var eventObj = _utils.xml2JsObj(event), publishEvent; 3998 3999 publishEvent = {content : event, object : eventObj}; 4000 4001 if (eventObj) { 4002 _hub.publish(_topics.PRESENCE, publishEvent); 4003 } 4004 }, 4005 4006 /** 4007 * Clone the connection info object from cache. 4008 * @returns {Object} 4009 * A connection info object containing a "status" and "resourceID". 4010 * @private 4011 */ 4012 _cloneConnInfoObj = function () { 4013 if (_connInfoCache) { 4014 return { 4015 status: _connInfoCache.status, 4016 resourceID: _connInfoCache.resourceID 4017 }; 4018 } else { 4019 return null; 4020 } 4021 }, 4022 4023 /** 4024 * Cleans up any outstanding subscribe/unsubscribe requests and notifies them of errors. 4025 * This is done if we get disconnected because we cleanup explicit subscriptions on disconnect. 4026 * @private 4027 */ 4028 _cleanupPendingRequests = function () { 4029 var node, curSubid, errObj = { 4030 error: { 4031 errorType: "Disconnected", 4032 errorMessage: "Outstanding request will never complete." 4033 } 4034 }; 4035 4036 // Iterate through all outstanding subscribe requests to notify them that it will never return 4037 for (node in _nodesList) { 4038 if (_nodesList.hasOwnProperty(node)) { 4039 for (curSubid in _nodesList[node].subscribing.reqIds) { 4040 if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) { 4041 // Notify this outstanding subscribe request to give up and error out 4042 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 4043 } 4044 } 4045 for (curSubid in _nodesList[node].unsubscribing.reqIds) { 4046 if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) { 4047 // Notify this outstanding unsubscribe request to give up and error out 4048 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 4049 } 4050 } 4051 } 4052 } 4053 }, 4054 4055 /** 4056 * Publishes the connection info to the connection info topic. 4057 * @param {Object} connInfo 4058 * The connection info object containing the status and resource ID. 4059 * @private 4060 */ 4061 _connInfoHandler = function (connInfo) { 4062 var before = _connInfoCache.status; 4063 _connInfoCache = connInfo; 4064 _logger.log("MasterPublisher._connInfoHandler() - Connection status: " + connInfo.status); 4065 _hub.publish(_topics.EVENTS_CONNECTION_INFO, _cloneConnInfoObj()); 4066 if (before === "connected" && connInfo.status !== "connected") { 4067 // Fail all pending requests when we transition to disconnected 4068 _cleanupPendingRequests(); 4069 } 4070 }, 4071 4072 4073 /** 4074 * Utility method to bookkeep node subscription requests and determine 4075 * whehter it is necessary to tunnel the request to JabberWerx. 4076 * @param {String} node 4077 * The node of interest 4078 * @param {String} reqId 4079 * A unique string identifying the request/subscription 4080 * @private 4081 */ 4082 _subscribeNode = function (node, subid) { 4083 if (_connInfoCache.status !== "connected") { 4084 _hub.publish(_topics.RESPONSES + "." + subid, { 4085 error: { 4086 errorType: "Not connected", 4087 errorMessage: "Cannot subscribe without connection." 4088 } 4089 }); 4090 return; 4091 } 4092 // NODE DOES NOT YET EXIST 4093 if (!_nodesList[node]) { 4094 _nodesList[node] = { 4095 "subscribing": { 4096 "reqIds": {}, 4097 "length": 0 4098 }, 4099 "active": { 4100 "reqIds": {}, 4101 "length": 0 4102 }, 4103 "unsubscribing": { 4104 "reqIds": {}, 4105 "length": 0 4106 }, 4107 "holding": { 4108 "reqIds": {}, 4109 "length": 0 4110 } 4111 }; 4112 } 4113 if (_nodesList[node].active.length === 0) { 4114 if (_nodesList[node].unsubscribing.length === 0) { 4115 if (_nodesList[node].subscribing.length === 0) { 4116 _nodesList[node].subscribing.reqIds[subid] = true; 4117 _nodesList[node].subscribing.length += 1; 4118 4119 _logger.log("MasterPublisher._subscribeNode() - Attempting to subscribe to node '" + node + "'"); 4120 _tunnel.subscribe(node, function (err) { 4121 var errObj, curSubid; 4122 if (err) { 4123 errObj = { 4124 subscribe: { 4125 content: err 4126 } 4127 }; 4128 4129 try { 4130 errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), ""))); 4131 } catch (e) { 4132 errObj.error = { 4133 errorType: "parseError", 4134 errorMessage: "Could not serialize XML: " + e 4135 }; 4136 } 4137 _logger.log("MasterPublisher._subscribeNode() - Error subscribing to node '" + node + "': " + err); 4138 } else { 4139 _logger.log("MasterPublisher._subscribeNode() - Subscribed to node '" + node + "'"); 4140 } 4141 4142 for (curSubid in _nodesList[node].subscribing.reqIds) { 4143 if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) { 4144 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 4145 if (!err) { 4146 _nodesList[node].active.reqIds[curSubid] = true; 4147 _nodesList[node].active.length += 1; 4148 } 4149 delete _nodesList[node].subscribing.reqIds[curSubid]; 4150 _nodesList[node].subscribing.length -= 1; 4151 } 4152 } 4153 }); 4154 4155 } else { //other ids are subscribing 4156 _nodesList[node].subscribing.reqIds[subid] = true; 4157 _nodesList[node].subscribing.length += 1; 4158 } 4159 } else { //An unsubscribe request is pending, hold onto these subscribes until it is done 4160 _nodesList[node].holding.reqIds[subid] = true; 4161 _nodesList[node].holding.length += 1; 4162 } 4163 } else { // The node has active subscriptions; add this subid and return successful response 4164 _nodesList[node].active.reqIds[subid] = true; 4165 _nodesList[node].active.length += 1; 4166 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 4167 } 4168 }, 4169 4170 /** 4171 * Utility method to bookkeep node unsubscribe requests and determine 4172 * whehter it is necessary to tunnel the request to JabberWerx. 4173 * @param {String} node 4174 * The node to unsubscribe from 4175 * @param {String} reqId 4176 * A unique string identifying the subscription to remove 4177 * @private 4178 */ 4179 _unsubscribeNode = function (node, subid) { 4180 if (!_nodesList[node]) { //node DNE, publish success response 4181 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 4182 } else { 4183 if (_connInfoCache.status !== "connected") { 4184 _hub.publish(_topics.RESPONSES + "." + subid, { 4185 error: { 4186 errorType: "Not connected", 4187 errorMessage: "Cannot unsubscribe without connection." 4188 } 4189 }); 4190 return; 4191 } 4192 if (_nodesList[node].active.length > 1) { 4193 delete _nodesList[node].active.reqIds[subid]; 4194 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 4195 _nodesList[node].active.length -= 1; 4196 } else if (_nodesList[node].active.length === 1) { // transition subid from active category to unsubscribing category 4197 _nodesList[node].unsubscribing.reqIds[subid] = true; 4198 _nodesList[node].unsubscribing.length += 1; 4199 delete _nodesList[node].active.reqIds[subid]; 4200 _nodesList[node].active.length -= 1; 4201 4202 _logger.log("MasterPublisher._unsubscribeNode() - Attempting to unsubscribe from node '" + node + "'"); 4203 _tunnel.unsubscribe(node, function (err) { 4204 var errObj, curSubid; 4205 if (err) { 4206 errObj = { 4207 subscribe: { 4208 content: err 4209 } 4210 }; 4211 4212 try { 4213 errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), ""))); 4214 } catch (e) { 4215 errObj.error = { 4216 errorType: "parseError", 4217 errorMessage: "Could not serialize XML: " + e 4218 }; 4219 } 4220 _logger.log("MasterPublisher._unsubscribeNode() - Error unsubscribing from node '" + node + "': " + err); 4221 } else { 4222 _logger.log("MasterPublisher._unsubscribeNode() - Unsubscribed from node '" + node + "'"); 4223 } 4224 4225 for (curSubid in _nodesList[node].unsubscribing.reqIds) { 4226 if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) { 4227 // publish to all subids whether unsubscribe failed or succeeded 4228 _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 4229 if (!err) { 4230 delete _nodesList[node].unsubscribing.reqIds[curSubid]; 4231 _nodesList[node].unsubscribing.length -= 1; 4232 } else { // Just remove the subid from unsubscribing; the next subscribe request will operate with node already created 4233 delete _nodesList[node].unsubscribing.reqIds[curSubid]; 4234 _nodesList[node].unsubscribing.length -= 1; 4235 } 4236 } 4237 } 4238 4239 if (!err && _nodesList[node].holding.length > 0) { // if any subscribe requests came in while unsubscribing from OF, now transition from holding to subscribing 4240 for (curSubid in _nodesList[node].holding.reqIds) { 4241 if (_nodesList[node].holding.reqIds.hasOwnProperty(curSubid)) { 4242 delete _nodesList[node].holding.reqIds[curSubid]; 4243 _nodesList[node].holding.length -= 1; 4244 _subscribeNode(node, curSubid); 4245 } 4246 } 4247 } 4248 }); 4249 } else { // length <= 0? 4250 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 4251 } 4252 } 4253 }, 4254 4255 /** 4256 * Handles client requests to establish a BOSH connection. 4257 * @param {String} id 4258 * id of the xmpp user 4259 * @param {String} password 4260 * password of the xmpp user 4261 * @param {String} xmppDomain 4262 * xmppDomain of the xmpp user account 4263 * @private 4264 */ 4265 _connect = function (id, password, xmppDomain) { 4266 _tunnel.makeConnectReq(id, password, xmppDomain); 4267 }, 4268 4269 /** 4270 * Handles client requests made to the request topic. The type of the 4271 * request is described in the "type" property within the data payload. Each 4272 * type can result in a different operation. 4273 * @param {String} topic 4274 * The topic which data was published to. 4275 * @param {Object} data 4276 * The data containing requests information published by clients. 4277 * @param {String} data.type 4278 * The type of the request. Supported: "ConnectionInfoReq" 4279 * @param {Object} data.data 4280 * May contain data relevant for the particular requests. 4281 * @param {String} [data.invokeID] 4282 * The ID used to identify the request with the response. The invoke ID 4283 * will be included in the data in the publish to the topic. It is the 4284 * responsibility of the client to correlate the published data to the 4285 * request made by using the invoke ID. 4286 * @private 4287 */ 4288 _clientRequestHandler = function (topic, data) { 4289 var dataCopy; 4290 4291 //Ensure a valid data object with "type" and "data" properties. 4292 if (typeof data === "object" && 4293 typeof data.type === "string" && 4294 typeof data.data === "object") { 4295 switch (data.type) { 4296 case _REQTYPES.CONNECTIONINFO: 4297 //It is possible that Slave clients come up before the Master 4298 //client. If that is the case, the Slaves will need to make a 4299 //request for the Master to send the latest connection info to the 4300 //connectionInfo topic. 4301 dataCopy = _cloneConnInfoObj(); 4302 if (dataCopy) { 4303 if (data.invokeID !== undefined) { 4304 dataCopy.invokeID = data.invokeID; 4305 } 4306 _hub.publish(_topics.EVENTS_CONNECTION_INFO, dataCopy); 4307 } 4308 break; 4309 case _REQTYPES.SUBSCRIBE: 4310 if (typeof data.data.node === "string") { 4311 _subscribeNode(data.data.node, data.invokeID); 4312 } 4313 break; 4314 case _REQTYPES.UNSUBSCRIBE: 4315 if (typeof data.data.node === "string") { 4316 _unsubscribeNode(data.data.node, data.invokeID); 4317 } 4318 break; 4319 case _REQTYPES.CONNECT: 4320 // Deprecated: Disallow others (non-masters) from administering BOSH connections that are not theirs 4321 _logger.log("MasterPublisher._clientRequestHandler(): F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq"); 4322 break; 4323 default: 4324 break; 4325 } 4326 } 4327 }; 4328 4329 (function () { 4330 //Register to receive events and connection status from tunnel. 4331 _tunnel.registerEventHandler(_eventHandler); 4332 _tunnel.registerPresenceHandler(_presenceHandler); 4333 _tunnel.registerConnectionInfoHandler(_connInfoHandler); 4334 4335 //Listen to a request channel to respond to any requests made by other 4336 //clients because the Master may have access to useful information. 4337 _hub.subscribe(_topics.REQUESTS, _clientRequestHandler); 4338 }()); 4339 4340 /** 4341 * @private 4342 * Handles client requests to establish a BOSH connection. 4343 * @param {String} id 4344 * id of the xmpp user 4345 * @param {String} password 4346 * password of the xmpp user 4347 * @param {String} xmppDomain 4348 * xmppDomain of the xmpp user account 4349 */ 4350 this.connect = function (id, password, xmppDomain) { 4351 _connect(id, password, xmppDomain); 4352 }; 4353 4354 /** 4355 * @private 4356 * Resets the list of explicit subscriptions 4357 */ 4358 this.wipeout = function () { 4359 _cleanupPendingRequests(); 4360 _nodesList = {}; 4361 }; 4362 4363 //BEGIN TEST CODE// 4364 /** 4365 * Test code added to expose private functions that are used by unit test 4366 * framework. This section of code is removed during the build process 4367 * before packaging production code. The [begin|end]TestSection are used 4368 * by the build to identify the section to strip. 4369 * @ignore 4370 */ 4371 this.beginTestSection = 0; 4372 4373 /** 4374 * @ignore 4375 */ 4376 this.getTestObject = function () { 4377 //Load mock dependencies. 4378 var _mock = new MockControl(); 4379 _hub = _mock.createMock(gadgets.Hub); 4380 _tunnel = _mock.createMock(); 4381 4382 return { 4383 //Expose mock dependencies 4384 mock: _mock, 4385 hub: _hub, 4386 tunnel: _tunnel, 4387 setTunnel: function (tunnel) { 4388 _tunnel = tunnel; 4389 }, 4390 getTunnel: function () { 4391 return _tunnel; 4392 }, 4393 4394 //Expose internal private functions 4395 reqtypes: _REQTYPES, 4396 eventHandler: _eventHandler, 4397 presenceHandler: _presenceHandler, 4398 4399 subscribeNode: _subscribeNode, 4400 unsubscribeNode: _unsubscribeNode, 4401 4402 getNodeList: function () { 4403 return _nodesList; 4404 }, 4405 setNodeList: function (nodelist) { 4406 _nodesList = nodelist; 4407 }, 4408 4409 cloneConnInfoObj: _cloneConnInfoObj, 4410 connInfoHandler: _connInfoHandler, 4411 clientRequestHandler: _clientRequestHandler 4412 4413 }; 4414 }; 4415 4416 4417 /** 4418 * @ignore 4419 */ 4420 this.endTestSection = 0; 4421 //END TEST CODE// 4422 4423 }; 4424 4425 window.finesse = window.finesse || {}; 4426 window.finesse.clientservices = window.finesse.clientservices || {}; 4427 window.finesse.clientservices.MasterPublisher = MasterPublisher; 4428 4429 return MasterPublisher; 4430 }); 4431 4432 /** The following comment is to prevent jslint errors about 4433 * using variables before they are defined. 4434 */ 4435 /*global publisher:true */ 4436 4437 /** 4438 * Exposes a set of API wrappers that will hide the dirty work of 4439 * constructing Finesse API requests and consuming Finesse events. 4440 * 4441 * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities 4442 */ 4443 4444 4445 /** 4446 * Allow clients to make Finesse API requests and consume Finesse events by 4447 * calling a set of exposed functions. The Services layer will do the dirty 4448 * work of establishing a shared BOSH connection (for designated Master 4449 * modules), consuming events for client subscriptions, and constructing API 4450 * requests. 4451 */ 4452 /** @private */ 4453 define('clientservices/ClientServices',[ 4454 "clientservices/MasterTunnel", 4455 "clientservices/MasterPublisher", 4456 "clientservices/Topics", 4457 "utilities/Utilities" 4458 ], 4459 function (MasterTunnel, MasterPublisher, Topics, Utilities) { 4460 4461 var ClientServices = (function () {/** @lends finesse.clientservices.ClientServices.prototype */ 4462 var 4463 4464 /** 4465 * Shortcut reference to the master tunnel 4466 * @private 4467 */ 4468 _tunnel, 4469 4470 _publisher, 4471 4472 /** 4473 * Shortcut reference to the finesse.utilities.Utilities singleton 4474 * This will be set by init() 4475 * @private 4476 */ 4477 _util, 4478 4479 /** 4480 * Shortcut reference to the gadgets.io object. 4481 * This will be set by init() 4482 * @private 4483 */ 4484 _io, 4485 4486 /** 4487 * Shortcut reference to the gadget pubsub Hub instance. 4488 * This will be set by init() 4489 * @private 4490 */ 4491 _hub, 4492 4493 /** 4494 * Logger object set externally by setLogger, defaults to nothing. 4495 * @private 4496 */ 4497 _logger = {}, 4498 4499 /** 4500 * Shortcut reference to the Topics class. 4501 * This will be set by init() 4502 * @private 4503 */ 4504 _topics, 4505 4506 /** 4507 * Config object needed to initialize this library 4508 * This must be set by init() 4509 * @private 4510 */ 4511 _config, 4512 4513 /** 4514 * @private 4515 * Whether or not this ClientService instance is a Master. 4516 */ 4517 _isMaster = false, 4518 4519 /** 4520 * @private 4521 * Whether the Client Services have been initiated yet. 4522 */ 4523 _inited = false, 4524 4525 /** 4526 * Stores the list of subscription IDs for all subscriptions so that it 4527 * could be retrieve for unsubscriptions. 4528 * @private 4529 */ 4530 _subscriptionID = {}, 4531 4532 /** 4533 * The possible states of the JabberWerx BOSH connection. 4534 * @private 4535 */ 4536 _STATUS = { 4537 CONNECTING: "connecting", 4538 CONNECTED: "connected", 4539 DISCONNECTED: "disconnected", 4540 DISCONNECTED_CONFLICT: "conflict", 4541 DISCONNECTED_UNAUTHORIZED: "unauthorized", 4542 DISCONNECTING: "disconnecting", 4543 RECONNECTING: "reconnecting", 4544 UNLOADING: "unloading", 4545 FAILING: "failing", 4546 RECOVERED: "recovered" 4547 }, 4548 4549 _failoverMode = false, 4550 4551 /** 4552 * Handler function to be invoked when BOSH connection is connecting. 4553 * @private 4554 */ 4555 _onConnectingHandler, 4556 4557 /** 4558 * Handler function to be invoked when BOSH connection is connected 4559 * @private 4560 */ 4561 _onConnectHandler, 4562 4563 /** 4564 * Handler function to be invoked when BOSH connection is disconnecting. 4565 * @private 4566 */ 4567 _onDisconnectingHandler, 4568 4569 /** 4570 * Handler function to be invoked when the BOSH is disconnected. 4571 * @private 4572 */ 4573 _onDisconnectHandler, 4574 4575 /** 4576 * Handler function to be invoked when the BOSH is reconnecting. 4577 * @private 4578 */ 4579 _onReconnectingHandler, 4580 4581 /** 4582 * Handler function to be invoked when the BOSH is unloading. 4583 * @private 4584 */ 4585 _onUnloadingHandler, 4586 4587 /** 4588 * Contains a cache of the latest connection info containing the current 4589 * state of the BOSH connection and the resource ID. 4590 * @private 4591 */ 4592 _connInfo, 4593 4594 /** 4595 * Keeps track of all the objects that need to be refreshed when we recover 4596 * due to our resilient connection. Only objects that we subscribe to will 4597 * be added to this list. 4598 * @private 4599 */ 4600 _refreshList = [], 4601 4602 /** 4603 * @private 4604 * Centralized logger.log method for external logger 4605 * @param {String} msg 4606 * Message to log 4607 */ 4608 _log = function (msg) { 4609 // If the external logger throws up, it stops here. 4610 try { 4611 if (_logger.log) { 4612 _logger.log("[ClientServices] " + msg); 4613 } 4614 } catch (e) { } 4615 }, 4616 4617 /** 4618 * Go through each object in the _refreshList and call its refresh() function 4619 * @private 4620 */ 4621 _refreshObjects = function () { 4622 var i; 4623 4624 // wipe out the explicit subscription list before we refresh objects 4625 if (_publisher) { 4626 _publisher.wipeout(); 4627 } 4628 4629 // refresh each item in the refresh list 4630 for (i = _refreshList.length - 1; i >= 0; i -= 1) { 4631 _log("Refreshing " + _refreshList[i].getRestUrl()); 4632 _refreshList[i].refresh(10); 4633 } 4634 }, 4635 4636 /** 4637 * Handler to process connection info publishes. 4638 * @param {Object} data 4639 * The connection info data object. 4640 * @param {String} data.status 4641 * The BOSH connection status. 4642 * @param {String} data.resourceID 4643 * The resource ID for the connection. 4644 * @private 4645 */ 4646 _connInfoHandler = function (data) { 4647 4648 //Invoke registered handler depending on status received. Due to the 4649 //request topic where clients can make request for the Master to publish 4650 //the connection info, there is a chance that duplicate connection info 4651 //events may be sent, so ensure that there has been a state change 4652 //before invoking the handlers. 4653 if (_connInfo === undefined || _connInfo.status !== data.status) { 4654 _connInfo = data; 4655 switch (data.status) { 4656 case _STATUS.CONNECTING: 4657 if (_isMaster && _onConnectingHandler) { 4658 _onConnectingHandler(); 4659 } 4660 break; 4661 case _STATUS.CONNECTED: 4662 if ((_isMaster || !_failoverMode) && _onConnectHandler) { 4663 _onConnectHandler(); 4664 } 4665 break; 4666 case _STATUS.DISCONNECTED: 4667 if (_isMaster && _onDisconnectHandler) { 4668 _onDisconnectHandler(); 4669 } 4670 break; 4671 case _STATUS.DISCONNECTED_CONFLICT: 4672 if (_isMaster && _onDisconnectHandler) { 4673 _onDisconnectHandler("conflict"); 4674 } 4675 break; 4676 case _STATUS.DISCONNECTED_UNAUTHORIZED: 4677 if (_isMaster && _onDisconnectHandler) { 4678 _onDisconnectHandler("unauthorized"); 4679 } 4680 break; 4681 case _STATUS.DISCONNECTING: 4682 if (_isMaster && _onDisconnectingHandler) { 4683 _onDisconnectingHandler(); 4684 } 4685 break; 4686 case _STATUS.RECONNECTING: 4687 if (_isMaster && _onReconnectingHandler) { 4688 _onReconnectingHandler(); 4689 } 4690 break; 4691 case _STATUS.UNLOADING: 4692 if (_isMaster && _onUnloadingHandler) { 4693 _onUnloadingHandler(); 4694 } 4695 break; 4696 case _STATUS.FAILING: 4697 if (!_isMaster) { 4698 // Stop 4699 _failoverMode = true; 4700 if (_onDisconnectHandler) { 4701 _onDisconnectHandler(); 4702 } 4703 } 4704 break; 4705 case _STATUS.RECOVERED: 4706 if (!_isMaster) { 4707 _failoverMode = false; 4708 if (_onConnectHandler) { 4709 _onConnectHandler(); 4710 } 4711 } 4712 // Whenever we are recovered, we need to refresh any objects 4713 // that are stored. 4714 _refreshObjects(); 4715 break; 4716 } 4717 } 4718 }, 4719 4720 /** 4721 * Ensure that ClientServices have been inited. 4722 * @private 4723 */ 4724 _isInited = function () { 4725 if (!_inited) { 4726 throw new Error("ClientServices needs to be inited."); 4727 } 4728 }, 4729 4730 /** 4731 * Have the client become the Master by initiating a tunnel to a shared 4732 * event BOSH connection. The Master is responsible for publishing all 4733 * events to the pubsub infrastructure. 4734 * @private 4735 */ 4736 _becomeMaster = function () { 4737 _tunnel = new MasterTunnel(_config.host, _config.scheme); 4738 _publisher = new MasterPublisher(_tunnel, _hub); 4739 _tunnel.init(_config.id, _config.password, _config.xmppDomain, _config.pubsubDomain, _config.resource); 4740 _isMaster = true; 4741 }, 4742 4743 /** 4744 * Make a request to the request channel to have the Master publish the 4745 * connection info object. 4746 * @private 4747 */ 4748 _makeConnectionInfoReq = function () { 4749 var data = { 4750 type: "ConnectionInfoReq", 4751 data: {}, 4752 invokeID: (new Date()).getTime() 4753 }; 4754 _hub.publish(_topics.REQUESTS, data); 4755 }, 4756 4757 /** 4758 * Utility method to register a handler which is associated with a 4759 * particular connection status. 4760 * @param {String} status 4761 * The connection status string. 4762 * @param {Function} handler 4763 * The handler to associate with a particular connection status. 4764 * @throws {Error} 4765 * If the handler provided is not a function. 4766 * @private 4767 */ 4768 _registerHandler = function (status, handler) { 4769 if (typeof handler === "function") { 4770 if (_connInfo && _connInfo.status === status) { 4771 handler(); 4772 } 4773 switch (status) { 4774 case _STATUS.CONNECTING: 4775 _onConnectingHandler = handler; 4776 break; 4777 case _STATUS.CONNECTED: 4778 _onConnectHandler = handler; 4779 break; 4780 case _STATUS.DISCONNECTED: 4781 _onDisconnectHandler = handler; 4782 break; 4783 case _STATUS.DISCONNECTING: 4784 _onDisconnectingHandler = handler; 4785 break; 4786 case _STATUS.RECONNECTING: 4787 _onReconnectingHandler = handler; 4788 break; 4789 case _STATUS.UNLOADING: 4790 _onUnloadingHandler = handler; 4791 break; 4792 } 4793 4794 } else { 4795 throw new Error("Callback is not a function"); 4796 } 4797 }; 4798 4799 return { 4800 4801 /** 4802 * @private 4803 * Adds an item to the list to be refreshed upon reconnect 4804 * @param {RestBase} object - rest object to be refreshed 4805 */ 4806 addToRefreshList: function (object) { 4807 _refreshList.push(object); 4808 }, 4809 4810 /** 4811 * @private 4812 * Removes the given item from the refresh list 4813 * @param {RestBase} object - rest object to be removed 4814 */ 4815 removeFromRefreshList: function (object) { 4816 var i; 4817 for (i = _refreshList.length - 1; i >= 0; i -= 1) { 4818 if (_refreshList[i] === object) { 4819 _refreshList.splice(i, 1); 4820 break; 4821 } 4822 } 4823 }, 4824 4825 /** 4826 * @private 4827 * The location of the tunnel HTML URL. 4828 * @returns {String} 4829 * The location of the tunnel HTML URL. 4830 */ 4831 getTunnelURL: function () { 4832 return _tunnel.getTunnelURL(); 4833 }, 4834 4835 /** 4836 * @private 4837 * Indicates whether the tunnel frame is loaded. 4838 * @returns {Boolean} 4839 * True if the tunnel frame is loaded, false otherwise. 4840 */ 4841 isTunnelLoaded: function () { 4842 return _tunnel.isTunnelLoaded(); 4843 }, 4844 4845 /** 4846 * @private 4847 * Indicates whether the ClientServices instance is a Master. 4848 * @returns {Boolean} 4849 * True if this instance of ClientServices is a Master, false otherwise. 4850 */ 4851 isMaster: function () { 4852 return _isMaster; 4853 }, 4854 4855 /** 4856 * @private 4857 * Get the resource ID. An ID is only available if the BOSH connection has 4858 * been able to connect successfully. 4859 * @returns {String} 4860 * The resource ID string. Null if the BOSH connection was never 4861 * successfully created and/or the resource ID has not been associated. 4862 */ 4863 getResourceID: function () { 4864 if (_connInfo !== undefined) { 4865 return _connInfo.resourceID; 4866 } 4867 return null; 4868 }, 4869 4870 /* 4871 getHub: function () { 4872 return _hub; 4873 }, 4874 */ 4875 /** 4876 * @private 4877 * Add a callback to be invoked when the BOSH connection is attempting 4878 * to connect. If the connection is already trying to connect, the 4879 * callback will be invoked immediately. 4880 * @param {Function} handler 4881 * An empty param function to be invoked on connecting. Only one 4882 * handler can be registered at a time. Handlers already registered 4883 * will be overwritten. 4884 */ 4885 registerOnConnectingHandler: function (handler) { 4886 _registerHandler(_STATUS.CONNECTING, handler); 4887 }, 4888 4889 /** 4890 * @private 4891 * Removes the on connecting callback that was registered. 4892 */ 4893 unregisterOnConnectingHandler: function () { 4894 _onConnectingHandler = undefined; 4895 }, 4896 4897 /** 4898 * Add a callback to be invoked when all of the following conditions are met: 4899 * <ul> 4900 * <li>When Finesse goes IN_SERVICE</li> 4901 * <li>The BOSH connection is established</li> 4902 * <li>The Finesse user presence becomes available</li> 4903 * </ul> 4904 * If all these conditions are met at the time this function is called, then 4905 * the handler will be invoked immediately. 4906 * @param {Function} handler 4907 * An empty param function to be invoked on connect. Only one handler 4908 * can be registered at a time. Handlers already registered will be 4909 * overwritten. 4910 * @example 4911 * finesse.clientservices.ClientServices.registerOnConnectHandler(gadget.myCallback); 4912 */ 4913 registerOnConnectHandler: function (handler) { 4914 _registerHandler(_STATUS.CONNECTED, handler); 4915 }, 4916 4917 /** 4918 * @private 4919 * Removes the on connect callback that was registered. 4920 */ 4921 unregisterOnConnectHandler: function () { 4922 _onConnectHandler = undefined; 4923 }, 4924 4925 /** 4926 * Add a callback to be invoked when any of the following occurs: 4927 * <ul> 4928 * <li>Finesse is no longer IN_SERVICE</li> 4929 * <li>The BOSH connection is lost</li> 4930 * <li>The presence of the Finesse user is no longer available</li> 4931 * </ul> 4932 * If any of these conditions are met at the time this function is 4933 * called, the callback will be invoked immediately. 4934 * @param {Function} handler 4935 * An empty param function to be invoked on disconnected. Only one 4936 * handler can be registered at a time. Handlers already registered 4937 * will be overwritten. 4938 * @example 4939 * finesse.clientservices.ClientServices.registerOnDisconnectHandler(gadget.myCallback); 4940 */ 4941 registerOnDisconnectHandler: function (handler) { 4942 _registerHandler(_STATUS.DISCONNECTED, handler); 4943 }, 4944 4945 /** 4946 * @private 4947 * Removes the on disconnect callback that was registered. 4948 */ 4949 unregisterOnDisconnectHandler: function () { 4950 _onDisconnectHandler = undefined; 4951 }, 4952 4953 /** 4954 * @private 4955 * Add a callback to be invoked when the BOSH is currently disconnecting. If 4956 * the connection is already disconnecting, invoke the callback immediately. 4957 * @param {Function} handler 4958 * An empty param function to be invoked on disconnected. Only one 4959 * handler can be registered at a time. Handlers already registered 4960 * will be overwritten. 4961 */ 4962 registerOnDisconnectingHandler: function (handler) { 4963 _registerHandler(_STATUS.DISCONNECTING, handler); 4964 }, 4965 4966 /** 4967 * @private 4968 * Removes the on disconnecting callback that was registered. 4969 */ 4970 unregisterOnDisconnectingHandler: function () { 4971 _onDisconnectingHandler = undefined; 4972 }, 4973 4974 /** 4975 * @private 4976 * Add a callback to be invoked when the BOSH connection is attempting 4977 * to connect. If the connection is already trying to connect, the 4978 * callback will be invoked immediately. 4979 * @param {Function} handler 4980 * An empty param function to be invoked on connecting. Only one 4981 * handler can be registered at a time. Handlers already registered 4982 * will be overwritten. 4983 */ 4984 registerOnReconnectingHandler: function (handler) { 4985 _registerHandler(_STATUS.RECONNECTING, handler); 4986 }, 4987 4988 /** 4989 * @private 4990 * Removes the on reconnecting callback that was registered. 4991 */ 4992 unregisterOnReconnectingHandler: function () { 4993 _onReconnectingHandler = undefined; 4994 }, 4995 4996 /** 4997 * @private 4998 * Add a callback to be invoked when the BOSH connection is unloading 4999 * 5000 * @param {Function} handler 5001 * An empty param function to be invoked on connecting. Only one 5002 * handler can be registered at a time. Handlers already registered 5003 * will be overwritten. 5004 */ 5005 registerOnUnloadingHandler: function (handler) { 5006 _registerHandler(_STATUS.UNLOADING, handler); 5007 }, 5008 5009 /** 5010 * @private 5011 * Removes the on unloading callback that was registered. 5012 */ 5013 unregisterOnUnloadingHandler: function () { 5014 _onUnloadingHandler = undefined; 5015 }, 5016 5017 /** 5018 * @private 5019 * Proxy method for gadgets.io.makeRequest. The will be identical to gadgets.io.makeRequest 5020 * ClientServices will mixin the BASIC Auth string, locale, and host, since the 5021 * configuration is encapsulated in here anyways. 5022 * This removes the dependency 5023 * @param {String} url 5024 * The relative url to make the request to (the host from the passed in config will be 5025 * appended). It is expected that any encoding to the URL is already done. 5026 * @param {Function} handler 5027 * Callback handler for makeRequest to invoke when the response returns. 5028 * Completely passed through to gadgets.io.makeRequest 5029 * @param {Object} params 5030 * The params object that gadgets.io.makeRequest expects. Authorization and locale 5031 * headers are mixed in. 5032 */ 5033 makeRequest: function (url, handler, params) { 5034 var requestedScheme, scheme = "http"; 5035 5036 // ClientServices needs to be initialized with a config for restHost, auth, and locale 5037 _isInited(); 5038 5039 // Allow mixin of auth and locale headers 5040 params = params || {}; 5041 5042 // Override refresh interval to 0 instead of default 3600 as a way to workaround makeRequest 5043 // using GET http method because then the params are added to the url as query params, which 5044 // exposes the authorization string in the url. This is a placeholder until oauth comes in 5045 params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = params[gadgets.io.RequestParameters.REFRESH_INTERVAL] || 0; 5046 5047 params[gadgets.io.RequestParameters.HEADERS] = params[gadgets.io.RequestParameters.HEADERS] || {}; 5048 5049 // Add Basic auth to request header 5050 params[gadgets.io.RequestParameters.HEADERS].Authorization = "Basic " + _config.authorization; 5051 //Locale 5052 params[gadgets.io.RequestParameters.HEADERS].locale = _config.locale; 5053 5054 //Allow clients to override the scheme: 5055 // - If not specified => we use HTTP 5056 // - If null specified => we use _config.scheme 5057 // - Otherwise => we use whatever they provide 5058 requestedScheme = params.SCHEME; 5059 if (!(requestedScheme === undefined || requestedScheme === "undefined")) { 5060 if (requestedScheme === null) { 5061 scheme = _config.scheme; 5062 } else { 5063 scheme = requestedScheme; 5064 } 5065 } 5066 scheme = _config.restScheme || scheme; 5067 5068 _log("RequestedScheme: " + requestedScheme + "; Scheme: " + scheme); 5069 gadgets.io.makeRequest(encodeURI(scheme + "://" + _config.restHost + ":" + _config.localhostPort) + url, handler, params); 5070 }, 5071 5072 /** 5073 * @private 5074 * Utility function to make a subscription to a particular topic. Only one 5075 * callback function is registered to a particular topic at any time. 5076 * @param {String} topic 5077 * The full topic name. The topic name should follow the OpenAjax 5078 * convention using dot notation (ex: finesse.api.User.1000). 5079 * @param {Function} callback 5080 * The function that should be invoked with the data when an event 5081 * is delivered to the specific topic. 5082 * @returns {Boolean} 5083 * True if the subscription was made successfully and the callback was 5084 * been registered. False if the subscription already exist, the 5085 * callback was not overwritten. 5086 */ 5087 subscribe: function (topic, callback, disableDuringFailover) { 5088 _isInited(); 5089 5090 //Ensure that the same subscription isn't made twice. 5091 if (!_subscriptionID[topic]) { 5092 //Store the subscription ID using the topic name as the key. 5093 _subscriptionID[topic] = _hub.subscribe(topic, 5094 //Invoke the callback just with the data object. 5095 function (topic, data) { 5096 if (!disableDuringFailover || _isMaster || !_failoverMode) { 5097 // Halt all intermediate event processing while the master instance attempts to rebuild the connection. This only occurs: 5098 // - For RestBase objects (which pass the disableDuringFailover flag), so that intermediary events while recovering are hidden away until everything is all good 5099 // - We shouldn't be halting anything else because we have infrastructure requests that use the hub pub sub 5100 // - Master instance will get all events regardless, because it is responsible for managing failover 5101 // - If we are not in a failover mode, everything goes 5102 // _refreshObjects will reconcile anything that was missed once we are back in action 5103 callback(data); 5104 } 5105 }); 5106 return true; 5107 } 5108 return false; 5109 }, 5110 5111 /** 5112 * @private 5113 * Unsubscribe from a particular topic. 5114 * @param {String} topic 5115 * The full topic name. 5116 */ 5117 unsubscribe: function (topic) { 5118 _isInited(); 5119 5120 //Unsubscribe from the topic using the subscription ID recorded when 5121 //the subscription was made, then delete the ID from data structure. 5122 if (_subscriptionID[topic]) { 5123 _hub.unsubscribe(_subscriptionID[topic]); 5124 delete _subscriptionID[topic]; 5125 } 5126 }, 5127 5128 /** 5129 * @private 5130 * Make a request to the request channel to have the Master subscribe 5131 * to a node. 5132 * @param {String} node 5133 * The node to subscribe to. 5134 */ 5135 subscribeNode: function (node, handler) { 5136 if (handler && typeof handler !== "function") { 5137 throw new Error("ClientServices.subscribeNode: handler is not a function"); 5138 } 5139 5140 // Construct the request to send to MasterPublisher through the OpenAjax Hub 5141 var data = { 5142 type: "SubscribeNodeReq", 5143 data: {node: node}, 5144 invokeID: _util.generateUUID() 5145 }, 5146 responseTopic = _topics.RESPONSES + "." + data.invokeID, 5147 _this = this; 5148 5149 // We need to first subscribe to the response channel 5150 this.subscribe(responseTopic, function (rsp) { 5151 // Since this channel is only used for this singular request, 5152 // we are not interested anymore. 5153 // This is also critical to not leaking memory by having OpenAjax 5154 // store a bunch of orphaned callback handlers that enclose on 5155 // our entire ClientServices singleton 5156 _this.unsubscribe(responseTopic); 5157 if (handler) { 5158 handler(data.invokeID, rsp); 5159 } 5160 }); 5161 // Then publish the request on the request channel 5162 _hub.publish(_topics.REQUESTS, data); 5163 }, 5164 5165 /** 5166 * @private 5167 * Make a request to the request channel to have the Master unsubscribe 5168 * from a node. 5169 * @param {String} node 5170 * The node to unsubscribe from. 5171 */ 5172 unsubscribeNode: function (node, subid, handler) { 5173 if (handler && typeof handler !== "function") { 5174 throw new Error("ClientServices.unsubscribeNode: handler is not a function"); 5175 } 5176 5177 // Construct the request to send to MasterPublisher through the OpenAjax Hub 5178 var data = { 5179 type: "UnsubscribeNodeReq", 5180 data: { 5181 node: node, 5182 subid: subid 5183 }, 5184 invokeID: _util.generateUUID() 5185 }, 5186 responseTopic = _topics.RESPONSES + "." + data.invokeID, 5187 _this = this; 5188 5189 // We need to first subscribe to the response channel 5190 this.subscribe(responseTopic, function (rsp) { 5191 // Since this channel is only used for this singular request, 5192 // we are not interested anymore. 5193 // This is also critical to not leaking memory by having OpenAjax 5194 // store a bunch of orphaned callback handlers that enclose on 5195 // our entire ClientServices singleton 5196 _this.unsubscribe(responseTopic); 5197 if (handler) { 5198 handler(rsp); 5199 } 5200 }); 5201 // Then publish the request on the request channel 5202 _hub.publish(_topics.REQUESTS, data); 5203 }, 5204 5205 /** 5206 * @private 5207 * Make a request to the request channel to have the Master connect to the XMPP server via BOSH 5208 */ 5209 makeConnectionReq : function () { 5210 // Disallow others (non-masters) from administering BOSH connections that are not theirs 5211 if (_isMaster && _publisher) { 5212 _publisher.connect(_config.id, _config.password, _config.xmppDomain); 5213 } else { 5214 _log("F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq"); 5215 } 5216 }, 5217 5218 /** 5219 * @private 5220 * Set's the global logger for this Client Services instance. 5221 * @param {Object} logger 5222 * Logger object with the following attributes defined:<ul> 5223 * <li><b>log:</b> function (msg) to simply log a message 5224 * </ul> 5225 */ 5226 setLogger: function (logger) { 5227 // We want to check the logger coming in so we don't have to check every time it is called. 5228 if (logger && typeof logger === "object" && typeof logger.log === "function") { 5229 _logger = logger; 5230 } else { 5231 // We are resetting it to an empty object so that _logger.log in .log is falsy. 5232 _logger = {}; 5233 } 5234 }, 5235 5236 /** 5237 * @private 5238 * Centralized logger.log method for external logger 5239 * @param {String} msg 5240 * Message to log 5241 */ 5242 log: _log, 5243 5244 /** 5245 * @class 5246 * Allow clients to make Finesse API requests and consume Finesse events by 5247 * calling a set of exposed functions. The Services layer will do the dirty 5248 * work of establishing a shared BOSH connection (for designated Master 5249 * modules), consuming events for client subscriptions, and constructing API 5250 * requests. 5251 * 5252 * @constructs 5253 */ 5254 _fakeConstuctor: function () { 5255 /* This is here so we can document init() as a method rather than as a constructor. */ 5256 }, 5257 5258 /** 5259 * Initiates the Client Services with the specified config parameters. 5260 * Enabling the Client Services as Master will trigger the establishment 5261 * of a BOSH event connection. 5262 * @param {finesse.gadget.Config} config 5263 * Configuration object containing properties used for making REST requests:<ul> 5264 * <li><b>host:</b> The Finesse server IP/host as reachable from the browser 5265 * <li><b>restHost:</b> The Finesse API IP/host as reachable from the gadget container 5266 * <li><b>id:</b> The ID of the user. This is an optional param as long as the 5267 * appropriate authorization string is provided, otherwise it is 5268 * required.</li> 5269 * <li><b>password:</b> The password belonging to the user. This is an optional param as 5270 * long as the appropriate authorization string is provided, 5271 * otherwise it is required.</li> 5272 * <li><b>authorization:</b> The base64 encoded "id:password" authentication string. This 5273 * param is provided to allow the ability to hide the password 5274 * param. If provided, the id and the password extracted from this 5275 * string will be used over the config.id and config.password.</li> 5276 * </ul> 5277 * @throws {Error} If required constructor parameter is missing. 5278 * @example 5279 * finesse.clientservices.ClientServices.init(finesse.gadget.Config); 5280 */ 5281 init: function (config) { 5282 if (!_inited) { 5283 //Validate the properties within the config object if one is provided. 5284 if (!(typeof config === "object" && 5285 typeof config.host === "string" && config.host.length > 0 && config.restHost && 5286 (typeof config.authorization === "string" || 5287 (typeof config.id === "string" && 5288 typeof config.password === "string")))) { 5289 throw new Error("Config object contains invalid properties."); 5290 } 5291 5292 // Initialize configuration 5293 _config = config; 5294 5295 // Set shortcuts 5296 _util = Utilities; 5297 _topics = Topics; 5298 5299 //TODO: document when this is properly supported 5300 // Allows hub and io dependencies to be passed in. Currently only used for unit tests. 5301 _hub = config.hub || gadgets.Hub; 5302 _io = config.io || gadgets.io; 5303 5304 //If the authorization string is provided, then use that to 5305 //extract the ID and the password. Otherwise use the ID and 5306 //password from the respective ID and password params. 5307 if (_config.authorization) { 5308 var creds = _util.getCredentials(_config.authorization); 5309 _config.id = creds.id; 5310 _config.password = creds.password; 5311 } 5312 else { 5313 _config.authorization = _util.b64Encode( 5314 _config.id + ":" + _config.password); 5315 } 5316 5317 _inited = true; 5318 5319 if (_hub) { 5320 //Subscribe to receive connection information. Since it is possible that 5321 //the client comes up after the Master comes up, the client will need 5322 //to make a request to have the Master send the latest connection info. 5323 //It would be possible that all clients get connection info again. 5324 this.subscribe(_topics.EVENTS_CONNECTION_INFO, _connInfoHandler); 5325 _makeConnectionInfoReq(); 5326 } 5327 } 5328 5329 //Return the CS object for object chaining. 5330 return this; 5331 }, 5332 5333 /** 5334 * @private 5335 * Initializes the BOSH component of this ClientServices instance. This establishes 5336 * the BOSH connection and will trigger the registered handlers as the connection 5337 * status changes respectively:<ul> 5338 * <li>registerOnConnectingHandler</li> 5339 * <li>registerOnConnectHandler</li> 5340 * <li>registerOnDisconnectHandler</li> 5341 * <li>registerOnDisconnectingHandler</li> 5342 * <li>registerOnReconnectingHandler</li> 5343 * <li>registerOnUnloadingHandler</li> 5344 * <ul> 5345 * 5346 * @param {Object} config 5347 * An object containing the following (optional) handlers for the request:<ul> 5348 * <li><b>xmppDomain:</b> {String} The domain of the XMPP server. Available from the SystemInfo object. 5349 * This is used to construct the JID: user@domain.com</li> 5350 * <li><b>pubsubDomain:</b> {String} The pub sub domain where the pub sub service is running. 5351 * Available from the SystemInfo object. 5352 * This is used for creating or removing subscriptions.</li> 5353 * <li><b>resource:</b> {String} The resource to connect to the notification server with.</li> 5354 * </ul> 5355 */ 5356 initBosh: function (config) { 5357 //Validate the properties within the config object if one is provided. 5358 if (!(typeof config === "object" && typeof config.xmppDomain === "string" && typeof config.pubsubDomain === "string")) { 5359 throw new Error("Config object contains invalid properties."); 5360 } 5361 5362 // Mixin the required information for establishing the BOSH connection 5363 _config.xmppDomain = config.xmppDomain; 5364 _config.pubsubDomain = config.pubsubDomain; 5365 _config.resource = config.resource; 5366 5367 //Initiate Master launch sequence 5368 _becomeMaster(); 5369 }, 5370 5371 /** 5372 * @private 5373 * Sets the failover mode to either FAILING or RECOVERED. This will only occur in the master instance and is meant to be 5374 * used by a stereotypical failover monitor type module to notify non-master instances (i.e. in gadgets) 5375 * @param {Object} failoverMode 5376 * true if failing, false or something falsy when recovered 5377 */ 5378 setFailoverMode: function (failoverMode) { 5379 if (_isMaster) { 5380 _hub.publish(_topics.EVENTS_CONNECTION_INFO, { 5381 status: (failoverMode ? _STATUS.FAILING : _STATUS.RECOVERED) 5382 }); 5383 } 5384 }, 5385 5386 /** 5387 * @private 5388 * Private accessor used to inject mocked private dependencies for unit testing 5389 */ 5390 _getTestObj: function () { 5391 return { 5392 setPublisher: function (publisher) { 5393 _publisher = publisher; 5394 } 5395 }; 5396 } 5397 }; 5398 }()); 5399 5400 window.finesse = window.finesse || {}; 5401 window.finesse.clientservices = window.finesse.clientservices || {}; 5402 window.finesse.clientservices.ClientServices = ClientServices; 5403 5404 return ClientServices; 5405 5406 }); 5407 5408 /** 5409 * The following comment prevents JSLint errors concerning undefined global variables. 5410 * It tells JSLint that these identifiers are defined elsewhere. 5411 */ 5412 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */ 5413 5414 /** The following comment is to prevent jslint errors about 5415 * using variables before they are defined. 5416 */ 5417 /*global Handlebars */ 5418 5419 /** 5420 * JavaScript class to implement common notification 5421 * functionality. 5422 * 5423 * @requires Class 5424 * @requires finesse.FinesseBase 5425 */ 5426 /** @private */ 5427 define('restservices/Notifier',[ 5428 'FinesseBase', 5429 'clientservices/ClientServices' 5430 ], 5431 function (FinesseBase, ClientServices) { 5432 var Notifier = FinesseBase.extend({ 5433 /** 5434 * Initializes the notifier object. 5435 */ 5436 init : function () { 5437 this._super(); 5438 this._listenerCallback = []; 5439 }, 5440 5441 /** 5442 * Add a listener. 5443 * 5444 * @param callback_function 5445 * @param scope 5446 * is the callback function to add 5447 */ 5448 addListener : function (callback_function, scope) { 5449 var len = this._listenerCallback.length, i, cb, add = true; 5450 for (i = 0; i < len; i += 1) { 5451 cb = this._listenerCallback[i].callback; 5452 if (cb === callback_function) { 5453 // this callback already exists 5454 add = false; 5455 break; 5456 } 5457 } 5458 if (add) { 5459 this._listenerCallback.push({ "callback": this._isAFunction(callback_function), "scope": (scope || window) }); 5460 } 5461 }, 5462 5463 /** 5464 * Remove a listener. 5465 * 5466 * @param callback_function 5467 * is the callback function to remove 5468 * @return {Boolean} true if removed 5469 */ 5470 removeListener : function (callback_function) { 5471 5472 var result = false, len = this._listenerCallback.length, i, cb; 5473 for (i = len - 1; i >= 0; i -=1) { 5474 cb = this._listenerCallback[i].callback; 5475 if (cb === callback_function) { 5476 this._listenerCallback[i] = undefined; 5477 this._listenerCallback.splice(i, 1); 5478 result = true; 5479 break; 5480 } 5481 } 5482 5483 return result; 5484 }, 5485 5486 /** 5487 * Removes all listeners 5488 * @return {undefined} 5489 */ 5490 reset: function () { 5491 this._listenerCallback = []; 5492 }, 5493 5494 /** 5495 * Notify all listeners. 5496 * 5497 * @param obj 5498 * is the object that has changed 5499 */ 5500 notifyListeners : function (obj) { 5501 var len = this._listenerCallback.length, i, callbackFunction, scope; 5502 5503 for (i = 0; i < len; i += 1) { 5504 // Be sure that one bad callback does not prevent other listeners 5505 // from receiving. 5506 try { 5507 callbackFunction = this._listenerCallback[i].callback; 5508 scope = this._listenerCallback[i].scope; 5509 if (typeof callbackFunction === 'function') { 5510 callbackFunction.call(scope, obj); 5511 } 5512 } catch (err) { 5513 ClientServices.log("Exception caught: " + err); 5514 } 5515 } 5516 }, 5517 5518 /** 5519 * Gets a copy of the listeners. 5520 * @return changeListenerCopy (array of callbacks) 5521 */ 5522 getListeners : function () { 5523 var changeListenerCopy = [], len = this._listenerCallback.length, i; 5524 5525 for (i = 0; i < len; i += 1) { 5526 changeListenerCopy.push(this._listenerCallback[i].callback); 5527 } 5528 5529 return changeListenerCopy; 5530 }, 5531 5532 /** 5533 * Verifies that the handler is function. 5534 * @param handler to verify 5535 * @return the handler 5536 * @throws Error if not a function 5537 */ 5538 _isAFunction : function (handler) { 5539 if (handler === undefined || typeof handler === "function") { 5540 return handler; 5541 } else { 5542 throw new Error("handler must be a function"); 5543 } 5544 } 5545 }); 5546 5547 window.finesse = window.finesse || {}; 5548 window.finesse.restservices = window.finesse.restservices || {}; 5549 window.finesse.restservices.Notifier = Notifier; 5550 5551 /** @namespace JavaScript classes and methods that represent REST objects and collections. */ 5552 finesse.restservices = finesse.restservices || {}; 5553 5554 return Notifier; 5555 }); 5556 5557 /** 5558 * JavaScript base object that all REST objects should inherit 5559 * from because it encapsulates and provides the common functionality that 5560 * all REST objects need. 5561 * 5562 * @requires finesse.clientservices.ClientServices 5563 * @requires Class 5564 */ 5565 5566 /** @private */ 5567 define('restservices/RestBase',[ 5568 "FinesseBase", 5569 "utilities/Utilities", 5570 "restservices/Notifier", 5571 "clientservices/ClientServices", 5572 "clientservices/Topics" 5573 ], 5574 function (FinesseBase, Utilities, Notifier, ClientServices, Topics) { 5575 5576 var RestBase = FinesseBase.extend(/** @lends finesse.restservices.RestBase.prototype */{ 5577 5578 doNotLog: false, 5579 5580 /** 5581 * Used by _processUpdate() and restRequest(). 5582 * Maps requestIds to object-wrapped callbacks passed to restRequest(), 5583 * so that one of the callbacks can be fired when a corresponding event is 5584 * received inside _processUpdate(). 5585 * @private 5586 */ 5587 _pendingCallbacks: {}, 5588 5589 /** 5590 * Gets the REST class for the current object. This object throws an 5591 * exception because subtype must implement. 5592 * @throws {Error} because subtype must implement 5593 * @private 5594 */ 5595 getRestClass: function () { 5596 throw new Error("getRestClass(): Not implemented in subtype."); 5597 }, 5598 5599 /** 5600 * Gets the REST type for the current object. This object throws an 5601 * exception because subtype must implement. 5602 * @throws {Error} because subtype must implement. 5603 * @private 5604 */ 5605 getRestType: function () { 5606 throw new Error("getRestType(): Not implemented in subtype."); 5607 }, 5608 5609 /** 5610 * Gets the node path for the current object. This object throws an 5611 * exception because subtype must implement. 5612 * @throws {Error} because subtype must implement. 5613 * @private 5614 */ 5615 getXMPPNodePath: function () { 5616 throw new Error("getXMPPNodePath(): Not implemented in subtype."); 5617 }, 5618 5619 /** 5620 * Boolean function that specifies whether the REST object supports 5621 * requests. True by default. Subclasses should override if false. 5622 * @private 5623 */ 5624 supportsRequests: true, 5625 5626 /** 5627 * Boolean function that specifies whether the REST object supports 5628 * subscriptions. True by default. Subclasses should override if false. 5629 * @private 5630 */ 5631 supportsSubscriptions: true, 5632 5633 /** 5634 * Boolean function that specifies whether the REST object should retain 5635 * a copy of the REST response. False by default. Subclasses should override if true. 5636 * @private 5637 */ 5638 keepRestResponse: false, 5639 5640 /** 5641 * Number that represents the REST Response status that is returned. Only 5642 * set if keepRestResponse is set to true. 5643 * @public 5644 */ 5645 restResponseStatus: null, 5646 5647 /** 5648 * Object to store additional headers to be sent with the REST Request, null by default. 5649 * @private 5650 */ 5651 extraHeaders: null, 5652 5653 /** 5654 * Boolean function that specifies whether the REST object explicitly 5655 * subscribes. False by default. Subclasses should override if true. 5656 * @private 5657 */ 5658 explicitSubscription: false, 5659 5660 /** 5661 * Boolean function that specifies whether subscribing should be 5662 * automatically done at construction. Defaults to true. 5663 * This be overridden at object construction, not by implementing subclasses 5664 * @private 5665 */ 5666 autoSubscribe: true, 5667 5668 /** 5669 * Private reference to default logger 5670 * @private 5671 */ 5672 _logger: { 5673 log: ClientServices.log, 5674 error: ClientServices.log 5675 }, 5676 5677 /** 5678 * @class 5679 * JavaScript representation of a REST object. Also exposes methods to operate 5680 * on the object against the server. This object is typically extended into individual 5681 * REST Objects (like Dialog, User, etc...), and shouldn't be used directly. 5682 * 5683 * @constructor 5684 * @param {String} id 5685 * The ID that uniquely identifies the REST object. 5686 * @param {Object} callbacks 5687 * An object containing callbacks for instantiation and runtime 5688 * Callback to invoke upon successful instantiation, passes in REST object. 5689 * @param {Function} callbacks.onLoad(this) 5690 * Callback to invoke upon loading the data for the first time. 5691 * @param {Function} callbacks.onChange(this) 5692 * Callback to invoke upon successful update object (PUT) 5693 * @param {Function} callbacks.onAdd(this) 5694 * Callback to invoke upon successful update to add object (POST) 5695 * @param {Function} callbacks.onDelete(this) 5696 * Callback to invoke upon successful update to delete object (DELETE) 5697 * @param {Function} callbacks.onError(rsp) 5698 * Callback to invoke on update error (refresh or event) 5699 * as passed by finesse.restservices.RestBase.restRequest() 5700 * { 5701 * status: {Number} The HTTP status code returned 5702 * content: {String} Raw string of response 5703 * object: {Object} Parsed object of response 5704 * error: {Object} Wrapped exception that was caught 5705 * error.errorType: {String} Type of error that was caught 5706 * error.errorMessage: {String} Message associated with error 5707 * } 5708 * @param {RestBase} [restObj] 5709 * A RestBase parent object which this object has an association with. 5710 * @constructs 5711 */ 5712 init: function (options, callbacks, restObj) { 5713 /** 5714 * Initialize the base class 5715 */ 5716 var _this = this; 5717 5718 this._super(); 5719 5720 if (typeof options === "object") { 5721 this._id = options.id; 5722 this._restObj = options.parentObj; 5723 this.autoSubscribe = (options.autoSubscribe === false) ? false : true; 5724 this.doNotSubscribe = options.doNotSubscribe; 5725 this.doNotRefresh = this.doNotRefresh || options.doNotRefresh; 5726 callbacks = { 5727 onLoad: options.onLoad, 5728 onChange: options.onChange, 5729 onAdd: options.onAdd, 5730 onDelete: options.onDelete, 5731 onError: options.onError 5732 }; 5733 } else { 5734 this._id = options; 5735 this._restObj = restObj; 5736 } 5737 5738 // Common stuff 5739 5740 this._data = {}; 5741 5742 //Contains the full rest response to be processed by upper layers if needed 5743 this._restResponse = undefined; 5744 5745 this._lastUpdate = {}; 5746 5747 this._util = Utilities; 5748 5749 //Should be correctly initialized in either a window OR gadget context 5750 this._config = finesse.container.Config; 5751 5752 // Setup all the notifiers - change, load and error. 5753 this._changeNotifier = new Notifier(); 5754 this._loadNotifier = new Notifier(); 5755 this._addNotifier = new Notifier(); 5756 this._deleteNotifier = new Notifier(); 5757 this._errorNotifier = new Notifier(); 5758 5759 this._loaded = false; 5760 5761 // Protect against null dereferencing of options allowing its 5762 // (nonexistent) keys to be read as undefined 5763 callbacks = callbacks || {}; 5764 5765 this.addHandler('load', callbacks.onLoad); 5766 this.addHandler('change', callbacks.onChange); 5767 this.addHandler('add', callbacks.onAdd); 5768 this.addHandler('delete', callbacks.onDelete); 5769 this.addHandler('error', callbacks.onError); 5770 5771 // Attempt to get the RestType then synchronize 5772 try { 5773 this.getRestType(); 5774 5775 // Only subscribe if this REST object supports subscriptions 5776 // and autoSubscribe was not requested to be disabled as a construction option 5777 if (this.supportsSubscriptions && this.autoSubscribe && !this.doNotSubscribe) { 5778 this.subscribe({ 5779 success: function () { 5780 //TODO: figure out how to use Function.call() or Function.apply() here... 5781 //this is exactly the same as the below else case other than the scope of "this" 5782 if (typeof options === "object" && options.data) { 5783 if (!_this._processObject(_this._normalize(options.data))) { 5784 // notify of error if we fail to construct 5785 _this._errorNotifier.notifyListeners(_this); 5786 } 5787 } else { 5788 // Only subscribe if this REST object supports requests 5789 if (_this.supportsRequests) { 5790 _this._synchronize(); 5791 } 5792 } 5793 }, 5794 error: function (err) { 5795 _this._errorNotifier.notifyListeners(err); 5796 } 5797 }); 5798 } else { 5799 if (typeof options === "object" && options.data) { 5800 if (!this._processObject(this._normalize(options.data))) { 5801 // notify of error if we fail to construct 5802 this._errorNotifier.notifyListeners(this); 5803 } 5804 } else { 5805 // Only subscribe if this REST object supports requests 5806 if (this.supportsRequests) { 5807 this._synchronize(); 5808 } 5809 } 5810 } 5811 5812 } catch (err) { 5813 this._logger.error('id=' + this._id + ': ' + err); 5814 } 5815 }, 5816 5817 /** 5818 * Determines if the object has a particular property. 5819 * @param obj is the object to examine 5820 * @param property is the property to check for 5821 * @returns {Boolean} 5822 */ 5823 hasProperty: function (obj, prop) { 5824 return (obj !== null) && (obj.hasOwnProperty(prop)); 5825 }, 5826 5827 /** 5828 * Gets a property from the object. 5829 * @param obj is the object to examine 5830 * @param property is the property to get 5831 * @returns {Property Value} or {Null} if not found 5832 */ 5833 getProperty: function (obj, property) { 5834 var result = null; 5835 5836 if (this.hasProperty(obj, property) === false) { 5837 result = null; 5838 } else { 5839 result = obj[property]; 5840 } 5841 return result; 5842 }, 5843 5844 /** 5845 * Utility to extracts the ID from the specified REST URI. This is with the 5846 * assumption that the ID is always the last element in the URI after the 5847 * "/" delimiter. 5848 * @param {String} restUri 5849 * The REST uri (i.e. /finesse/api/User/1000). 5850 * @private 5851 */ 5852 _extractId: function (restObj) { 5853 var obj, restUri = "", strLoc; 5854 for (obj in restObj) { 5855 if (restObj.hasOwnProperty(obj)) { 5856 restUri = restObj[obj].uri; 5857 break; 5858 } 5859 } 5860 return Utilities.getId(restUri); 5861 }, 5862 5863 /** 5864 * Gets the data for this object. 5865 * @returns {Object} which is contained in data 5866 */ 5867 getData: function () { 5868 return this._data; 5869 }, 5870 5871 /** 5872 * Gets the complete REST response to the request made 5873 * @returns {Object} which is contained in data 5874 * @private 5875 */ 5876 getRestResponse: function () { 5877 return this._restResponse; 5878 }, 5879 5880 /** 5881 * The REST URL in which this object can be referenced. 5882 * @return {String} 5883 * The REST URI for this object. 5884 * @private 5885 */ 5886 getRestUrl: function () { 5887 var 5888 restObj = this._restObj, 5889 restUrl = ""; 5890 5891 //Prepend the base REST object if one was provided. 5892 if (restObj instanceof RestBase) { 5893 restUrl += restObj.getRestUrl(); 5894 } 5895 //Otherwise prepend with the default webapp name. 5896 else { 5897 restUrl += "/finesse/api"; 5898 } 5899 5900 //Append the REST type. 5901 restUrl += "/" + this.getRestType(); 5902 5903 //Append ID if it is not undefined, null, or empty. 5904 if (this._id) { 5905 restUrl += "/" + this._id; 5906 } 5907 return restUrl; 5908 }, 5909 5910 /** 5911 * Getter for the id of this RestBase 5912 * @returns {String} 5913 * The id of this RestBase 5914 */ 5915 getId: function () { 5916 return this._id; 5917 }, 5918 5919 /** 5920 * Synchronize this object with the server using REST GET request. 5921 * @returns {Object} 5922 * { 5923 * abort: {function} Function that signifies the callback handler to NOT process the response of the rest request 5924 * } 5925 * @private 5926 */ 5927 _synchronize: function (retries) { 5928 // Fetch this REST object 5929 if (typeof this._id === "string") { 5930 var _this = this, isLoaded = this._loaded, _RETRY_INTERVAL = 10 * 1000; 5931 5932 return this._doGET( 5933 { 5934 success: function (rsp) { 5935 if (!_this._processResponse(rsp)) { 5936 if (retries > 0) { 5937 setTimeout(function () { 5938 _this._synchronize(retries - 1); 5939 }, _RETRY_INTERVAL); 5940 } else { 5941 _this._errorNotifier.notifyListeners(_this); 5942 } 5943 } else { 5944 // If this object was already "loaded" prior to 5945 // the _doGET request, then call the 5946 // changeNotifier 5947 if (isLoaded) { 5948 _this._changeNotifier.notifyListeners(_this); 5949 } 5950 } 5951 }, 5952 error: function (rsp) { 5953 if (retries > 0) { 5954 setTimeout(function () { 5955 _this._synchronize(retries - 1); 5956 }, _RETRY_INTERVAL); 5957 5958 } else { 5959 _this._errorNotifier.notifyListeners(rsp); 5960 } 5961 } 5962 } 5963 ); 5964 5965 } else { 5966 throw new Error("Can't construct a <" + this.getRestType() + "> due to invalid id type."); 5967 } 5968 }, 5969 5970 /** 5971 * Adds an handler to this object. 5972 * If notifierType is 'load' and the object has already loaded, the callback is invoked immediately 5973 * @param {String} notifierType 5974 * The type of notifier to add to ('load', 'change', 'add', 'delete', 'error') 5975 * @param {Function} callback 5976 * The function callback to invoke. 5977 * @example 5978 * // Handler for additions to the Dialogs collection object. 5979 * // When Dialog (a RestBase object) is created, add a change handler. 5980 * _handleDialogAdd = function(dialog) { 5981 * dialog.addHandler('change', _handleDialogChange); 5982 * } 5983 */ 5984 addHandler: function (notifierType, callback, scope) { 5985 var notifier = null; 5986 try { 5987 Utilities.validateHandler(callback); 5988 5989 notifier = this._getNotifierReference(notifierType); 5990 5991 notifier.addListener(callback, scope); 5992 5993 // If load handler is added and object has 5994 // already been loaded, invoke callback 5995 // immediately 5996 if (notifierType === 'load' && this._loaded) { 5997 callback.call((scope || window), this); 5998 } 5999 } catch (err) { 6000 this._logger.error('id=' + this._id + ': ' + err); 6001 } 6002 }, 6003 6004 /** 6005 * Removes a handler from this object. 6006 * @param {String} notifierType 6007 * The type of notifier to remove ('load', 'change', 'add', 'delete', 'error') 6008 * @param {Function} callback 6009 * The function to remove. 6010 */ 6011 removeHandler: function (notifierType, callback) { 6012 var notifier = null; 6013 try { 6014 Utilities.validateHandler(callback); 6015 6016 notifier = this._getNotifierReference(notifierType); 6017 6018 if (typeof(callback) === "undefined") 6019 { 6020 // Remove all listeners for the type 6021 notifier.reset(); 6022 } 6023 else 6024 { 6025 // Remove the specified listener 6026 finesse.utilities.Utilities.validateHandler(callback); 6027 notifier.removeListener(callback); 6028 } 6029 } catch (err) { 6030 this._logger.error('id=' + this._id + ': ' + err); 6031 } 6032 }, 6033 6034 /** 6035 * Utility method gating any operations that require complete instantiation 6036 * @throws Error 6037 * If this object was not fully instantiated yet 6038 * @returns {finesse.restservices.RestBase} 6039 * This RestBase object to allow cascading 6040 */ 6041 isLoaded: function () { 6042 if (!this._loaded) { 6043 throw new Error("Cannot operate on object that is not fully instantiated, use onLoad/onLoadError handlers"); 6044 } 6045 return this; // Allow cascading 6046 }, 6047 6048 6049 6050 /** 6051 * Force an update on this object. Since an asynchronous GET is performed, 6052 * it is necessary to have an onChange handler registered in order to be 6053 * notified when the response of this returns. 6054 * @param {Integer} retries 6055 * The number or retry attempts to make. 6056 * @returns {Object} 6057 * { 6058 * abort: {function} Function that signifies the callback handler to NOT process the response of the asynchronous request 6059 * } 6060 */ 6061 refresh: function (retries) { 6062 var _this = this; 6063 6064 if (this.explicitSubscription) { 6065 this._subscribeNode({ 6066 success: function () { 6067 //Disallow GETs if object doesn't support it. 6068 if (!_this.supportsRequests) { 6069 throw new Error("Object doesn't support request operations."); 6070 } 6071 6072 _this._synchronize(retries); 6073 6074 return this; // Allow cascading 6075 }, 6076 error: function (err) { 6077 _this._errorNotifier.notifyListeners(err); 6078 } 6079 }); 6080 } else { 6081 //Disallow GETs if object doesn't support it. 6082 if (!this.supportsRequests) { 6083 throw new Error("Object doesn't support request operations."); 6084 } 6085 6086 return this._synchronize(retries); 6087 } 6088 }, 6089 6090 /** 6091 * Utility method to validate against the known schema of this RestBase 6092 * @param {Object} obj 6093 * The object to validate 6094 * @returns {Boolean} 6095 * True if the object fits the schema of this object. This usually 6096 * means all required keys or nested objects are present. 6097 * False otherwise. 6098 * @private 6099 */ 6100 _validate: function (obj) { 6101 var valid = (typeof obj === "object" && this.hasProperty(obj, this.getRestType())); 6102 if (!valid) 6103 { 6104 this._logger.error(this.getRestType() + " failed validation! Does your JS REST class return the correct string from getRestType()?"); 6105 } 6106 return valid; 6107 }, 6108 6109 /** 6110 * Utility method to fetch this RestBase from the server 6111 * @param {finesse.interfaces.RequestHandlers} handlers 6112 * An object containing the handlers for the request 6113 * @returns {Object} 6114 * { 6115 * abort: {function} Function that signifies the callback handler to NOT process the response of the rest request 6116 * } 6117 * @private 6118 */ 6119 _doGET: function (handlers) { 6120 return this.restRequest(this.getRestUrl(), handlers); 6121 }, 6122 6123 /** 6124 * Common update event handler used by the pubsub callback closure. 6125 * Processes the update event then notifies listeners. 6126 * @param {Object} scope 6127 * An object containing callbacks to handle the asynchronous get 6128 * @param {Object} update 6129 * An object containing callbacks to handle the asynchronous get 6130 * @private 6131 */ 6132 _updateEventHandler: function (scope, update) { 6133 if (scope._processUpdate(update)) { 6134 switch (update.object.Update.event) { 6135 case "POST": 6136 scope._addNotifier.notifyListeners(scope); 6137 break; 6138 case "PUT": 6139 scope._changeNotifier.notifyListeners(scope); 6140 break; 6141 case "DELETE": 6142 scope._deleteNotifier.notifyListeners(scope); 6143 break; 6144 } 6145 } 6146 }, 6147 6148 /** 6149 * Utility method to create a callback to be given to OpenAjax to invoke when a message 6150 * is published on the topic of our REST URL (also XEP-0060 node). 6151 * This needs to be its own defined method so that subclasses can have their own implementation. 6152 * @returns {Function} callback(update) 6153 * The callback to be invoked when an update event is received. This callback will 6154 * process the update and notify listeners. 6155 * @private 6156 */ 6157 _createPubsubCallback: function () { 6158 var _this = this; 6159 return function (update) { 6160 _this._updateEventHandler(_this, update); 6161 }; 6162 }, 6163 6164 /** 6165 * Subscribe to pubsub infra using the REST URL as the topic name. 6166 * @param {finesse.interfaces.RequestHandlers} handlers 6167 * An object containing the handlers for the request 6168 * @private 6169 */ 6170 subscribe: function (callbacks) { 6171 // Only need to do a subscription to client pubsub. No need to trigger 6172 // a subscription on the Finesse server due to implicit subscribe (at 6173 // least for now). 6174 var _this = this, 6175 topic = Topics.getTopic(this.getRestUrl()), 6176 handlers, 6177 successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true); 6178 6179 callbacks = callbacks || {}; 6180 6181 handlers = { 6182 /** @private */ 6183 success: function () { 6184 // Add item to the refresh list in ClientServices to refresh if 6185 // we recover due to our resilient connection. However, do 6186 // not add if doNotRefresh flag is set. 6187 if (!_this.doNotRefresh) { 6188 ClientServices.addToRefreshList(_this); 6189 } 6190 6191 if (typeof callbacks.success === "function") { 6192 callbacks.success(); 6193 } 6194 }, 6195 /** @private */ 6196 error: function (err) { 6197 if (successful) { 6198 ClientServices.unsubscribe(topic); 6199 } 6200 6201 if (typeof callbacks.error === "function") { 6202 callbacks.error(err); 6203 } 6204 } 6205 }; 6206 6207 // Request a node subscription only if this object requires explicit subscriptions 6208 if (this.explicitSubscription === true) { 6209 this._subscribeNode(handlers); 6210 } else { 6211 if (successful) { 6212 this._subid = "OpenAjaxOnly"; 6213 handlers.success(); 6214 } else { 6215 handlers.error(); 6216 } 6217 } 6218 6219 return this; 6220 }, 6221 6222 /** 6223 * Unsubscribe to pubsub infra using the REST URL as the topic name. 6224 * @param {finesse.interfaces.RequestHandlers} handlers 6225 * An object containing the handlers for the request 6226 * @private 6227 */ 6228 unsubscribe: function (callbacks) { 6229 // Only need to do a subscription to client pubsub. No need to trigger 6230 // a subscription on the Finesse server due to implicit subscribe (at 6231 // least for now). 6232 var _this = this, 6233 topic = Topics.getTopic(this.getRestUrl()), 6234 handlers; 6235 6236 // no longer keep track of object to refresh on reconnect 6237 ClientServices.removeFromRefreshList(_this); 6238 6239 callbacks = callbacks || {}; 6240 6241 handlers = { 6242 /** @private */ 6243 success: function () { 6244 if (typeof callbacks.success === "function") { 6245 callbacks.success(); 6246 } 6247 }, 6248 /** @private */ 6249 error: function (err) { 6250 if (typeof callbacks.error === "function") { 6251 callbacks.error(err); 6252 } 6253 } 6254 }; 6255 6256 if (this._subid) { 6257 ClientServices.unsubscribe(topic); 6258 // Request a node unsubscribe only if this object requires explicit subscriptions 6259 if (this.explicitSubscription === true) { 6260 this._unsubscribeNode(handlers); 6261 } else { 6262 this._subid = undefined; 6263 handlers.success(); 6264 } 6265 } else { 6266 handlers.success(); 6267 } 6268 6269 return this; 6270 }, 6271 6272 /** 6273 * Private utility to perform node subscribe requests for explicit subscriptions 6274 * @param {finesse.interfaces.RequestHandlers} handlers 6275 * An object containing the handlers for the request 6276 * @private 6277 */ 6278 _subscribeNode: function (callbacks) { 6279 var _this = this; 6280 6281 // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined 6282 callbacks = callbacks || {}; 6283 6284 ClientServices.subscribeNode(this.getXMPPNodePath(), function (subid, err) { 6285 if (err) { 6286 if (typeof callbacks.error === "function") { 6287 callbacks.error(err); 6288 } 6289 } else { 6290 // Store the subid on a successful subscribe 6291 _this._subid = subid; 6292 if (typeof callbacks.success === "function") { 6293 callbacks.success(); 6294 } 6295 } 6296 }); 6297 }, 6298 6299 /** 6300 * Private utility to perform node unsubscribe requests for explicit subscriptions 6301 * @param {finesse.interfaces.RequestHandlers} handlers 6302 * An object containing the handlers for the request 6303 * @private 6304 */ 6305 _unsubscribeNode: function (callbacks) { 6306 var _this = this; 6307 6308 // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined 6309 callbacks = callbacks || {}; 6310 6311 ClientServices.unsubscribeNode(this.getXMPPNodePath(), this._subid, function (err) { 6312 _this._subid = undefined; 6313 if (err) { 6314 if (typeof callbacks.error === "function") { 6315 callbacks.error(err); 6316 } 6317 } else { 6318 if (typeof callbacks.success === "function") { 6319 callbacks.success(); 6320 } 6321 } 6322 }); 6323 }, 6324 6325 /** 6326 * Validate and store the object into the internal data store. 6327 * @param {Object} object 6328 * The JavaScript object that should match of schema of this REST object. 6329 * @returns {Boolean} 6330 * True if the object was validated and stored successfully. 6331 * @private 6332 */ 6333 _processObject: function (object) { 6334 if (this._validate(object)) { 6335 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here? 6336 6337 // If loaded for the first time, call the load notifiers. 6338 if (!this._loaded) { 6339 this._loaded = true; 6340 this._loadNotifier.notifyListeners(this); 6341 } 6342 6343 return true; 6344 } 6345 return false; 6346 }, 6347 6348 /** 6349 * Normalize the object to mitigate the differences between the backend 6350 * and what this REST object should hold. For example, the backend sends 6351 * send an event with the root property name being lower case. In order to 6352 * match the GET, the property should be normalized to an upper case. 6353 * @param {Object} object 6354 * The object which should be normalized. 6355 * @returns {Object} 6356 * Return the normalized object. 6357 * @private 6358 */ 6359 _normalize: function (object) { 6360 var 6361 restType = this.getRestType(), 6362 // Get the REST object name with first character being lower case. 6363 objRestType = restType.charAt(0).toLowerCase() + restType.slice(1); 6364 6365 // Normalize payload to match REST object. The payload for an update 6366 // use a lower case object name as oppose to upper case. Only normalize 6367 // if necessary. 6368 if (!this.hasProperty(object, restType) && this.hasProperty(object, objRestType)) { 6369 //Since the object is going to be modified, clone the object so that 6370 //it doesn't affect others (due to OpenAjax publishing to other 6371 //subscriber. 6372 object = jQuery.extend(true, {}, object); 6373 6374 object[restType] = object[objRestType]; 6375 delete(object[objRestType]); 6376 } 6377 return object; 6378 }, 6379 6380 /** 6381 * Utility method to process the response of a successful get 6382 * @param {Object} rsp 6383 * The response of a successful get 6384 * @returns {Boolean} 6385 * True if the update was successfully processed (the response object 6386 * passed the schema validation) and updated the internal data cache, 6387 * false otherwise. 6388 * @private 6389 */ 6390 _processResponse: function (rsp) { 6391 try { 6392 if (this.keepRestResponse) { 6393 this._restResponse = rsp.content; 6394 this.restResponseStatus = rsp.status; 6395 } 6396 return this._processObject(rsp.object); 6397 } 6398 catch (err) { 6399 this._logger.error(this.getRestType() + ': ' + err); 6400 } 6401 return false; 6402 }, 6403 6404 /** 6405 * Method that is called at the end of _processUpdate() which by default 6406 * will just delete the requestId-to-callbacks mapping but can be overridden. 6407 * @param {String} requestId The requestId of the event 6408 */ 6409 _postProcessUpdateStrategy: function (requestId) { 6410 //Clean up _pendingCallbacks now that we fired a callback corresponding to the received requestId. 6411 delete this._pendingCallbacks[requestId]; 6412 }, 6413 6414 /** 6415 * Utility method to process the update notification. 6416 * @param {Object} update 6417 * The payload of an update notification. 6418 * @returns {Boolean} 6419 * True if the update was successfully processed (the update object 6420 * passed the schema validation) and updated the internal data cache, 6421 * false otherwise. 6422 * @private 6423 */ 6424 _processUpdate: function (update) { 6425 try { 6426 var updateObj, requestId, fakeResponse, receivedError; 6427 6428 // The backend will send the data object with a lower case. To be 6429 // consistent with what should be represented in this object, the 6430 // object name should be upper case. This will normalize the object. 6431 updateObj = this._normalize(update.object.Update.data); 6432 6433 // Store the last event. 6434 this._lastUpdate = update.object; 6435 6436 requestId = this._lastUpdate.Update ? this._lastUpdate.Update.requestId : undefined; 6437 6438 if (requestId && this._pendingCallbacks[requestId]) { 6439 6440 /* 6441 * The passed success/error callbacks are expecting to be passed an AJAX response, so construct 6442 * a simulated/"fake" AJAX response object from the information in the received event. 6443 * The constructed object should conform to the contract for response objects specified 6444 * in _createAjaxHandler(). 6445 */ 6446 fakeResponse = {}; 6447 6448 //The contract says that rsp.content should contain the raw text of the response so we simulate that here. 6449 //For some reason json2xml has trouble with the native update object, so we serialize a clone of it by 6450 //doing a parse(stringify(update)). 6451 fakeResponse.content = this._util.json2xml(gadgets.json.parse(gadgets.json.stringify(update))); 6452 6453 fakeResponse.object = {}; 6454 6455 if (updateObj.apiErrors && updateObj.apiErrors.apiError) { //Error case 6456 6457 //TODO: The lowercase -> uppercase ApiErrors translation method below is undesirable, can it be improved? 6458 receivedError = updateObj.apiErrors.apiError; 6459 fakeResponse.object.ApiErrors = {}; 6460 fakeResponse.object.ApiErrors.ApiError = {}; 6461 fakeResponse.object.ApiErrors.ApiError.ErrorData = receivedError.errorData || undefined; 6462 fakeResponse.object.ApiErrors.ApiError.ErrorMessage = receivedError.errorMessage || undefined; 6463 fakeResponse.object.ApiErrors.ApiError.ErrorType = receivedError.errorType || undefined; 6464 6465 /* 6466 * Since this is the error case, supply the error callback with a '400 BAD REQUEST' status code. We don't know what the real 6467 * status code should be since the event we're constructing fakeResponse from doesn't include a status code. 6468 * This is just to conform to the contract for the error callback in _createAjaxHandler(). 6469 **/ 6470 fakeResponse.status = 400; 6471 6472 } else { //Success case 6473 6474 fakeResponse.object = this._lastUpdate; 6475 6476 /* 6477 * Since this is the success case, supply the success callback with a '200 OK' status code. We don't know what the real 6478 * status code should be since the event we're constructing fakeResponse from doesn't include a status code. 6479 * This is just to conform to the contract for the success callback in _createAjaxHandler(). 6480 **/ 6481 fakeResponse.status = 200; 6482 } 6483 6484 try { 6485 6486 if (fakeResponse.object.ApiErrors && this._pendingCallbacks[requestId].error) { 6487 this._pendingCallbacks[requestId].error(fakeResponse); 6488 } 6489 // HTTP 202 is handled as a success, besides, we cannot infer that a non-error is a success. 6490 /*else if (this._pendingCallbacks[requestId].success) { 6491 this._pendingCallbacks[requestId].success(fakeResponse); 6492 }*/ 6493 6494 } catch (callbackErr) { 6495 6496 this._logger.error(this.getRestType() + ": Caught error while firing callback: " + callbackErr); 6497 6498 } 6499 6500 this._postProcessUpdateStrategy(requestId); 6501 6502 } 6503 6504 return this._processObject(updateObj); 6505 } 6506 catch (err) { 6507 this._logger.error(this.getRestType() + ': ' + err); 6508 } 6509 return false; 6510 }, 6511 6512 /** 6513 * Utility method to create ajax response handler closures around the 6514 * provided callbacks. Callbacks should be passed through from .ajax(). 6515 * makeRequest is responsible for garbage collecting these closures. 6516 * @param {finesse.interfaces.RequestHandlers} handlers 6517 * An object containing the handlers for the request 6518 * @returns {Object} 6519 * { 6520 * abort: {function} Function that signifies the callback handler to NOT process the response when the response returns 6521 * callback: {function} Callback handler to be invoked when the response returns 6522 * } 6523 * @private 6524 */ 6525 _createAjaxHandler: function (options) { 6526 //We should not need to check this again since it has already been done in .restRequest() 6527 //options = options || {}; 6528 6529 //Flag to indicate whether or not to process the response 6530 var abort = false, 6531 6532 //Get a reference to the parent User object 6533 _this = this; 6534 6535 return { 6536 6537 abort: function () { 6538 abort = true; 6539 }, 6540 6541 callback: function (rsp) { 6542 6543 if (abort) { 6544 // Do not process the response 6545 return; 6546 } 6547 6548 var requestId, error = false, rspObj; 6549 6550 if (options.success || options.error) { 6551 rspObj = { 6552 status: rsp.rc, 6553 content: rsp.text 6554 }; 6555 6556 if (!_this.doNotLog) { 6557 _this._logger.log(_this.getRestType() + ": requestId='" + options.uuid + "', Returned with status=" + rspObj.status + ", content='" + rspObj.content + "'"); 6558 } 6559 6560 //Some responses may not have a body. 6561 if (rsp.text && rsp.text.length > 0) { 6562 try { 6563 rspObj.object = _this._util.xml2js(rsp.text); 6564 } catch (e) { 6565 error = true; 6566 rspObj.error = { 6567 errorType: "parseError", 6568 errorMessage: "Could not serialize XML: " + e 6569 }; 6570 } 6571 } else { 6572 rspObj.object = {}; 6573 } 6574 6575 if (!error && rspObj.status >= 200 && rspObj.status < 300) { 6576 if (options.success) { 6577 options.success(rspObj); 6578 } 6579 } else { 6580 if (options.error) { 6581 options.error(rspObj); 6582 } 6583 } 6584 6585 /* 6586 * If a synchronous error happened after a non-GET request (usually a validation error), we 6587 * need to clean up the request's entry in _pendingCallbacks since no corresponding event 6588 * will arrive later. The corresponding requestId should be present in the response headers. 6589 * 6590 * It appears Shindig changes the header keys to lower case, hence 'requestid' instead of 6591 * 'requestId' below. 6592 **/ 6593 if (rspObj.status !== 202 && rsp.headers && rsp.headers.requestid) { 6594 requestId = rsp.headers.requestid[0]; 6595 if (_this._pendingCallbacks[requestId]) { 6596 delete _this._pendingCallbacks[requestId]; 6597 } 6598 } 6599 } 6600 } 6601 }; 6602 }, 6603 6604 /** 6605 * Utility method to make an asynchronous request 6606 * @param {String} url 6607 * The unencoded URL to which the request is sent (will be encoded) 6608 * @param {Object} options 6609 * An object containing additional options for the request. 6610 * @param {Object} options.content 6611 * An object to send in the content body of the request. Will be 6612 * serialized into XML before sending. 6613 * @param {String} options.method 6614 * The type of request. Defaults to "GET" when none is specified. 6615 * @param {Function} options.success(rsp) 6616 * A callback function to be invoked for a successful request. 6617 * { 6618 * status: {Number} The HTTP status code returned 6619 * content: {String} Raw string of response 6620 * object: {Object} Parsed object of response 6621 * } 6622 * @param {Function} options.error(rsp) 6623 * A callback function to be invoked for an unsuccessful request. 6624 * { 6625 * status: {Number} The HTTP status code returned 6626 * content: {String} Raw string of response 6627 * object: {Object} Parsed object of response 6628 * error: {Object} Wrapped exception that was caught 6629 * error.errorType: {String} Type of error that was caught 6630 * error.errorMessage: {String} Message associated with error 6631 * } 6632 * @returns {Object} 6633 * { 6634 * abort: {function} Function that signifies the callback handler to NOT process the response of this asynchronous request 6635 * } 6636 * @private 6637 */ 6638 restRequest: function (url, options) { 6639 6640 var params, encodedUrl, ajaxHandler; 6641 6642 params = {}; 6643 6644 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 6645 options = options || {}; 6646 options.success = this._util.validateHandler(options.success); 6647 options.error = this._util.validateHandler(options.error); 6648 6649 // Request Headers 6650 params[gadgets.io.RequestParameters.HEADERS] = this.extraHeaders || {}; 6651 6652 // HTTP method is a passthrough to gadgets.io.makeRequest, makeRequest defaults to GET 6653 params[gadgets.io.RequestParameters.METHOD] = options.method; 6654 6655 //true if this should be a GET request, false otherwise 6656 if (!options.method || options.method === "GET") { 6657 //Disable caching for GETs 6658 if (url.indexOf("?") > -1) { 6659 url += "&"; 6660 } else { 6661 url += "?"; 6662 } 6663 url += "nocache=" + this._util.currentTimeMillis(); 6664 } else { 6665 /** 6666 * If not GET, generate a requestID and add it to the headers, then wrap 6667 * callbacks into an object and store it in _pendingCallbacks. 6668 * If we receive a synchronous error response instead of a 202 as expected, 6669 * the AJAX handler will clean up _pendingCallbacks. 6670 **/ 6671 /* 6672 * TODO: Clean up _pendingCallbacks if an entry persists after a certain amount of time has passed. 6673 * In the block below, can store the current time (new Date().getTime()) alongside the 6674 * callbacks in the new _pendingCallbacks entry. Then iterate through a copty of _pendingCallbacks, 6675 * deleting all entries inside _pendingCallbacks that are older than a certain threshold (2 minutes for example.) 6676 * This solves a potential memory leak issue if we never receive an event for a given stored requestId; 6677 * we don't want to store unfired callbacks forever. 6678 */ 6679 /** @private */ 6680 options.uuid = this._util.generateUUID(); 6681 params[gadgets.io.RequestParameters.HEADERS].requestId = options.uuid; 6682 //By default, Shindig strips nearly all of the response headers, but this parameter tells Shindig 6683 //to send the headers through unmodified; we need to be able to read the 'requestId' header if we 6684 //get a synchronous error as a result of a non-GET request. (See the bottom of _createAjaxHandler().) 6685 params[gadgets.io.RequestParameters.GET_FULL_HEADERS] = "true"; 6686 this._pendingCallbacks[options.uuid] = {}; 6687 this._pendingCallbacks[options.uuid].success = options.success; 6688 this._pendingCallbacks[options.uuid].error = options.error; 6689 } 6690 6691 encodedUrl = encodeURI(url) + (window.errorOnRestRequest ? "ERROR" : ""); 6692 6693 if (!this.doNotLog) { 6694 this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', Making REST request: method=" + (options.method || "GET") + ", url='" + encodedUrl + "'"); 6695 } 6696 6697 // Content Body 6698 if (typeof options.content === "object") { 6699 // Content Type 6700 params[gadgets.io.RequestParameters.HEADERS]["Content-Type"] = "application/xml"; 6701 // Content 6702 params[gadgets.io.RequestParameters.POST_DATA] = this._util.js2xml(options.content); 6703 6704 if (!this.doNotLog) { 6705 this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', POST_DATA='" + params[gadgets.io.RequestParameters.POST_DATA] + "'"); 6706 } 6707 } 6708 6709 ajaxHandler = this._createAjaxHandler(options); 6710 ClientServices.makeRequest(encodedUrl, ajaxHandler.callback, params); 6711 6712 return { 6713 abort: ajaxHandler.abort 6714 }; 6715 }, 6716 6717 /** 6718 * Retrieves a reference to a particular notifierType. 6719 * @param notifierType is a string which indicates the notifier to retrieve 6720 * ('load', 'change', 'add', 'delete', 'error') 6721 * @return {Notifier} 6722 * @private 6723 */ 6724 _getNotifierReference: function (notifierType) { 6725 var notifierReference = null; 6726 if (notifierType === 'load') { 6727 notifierReference = this._loadNotifier; 6728 } else if (notifierType === 'change') { 6729 notifierReference = this._changeNotifier; 6730 } else if (notifierType === 'add') { 6731 notifierReference = this._addNotifier; 6732 } else if (notifierType === 'delete') { 6733 notifierReference = this._deleteNotifier; 6734 } else if (notifierType === 'error') { 6735 notifierReference = this._errorNotifier; 6736 } else { 6737 throw new Error("_getNotifierReference(): Trying to get unknown notifier(notifierType=" + notifierType + ")"); 6738 } 6739 6740 return notifierReference; 6741 } 6742 }); 6743 6744 window.finesse = window.finesse || {}; 6745 window.finesse.restservices = window.finesse.restservices || {}; 6746 window.finesse.restservices.RestBase = RestBase; 6747 6748 return RestBase; 6749 }); 6750 6751 /** The following comment is to prevent jslint errors about 6752 * using variables before they are defined. 6753 */ 6754 /*global finesse*/ 6755 6756 /** 6757 * JavaScript base object that all REST collection objects should 6758 * inherit from because it encapsulates and provides the common functionality 6759 * that all REST objects need. 6760 * 6761 * @requires finesse.clientservices.ClientServices 6762 * @requires Class 6763 * @requires finesse.FinesseBase 6764 * @requires finesse.restservices.RestBase 6765 */ 6766 6767 /** 6768 * @class 6769 * JavaScript representation of a REST collection object. 6770 * 6771 * @constructor 6772 * @param {Function} callbacks.onCollectionAdd(this) 6773 * Callback to invoke upon successful item addition to the collection. 6774 * @param {Function} callbacks.onCollectionDelete(this) 6775 * Callback to invoke upon successful item deletion from the collection. 6776 * @borrows finesse.restservices.RestBase as finesse.restservices.RestCollectionBase 6777 */ 6778 /** @private */ 6779 define('restservices/RestCollectionBase',[ 6780 'restservices/RestBase', 6781 'utilities/Utilities', 6782 'restservices/Notifier' 6783 ], 6784 function (RestBase, Utilities, Notifier) { 6785 var RestCollectionBase = RestBase.extend(/** @lends finesse.restservices.RestCollectionBase.prototype */{ 6786 6787 /** 6788 * Boolean function that specifies whether the collection handles subscribing 6789 * and propagation of events for the individual REST object items the 6790 * collection holds. False by default. Subclasses should override if true. 6791 * @private 6792 */ 6793 supportsRestItemSubscriptions: false, 6794 6795 /** 6796 * Gets the constructor the individual items that make of the collection. 6797 * For example, a Dialogs collection object will hold a list of Dialog items. 6798 * @throws Error because subtype must implement. 6799 * @private 6800 */ 6801 getRestItemClass: function () { 6802 throw new Error("getRestItemClass(): Not implemented in subtype."); 6803 }, 6804 6805 /** 6806 * Gets the REST type of the individual items that make of the collection. 6807 * For example, a Dialogs collection object will hold a list of Dialog items. 6808 * @throws Error because subtype must implement. 6809 * @private 6810 */ 6811 getRestItemType: function () { 6812 throw new Error("getRestItemType(): Not implemented in subtype."); 6813 }, 6814 6815 /** 6816 * The base REST URL in which items this object contains can be referenced. 6817 * @return {String} 6818 * The REST URI for items this object contains. 6819 * @private 6820 */ 6821 getRestItemBaseUrl: function () { 6822 var 6823 restUrl = "/finesse/api"; 6824 6825 //Append the REST type. 6826 restUrl += "/" + this.getRestItemType(); 6827 6828 return restUrl; 6829 }, 6830 6831 /* 6832 * Creates a new object from the given data 6833 * @param data - data object 6834 * @private 6835 */ 6836 _objectCreator: function (data) { 6837 var objectId = this._extractId(data), 6838 newRestObj = this._collection[objectId], 6839 _this = this; 6840 6841 //Prevent duplicate entries into collection. 6842 if (!newRestObj) { 6843 //Create a new REST object using the subtype defined by the 6844 //overridden method. 6845 newRestObj = new (this.getRestItemClass())({ 6846 doNotSubscribe: this.handlesItemSubscription, 6847 doNotRefresh: this.handlesItemRefresh, 6848 id: objectId, 6849 data: data, 6850 onLoad: function (newObj) { 6851 //Normalize and add REST object to collection datastore. 6852 _this._collection[objectId] = newObj; 6853 _this._collectionAddNotifier.notifyListeners(newObj); 6854 _this.length += 1; 6855 } 6856 }); 6857 } 6858 else { 6859 //If entry already exist in collection, process the new event, 6860 //and notify all change listeners since an existing object has 6861 //change. This could happen in the case when the Finesse server 6862 //cycles, and sends a snapshot of the user's calls. 6863 newRestObj._processObject(data); 6864 newRestObj._changeNotifier.notifyListeners(newRestObj); 6865 } 6866 }, 6867 6868 /* 6869 * Deletes and object and notifies its handlers 6870 * @param data - data object 6871 * @private 6872 */ 6873 _objectDeleter: function (data) { 6874 var objectId = this._extractId(data), 6875 object = this._collection[objectId]; 6876 if (object) { 6877 //Even though this is a delete, let's make sure the object we are passing has got good data 6878 object._processObject(data); 6879 //Notify listeners and delete from internal datastore. 6880 this._collectionDeleteNotifier.notifyListeners(object); 6881 delete this._collection[objectId]; 6882 this.length -= 1; 6883 } 6884 }, 6885 6886 /** 6887 * Creates an anonymous function for notifiying error listeners of a particular object 6888 * data. 6889 * @param obj - the objects whose error listeners to notify 6890 * @returns {Function} 6891 * Callback for notifying of errors 6892 * @private 6893 */ 6894 _createErrorNotifier: function (obj) { 6895 return function (err) { 6896 obj._errorNotifier.notifyListeners(err); 6897 }; 6898 }, 6899 6900 /** 6901 * Replaces the collection with a refreshed list using the passed in 6902 * data. 6903 * @param data - data object (usually this._data) 6904 * @private 6905 */ 6906 _buildRefreshedCollection: function (data) { 6907 var i, dataObject, object, objectId, dataArray, newIds = [], foundFlag; 6908 if (data && this.getProperty(data, this.getRestItemType()) !== null) { 6909 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType())); 6910 } else { 6911 dataArray = []; 6912 } 6913 6914 // iterate through each item in the new data and add to or update collection 6915 for (i = 0; i < dataArray.length; i += 1) { 6916 dataObject = {}; 6917 dataObject[this.getRestItemType()] = dataArray[i]; 6918 objectId = this._extractId(dataObject); 6919 6920 this._objectCreator(dataObject); 6921 newIds.push(objectId); 6922 6923 // resubscribe if the object requires an explicit subscription 6924 object = this._collection[objectId]; 6925 if (this.handlesItemRefresh && object.explicitSubscription) { 6926 object._subscribeNode({ 6927 error: this._createErrorNotifier(object) 6928 }); 6929 } 6930 } 6931 6932 // now clean up items (if any) that were removed 6933 for (objectId in this._collection) { 6934 if (this._collection.hasOwnProperty(objectId)) { 6935 foundFlag = false; 6936 for (i = newIds.length - 1; i >= 0; i -= 1) { 6937 if (newIds[i] === objectId) { 6938 foundFlag = true; 6939 break; 6940 } 6941 } 6942 // did not find in updated list, so delete it 6943 if (!foundFlag) { 6944 this._objectDeleter({'data': this._collection[objectId]._data}); 6945 } 6946 } 6947 } 6948 }, 6949 6950 /** 6951 * The actual refresh operation, refactored out so we don't have to repeat code 6952 * @private 6953 */ 6954 _RESTRefresh: function () { 6955 var _this = this; 6956 this._doGET({ 6957 success: function(rsp) { 6958 if (_this._processResponse(rsp)) { 6959 _this._buildRefreshedCollection(_this._data); 6960 } else { 6961 _this._errorNotifier.notifyListeners(_this); 6962 } 6963 }, 6964 error: function(rsp) { 6965 _this._errorNotifier.notifyListeners(rsp); 6966 } 6967 }); 6968 }, 6969 6970 /** 6971 * Force an update on this object. Since an asynchronous GET is performed, 6972 * it is necessary to have an onChange handler registered in order to be 6973 * notified when the response of this returns. 6974 * @returns {finesse.restservices.RestBaseCollection} 6975 * This RestBaseCollection object to allow cascading 6976 */ 6977 refresh: function() { 6978 var _this = this, isLoaded = this._loaded; 6979 6980 // resubscribe if the collection requires an explicit subscription 6981 if (this.explicitSubscription) { 6982 this._subscribeNode({ 6983 success: function () { 6984 _this._RESTRefresh(); 6985 }, 6986 error: function (err) { 6987 _this._errorNotifier.notifyListeners(err); 6988 } 6989 }); 6990 } else { 6991 this._RESTRefresh(); 6992 } 6993 6994 return this; // Allow cascading 6995 }, 6996 6997 /** 6998 * @private 6999 * The _addHandlerCb and _deleteHandlerCb require that data be passed in the 7000 * format of an array of {(Object Type): object} objects. For example, a 7001 * queues object would return [{Queue: queue1}, {Queue: queue2}, ...]. 7002 * @param skipOuterObject If {true} is passed in for this param, then the "data" 7003 * property is returned instead of an object with the 7004 * data appended. 7005 * @return {Array} 7006 */ 7007 extractCollectionData: function (skipOuterObject) { 7008 var restObjs, 7009 obj, 7010 result = [], 7011 _this = this; 7012 7013 if (this._data) 7014 { 7015 restObjs = this._data[this.getRestItemType()]; 7016 7017 if (restObjs) 7018 { 7019 // check if there are multiple objects to pass 7020 if (!$.isArray(restObjs)) 7021 { 7022 restObjs = [restObjs]; 7023 } 7024 7025 // if so, create an object for each and add to result array 7026 $.each(restObjs, function (id, object) { 7027 if (skipOuterObject === true) 7028 { 7029 obj = object; 7030 } 7031 else 7032 { 7033 obj = {}; 7034 obj[_this.getRestItemType()] = object; 7035 } 7036 result.push(obj); 7037 }); 7038 } 7039 7040 } 7041 7042 return result; 7043 }, 7044 7045 /** 7046 * For Finesse, collections are handled uniquely on a POST and 7047 * doesn't necessary follow REST conventions. A POST on a collection 7048 * doesn't mean that the collection has been created, it means that an 7049 * item has been added to the collection. This function will generate 7050 * a closure which will handle this logic appropriately. 7051 * @param {Object} scope 7052 * The scope of where the callback should be invoked. 7053 * @private 7054 */ 7055 _addHandlerCb: function (scope) { 7056 return function (restItem) { 7057 var data = restItem.extractCollectionData(); 7058 7059 $.each(data, function (id, object) { 7060 scope._objectCreator(object); 7061 }); 7062 }; 7063 }, 7064 7065 /** 7066 * For Finesse, collections are handled uniquely on a DELETE and 7067 * doesn't necessary follow REST conventions. A DELETE on a collection 7068 * doesn't mean that the collection has been deleted, it means that an 7069 * item has been deleted from the collection. This function will generate 7070 * a closure which will handle this logic appropriately. 7071 * @param {Object} scope 7072 * The scope of where the callback should be invoked. 7073 * @private 7074 */ 7075 _deleteHandlerCb: function (scope) { 7076 return function (restItem) { 7077 var data = restItem.extractCollectionData(); 7078 7079 $.each(data, function (id, obj) { 7080 scope._objectDeleter(obj); 7081 }); 7082 }; 7083 }, 7084 7085 /** 7086 * Utility method to process the update notification for Rest Items 7087 * that are children of the collection whose events are published to 7088 * the collection's node. 7089 * @param {Object} update 7090 * The payload of an update notification. 7091 * @returns {Boolean} 7092 * True if the update was successfully processed (the update object 7093 * passed the schema validation) and updated the internal data cache, 7094 * false otherwise. 7095 * @private 7096 */ 7097 _processRestItemUpdate: function (update) { 7098 var object, objectId, updateObj = update.object.Update; 7099 7100 //Extract the ID from the source if the Update was an error. 7101 if (updateObj.data.apiErrors) { 7102 objectId = Utilities.getId(updateObj.source); 7103 } 7104 //Otherwise extract from the data object itself. 7105 else { 7106 objectId = this._extractId(updateObj.data); 7107 } 7108 7109 object = this._collection[objectId]; 7110 if (object) { 7111 if (object._processUpdate(update)) { 7112 switch (updateObj.event) { 7113 case "POST": 7114 object._addNotifier.notifyListeners(object); 7115 break; 7116 case "PUT": 7117 object._changeNotifier.notifyListeners(object); 7118 break; 7119 case "DELETE": 7120 object._deleteNotifier.notifyListeners(object); 7121 break; 7122 } 7123 } 7124 } 7125 }, 7126 7127 /** 7128 * SUBCLASS IMPLEMENTATION (override): 7129 * For collections, this callback has the additional responsibility of passing events 7130 * of collection item updates to the item objects themselves. The collection needs to 7131 * do this because item updates are published to the collection's node. 7132 * @returns {Function} 7133 * The callback to be invoked when an update event is received 7134 * @private 7135 */ 7136 _createPubsubCallback: function () { 7137 var _this = this; 7138 return function (update) { 7139 //If the source of the update is our REST URL, this means the collection itself is modified 7140 if (update.object.Update.source === _this.getRestUrl()) { 7141 _this._updateEventHandler(_this, update); 7142 } else { 7143 //Otherwise, it is safe to assume that if we got an event on our topic, it must be a 7144 //rest item update of one of our children that was published on our node (OpenAjax topic) 7145 _this._processRestItemUpdate(update); 7146 } 7147 }; 7148 }, 7149 7150 /** 7151 * @class 7152 * This is the base collection object. 7153 * 7154 * @constructs 7155 * @augments finesse.restservices.RestBase 7156 * @see finesse.restservices.Contacts 7157 * @see finesse.restservices.Dialogs 7158 * @see finesse.restservices.PhoneBooks 7159 * @see finesse.restservices.Queues 7160 * @see finesse.restservices.WorkflowActions 7161 * @see finesse.restservices.Workflows 7162 * @see finesse.restservices.WrapUpReasons 7163 */ 7164 _fakeConstuctor: function () { 7165 /* This is here to hide the real init constructor from the public docs */ 7166 }, 7167 7168 /** 7169 * @private 7170 * @param {Object} options 7171 * An object with the following properties:<ul> 7172 * <li><b>id:</b> The id of the object being constructed</li> 7173 * <li><b>onCollectionAdd(this): (optional)</b> when an object is added to this collection</li> 7174 * <li><b>onCollectionDelete(this): (optional)</b> when an object is removed from this collection</li> 7175 * <li><b>onLoad(this): (optional)</b> when the collection is successfully loaded from the server</li> 7176 * <li><b>onChange(this): (optional)</b> when an update notification of the collection is received. 7177 * This does not include adding and deleting members of the collection</li> 7178 * <li><b>onAdd(this): (optional)</b> when a notification that the collection is created is received</li> 7179 * <li><b>onDelete(this): (optional)</b> when a notification that the collection is deleted is received</li> 7180 * <li><b>onError(rsp): (optional)</b> if loading of the collection fails, invoked with the error response object:<ul> 7181 * <li><b>status:</b> {Number} The HTTP status code returned</li> 7182 * <li><b>content:</b> {String} Raw string of response</li> 7183 * <li><b>object:</b> {Object} Parsed object of response</li> 7184 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 7185 * <li><b>errorType:</b> {String} Type of error that was caught</li> 7186 * <li><b>errorMessage:</b> {String} Message associated with error</li> 7187 * </ul></li> 7188 * </ul></li> 7189 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 7190 **/ 7191 init: function (options) { 7192 7193 options = options || {}; 7194 options.id = ""; 7195 7196 //Make internal datastore collection to hold a list of objects. 7197 this._collection = {}; 7198 this.length = 0; 7199 7200 //Collections will have additional callbacks that will be invoked when 7201 //an item has been added/deleted. 7202 this._collectionAddNotifier = new Notifier(); 7203 this._collectionDeleteNotifier = new Notifier(); 7204 7205 //Initialize the base class. 7206 this._super(options); 7207 7208 this.addHandler('collectionAdd', options.onCollectionAdd); 7209 this.addHandler('collectionDelete', options.onCollectionDelete); 7210 7211 //For Finesse, collections are handled uniquely on a POST/DELETE and 7212 //doesn't necessary follow REST conventions. A POST on a collection 7213 //doesn't mean that the collection has been created, it means that an 7214 //item has been added to the collection. A DELETE means that an item has 7215 //been removed from the collection. Due to this, we are attaching 7216 //special callbacks to the add/delete that will handle this logic. 7217 this.addHandler("add", this._addHandlerCb(this)); 7218 this.addHandler("delete", this._deleteHandlerCb(this)); 7219 }, 7220 7221 /** 7222 * Returns the collection. 7223 * @returns {Object} 7224 * The collection as an object 7225 */ 7226 getCollection: function () { 7227 //TODO: is this safe? or should we instead return protected functions such as .each(function)? 7228 return this._collection; 7229 }, 7230 7231 /** 7232 * Utility method to build the internal collection data structure (object) based on provided data 7233 * @param {Object} data 7234 * The data to build the internal collection from 7235 * @private 7236 */ 7237 _buildCollection: function (data) { 7238 var i, object, objectId, dataArray; 7239 if (data && this.getProperty(data, this.getRestItemType()) !== null) { 7240 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType())); 7241 for (i = 0; i < dataArray.length; i += 1) { 7242 7243 object = {}; 7244 object[this.getRestItemType()] = dataArray[i]; 7245 objectId = this._extractId(object); 7246 this._collection[objectId] = new (this.getRestItemClass())({ 7247 doNotSubscribe: this.handlesItemSubscription, 7248 doNotRefresh: this.handlesItemRefresh, 7249 id: objectId, 7250 data: object 7251 }); 7252 this.length += 1; 7253 } 7254 } 7255 }, 7256 7257 /** 7258 * Called to know whether to include an item in the _collection and _data. Return false to keep it, true to filter out (discard) it. 7259 * Override this in subclasses if you need only object with certain attribute values. 7260 * @param {Object} item Item to test. 7261 * @return {Boolean} False to keep, true to filter out (discard); 7262 */ 7263 _filterOutItem: function (item) { 7264 return false; 7265 }, 7266 7267 /** 7268 * Validate and store the object into the internal data store. 7269 * SUBCLASS IMPLEMENTATION (override): 7270 * Performs collection specific logic to _buildCollection internally based on provided data 7271 * @param {Object} object 7272 * The JavaScript object that should match of schema of this REST object. 7273 * @returns {Boolean} 7274 * True if the object was validated and stored successfully. 7275 * @private 7276 */ 7277 _processObject: function (object) { 7278 var i, 7279 restItemType = this.getRestItemType(), 7280 items; 7281 if (this._validate(object)) { 7282 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here? 7283 7284 // If a subclass has overriden _filterOutItem then we'll need to run through the items and remove them 7285 if (this._data) 7286 { 7287 items = this._data[restItemType]; 7288 7289 if (typeof(items) !== "undefined") 7290 { 7291 if (typeof(items.length) === "undefined") 7292 { 7293 // Single object 7294 if (this._filterOutItem(items)) 7295 { 7296 this._data[restItemType] = items = []; 7297 } 7298 7299 } 7300 else 7301 { 7302 // filter out objects 7303 for (i = items.length - 1; i !== -1; i = i - 1) 7304 { 7305 if (this._filterOutItem(items[i])) 7306 { 7307 items.splice(i, 1); 7308 } 7309 } 7310 } 7311 } 7312 } 7313 7314 // If loaded for the first time, call the load notifiers. 7315 if (!this._loaded) { 7316 this._buildCollection(this._data); 7317 this._loaded = true; 7318 this._loadNotifier.notifyListeners(this); 7319 } 7320 7321 return true; 7322 7323 } 7324 return false; 7325 }, 7326 7327 /** 7328 * Retrieves a reference to a particular notifierType. 7329 * @param {String} notifierType 7330 * Specifies the notifier to retrieve (load, change, error, add, delete) 7331 * @return {Notifier} The notifier object. 7332 */ 7333 _getNotifierReference: function (notifierType) { 7334 var notifierReference; 7335 7336 try { 7337 //Use the base method to get references for load/change/error. 7338 notifierReference = this._super(notifierType); 7339 } catch (err) { 7340 //Check for add/delete 7341 if (notifierType === "collectionAdd") { 7342 notifierReference = this._collectionAddNotifier; 7343 } else if (notifierType === "collectionDelete") { 7344 notifierReference = this._collectionDeleteNotifier; 7345 } else { 7346 //Rethrow exception from base class. 7347 throw err; 7348 } 7349 } 7350 return notifierReference; 7351 } 7352 }); 7353 7354 window.finesse = window.finesse || {}; 7355 window.finesse.restservices = window.finesse.restservices || {}; 7356 window.finesse.restservices.RestCollectionBase = RestCollectionBase; 7357 7358 return RestCollectionBase; 7359 }); 7360 7361 /** 7362 * JavaScript representation of the Finesse Dialog object. 7363 * 7364 * @requires finesse.clientservices.ClientServices 7365 * @requires Class 7366 * @requires finesse.FinesseBase 7367 * @requires finesse.restservices.RestBase 7368 */ 7369 7370 /** @private */ 7371 define('restservices/Dialog',[ 7372 'restservices/RestBase', 7373 'utilities/Utilities' 7374 ], 7375 function (RestBase, Utilities) { 7376 var Dialog = RestBase.extend(/** @lends finesse.restservices.Dialog.prototype */{ 7377 7378 /** 7379 * @class 7380 * A Dialog is an attempted connection between or among multiple participants, 7381 * for example, a regular phone call, a conference, or a silent monitor session. 7382 * 7383 * @augments finesse.restservices.RestBase 7384 * @constructs 7385 */ 7386 _fakeConstuctor: function () { 7387 /* This is here to hide the real init constructor from the public docs */ 7388 }, 7389 7390 /** 7391 * @private 7392 * 7393 * @param {Object} options 7394 * An object with the following properties:<ul> 7395 * <li><b>id:</b> The id of the object being constructed</li> 7396 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 7397 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 7398 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 7399 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 7400 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 7401 * <li><b>status:</b> {Number} The HTTP status code returned</li> 7402 * <li><b>content:</b> {String} Raw string of response</li> 7403 * <li><b>object:</b> {Object} Parsed object of response</li> 7404 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 7405 * <li><b>errorType:</b> {String} Type of error that was caught</li> 7406 * <li><b>errorMessage:</b> {String} Message associated with error</li> 7407 * </ul></li> 7408 * </ul></li> 7409 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 7410 **/ 7411 init: function (options) { 7412 this._super(options); 7413 }, 7414 7415 /** 7416 * @private 7417 * Gets the REST class for the current object - this is the Dialog class. 7418 * @returns {Object} The Dialog class. 7419 */ 7420 getRestClass: function () { 7421 return Dialog; 7422 }, 7423 7424 /** 7425 * @private 7426 * The constant for agent device. 7427 */ 7428 _agentDeviceType: "AGENT_DEVICE", 7429 7430 /** 7431 * @private 7432 * Gets the REST type for the current object - this is a "Dialog". 7433 * @returns {String} The Dialog string. 7434 */ 7435 getRestType: function () { 7436 return "Dialog"; 7437 }, 7438 7439 /** 7440 * @private 7441 * Override default to indicate that this object doesn't support making 7442 * requests. 7443 */ 7444 supportsRequests: false, 7445 7446 /** 7447 * @private 7448 * Override default to indicate that this object doesn't support subscriptions. 7449 */ 7450 supportsSubscriptions: false, 7451 7452 /** 7453 * The requestId reaper timeout in ms 7454 */ 7455 REQUESTID_REAPER_TIMEOUT: 5000, 7456 7457 /** 7458 * Getter for the from address. 7459 * @returns {String} The from address. 7460 */ 7461 getFromAddress: function () { 7462 this.isLoaded(); 7463 return this.getData().fromAddress; 7464 }, 7465 7466 /** 7467 * Getter for the to address. 7468 * @returns {String} The to address. 7469 */ 7470 getToAddress: function () { 7471 this.isLoaded(); 7472 return this.getData().toAddress; 7473 }, 7474 7475 /** 7476 * Getter for the media type. 7477 * @returns {String} The media type. 7478 */ 7479 getMediaType: function () { 7480 this.isLoaded(); 7481 return this.getData().mediaType; 7482 }, 7483 7484 /** 7485 * @private 7486 * Getter for the uri. 7487 * @returns {String} The uri. 7488 */ 7489 getDialogUri: function () { 7490 this.isLoaded(); 7491 return this.getData().uri; 7492 }, 7493 7494 /** 7495 * Getter for the callType. 7496 * @deprecated Use getMediaProperties().callType instead. 7497 * @returns {String} The callType. 7498 */ 7499 getCallType: function () { 7500 this.isLoaded(); 7501 return this.getData().mediaProperties.callType; 7502 }, 7503 7504 /** 7505 * Getter for the DNIS. This is usually the actual number dialed. 7506 * @deprecated Use getMediaProperties().DNIS instead. 7507 * @returns {String} The callType. 7508 */ 7509 getDNIS: function () { 7510 this.isLoaded(); 7511 return this.getData().mediaProperties.DNIS; 7512 }, 7513 7514 /** 7515 * Getter for the Dialog state. 7516 * @returns {String} The Dialog state. 7517 */ 7518 getState: function () { 7519 this.isLoaded(); 7520 return this.getData().state; 7521 }, 7522 7523 /** 7524 * Retrieves a list of participants within the Dialog object. 7525 * @returns {Object} Array list of participants. 7526 */ 7527 getParticipants: function () { 7528 this.isLoaded(); 7529 var participants = this.getData().participants.Participant; 7530 //Due to the nature of the XML->JSO converter library, a single 7531 //element in the XML array will be considered to an object instead of 7532 //a real array. This will handle those cases to ensure that an array is 7533 //always returned. 7534 7535 return Utilities.getArray(participants); 7536 }, 7537 7538 /** 7539 * gets the participant timer counters 7540 * 7541 * @param {String} participantExt Extension of participant. 7542 * @returns {Object} which contains state, startTime, and stateChangeTime fields 7543 */ 7544 getParticipantTimerCounters : function (participantExt) { 7545 var part, participantTimerCounters = {}, idx, participants; 7546 7547 participants = this.getParticipants(); 7548 7549 7550 //Loop through all the participants and find the right participant (based on participantExt) 7551 for(idx=0;idx<participants.length;idx=idx+1) 7552 { 7553 part = participants[idx]; 7554 7555 if (part.mediaAddress === participantExt) 7556 { 7557 participantTimerCounters.startTime= part.startTime; 7558 participantTimerCounters.stateChangeTime= part.stateChangeTime; 7559 participantTimerCounters.state= part.state; 7560 break; 7561 } 7562 } 7563 7564 return participantTimerCounters; 7565 }, 7566 7567 /** 7568 * Determines the droppable participants. A droppable participant is a participant that is an agent extension. 7569 * (It is not a CTI Route Point, IVR Port, or the caller) 7570 * 7571 * @param {String} filterExtension used to remove a single extension from the list 7572 * @returns {Array} Participants which is an array of all participants which can be dropped. 7573 */ 7574 getDroppableParticipants: function (filterExtension) { 7575 this.isLoaded(); 7576 var droppableParticipants = [], participants, index, idx, filterExtensionToRemove = "", callStateOk, part; 7577 7578 participants = this.getParticipants(); 7579 7580 if (filterExtension) 7581 { 7582 filterExtensionToRemove = filterExtension; 7583 } 7584 7585 //Loop through all the participants to remove non-agents & remove filterExtension 7586 //We could have removed filterExtension using splice, but we have to iterate through 7587 //the list anyway. 7588 for(idx=0;idx<participants.length;idx=idx+1) 7589 { 7590 part = participants[idx]; 7591 7592 //Skip the filterExtension 7593 if (part.mediaAddress !== filterExtensionToRemove) 7594 { 7595 callStateOk = this._isParticipantStateDroppable(part); 7596 7597 //Remove non-agents & make sure callstate 7598 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType) 7599 { 7600 droppableParticipants.push(part); 7601 } 7602 } 7603 } 7604 7605 return Utilities.getArray(droppableParticipants); 7606 }, 7607 7608 _isParticipantStateDroppable : function (part) 7609 { 7610 var isParticipantStateDroppable = false; 7611 if (part.state === Dialog.ParticipantStates.ACTIVE || part.state === Dialog.ParticipantStates.ACCEPTED || part.state === Dialog.ParticipantStates.HELD) 7612 { 7613 isParticipantStateDroppable = true; 7614 } 7615 7616 return isParticipantStateDroppable; 7617 }, 7618 7619 /** 7620 * Is the participant droppable 7621 * 7622 * @param {String} participantExt Extension of participant. 7623 * @returns {Boolean} True is droppable. 7624 */ 7625 isParticipantDroppable : function (participantExt) { 7626 var droppableParticipants = null, isDroppable = false, idx, part, callStateOk; 7627 7628 droppableParticipants = this.getDroppableParticipants(); 7629 7630 if (droppableParticipants) 7631 { 7632 for(idx=0;idx<droppableParticipants.length;idx=idx+1) 7633 { 7634 part = droppableParticipants[idx]; 7635 7636 if (part.mediaAddress === participantExt) 7637 { 7638 callStateOk = this._isParticipantStateDroppable(part); 7639 7640 //Remove non-agents & make sure callstate 7641 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType) 7642 { 7643 isDroppable = true; 7644 break; 7645 } 7646 } 7647 } 7648 } 7649 7650 return isDroppable; 7651 }, 7652 7653 /** 7654 * Retrieves a list of media properties from the dialog object. 7655 * @returns {Object} Map of call variables; names mapped to values. 7656 * Variables may include the following:<ul> 7657 * <li>dialedNumber: The number dialed. 7658 * <li>callType: The type of call. Call types include:<ul> 7659 * <li>ACD_IN 7660 * <li>PREROUTE_ACD_IN 7661 * <li>PREROUTE_DIRECT_AGENT 7662 * <li>TRANSFER 7663 * <li>OTHER_IN 7664 * <li>OUT 7665 * <li>AGENT_INSIDE 7666 * <li>CONSULT 7667 * <li>CONFERENCE 7668 * <li>SUPERVISOR_MONITOR 7669 * <li>OUTBOUND 7670 * <li>OUTBOUND_PREVIEW</ul> 7671 * <li>DNIS: The DNIS provided. For routed calls, this is the route point. 7672 * <li>wrapUpReason: A description of the call. 7673 * <li>Call Variables, by name. The name indicates whether it is a call variable or ECC variable. 7674 * Call variable names start with callVariable#, where # is 1-10. ECC variable names (both scalar and array) are prepended with "user". 7675 * ECC variable arrays include an index enclosed within square brackets located at the end of the ECC array name. 7676 * <li>The following call variables provide additional details about an Outbound Option call:<ul> 7677 * <li>BACampaign 7678 * <li>BAAccountNumber 7679 * <li>BAResponse 7680 * <li>BAStatus<ul> 7681 * <li>PREDICTIVE_OUTBOUND: A predictive outbound call. 7682 * <li>PROGRESSIVE_OUTBOUND: A progressive outbound call. 7683 * <li>PREVIEW_OUTBOUND_RESERVATION: Agent is reserved for a preview outbound call. 7684 * <li>PREVIEW_OUTBOUND: Agent is on a preview outbound call.</ul> 7685 * <li>BADialedListID 7686 * <li>BATimeZone 7687 * <li>BABuddyName</ul></ul> 7688 * 7689 */ 7690 getMediaProperties: function () { 7691 var mpData, currentMediaPropertiesMap, thisMediaPropertiesJQuery; 7692 7693 this.isLoaded(); 7694 7695 // We have to convert to jQuery object to do a proper compare 7696 thisMediaPropertiesJQuery = jQuery(this.getData().mediaProperties); 7697 7698 if ((this._lastMediaPropertiesJQuery !== undefined) 7699 && (this._lastMediaPropertiesMap !== undefined) 7700 && (this._lastMediaPropertiesJQuery.is(thisMediaPropertiesJQuery))) { 7701 7702 return this._lastMediaPropertiesMap; 7703 } 7704 7705 currentMediaPropertiesMap = {}; 7706 7707 mpData = this.getData().mediaProperties; 7708 7709 if (mpData) { 7710 if (mpData.callvariables && mpData.callvariables.CallVariable) { 7711 jQuery.each(mpData.callvariables.CallVariable, function (i, callVariable) { 7712 currentMediaPropertiesMap[callVariable.name] = callVariable.value; 7713 }); 7714 } 7715 7716 jQuery.each(mpData, function (key, value) { 7717 if (key !== 'callvariables') { 7718 currentMediaPropertiesMap[key] = value; 7719 } 7720 }); 7721 } 7722 7723 this._lastMediaPropertiesMap = currentMediaPropertiesMap; 7724 this._lastMediaPropertiesJQuery = thisMediaPropertiesJQuery; 7725 7726 return this._lastMediaPropertiesMap; 7727 }, 7728 7729 /** 7730 * Retrieves information about the currently scheduled callback, if any. 7731 * @returns {Object} If no callback has been set, will return undefined. If 7732 * a callback has been set, it will return a map with one or more of the 7733 * following entries, depending on what values have been set. 7734 * callbackTime - the callback time, if it has been set. 7735 * callbackNumber - the callback number, if it has been set. 7736 */ 7737 getCallbackInfo: function() { 7738 this.isLoaded(); 7739 return this.getData().scheduledCallbackInfo; 7740 }, 7741 7742 /** 7743 * @private 7744 * Invoke a request to the server given a content body and handlers. 7745 * 7746 * @param {Object} contentBody 7747 * A JS object containing the body of the action request. 7748 * @param {finesse.interfaces.RequestHandlers} handlers 7749 * An object containing the handlers for the request 7750 */ 7751 _makeRequest: function (contentBody, handlers) { 7752 // Protect against null dereferencing of options allowing its 7753 // (nonexistent) keys to be read as undefined 7754 handlers = handlers || {}; 7755 7756 this.restRequest(this.getRestUrl(), { 7757 method: 'PUT', 7758 success: handlers.success, 7759 error: handlers.error, 7760 content: contentBody 7761 }); 7762 }, 7763 7764 /** 7765 * Invoke a consult call out to a destination. 7766 * 7767 * @param {String} mediaAddress 7768 * The media address of the user performing the consult call. 7769 * @param {String} toAddress 7770 * The destination address of the consult call. 7771 * @param {finesse.interfaces.RequestHandlers} handlers 7772 * An object containing the handlers for the request 7773 */ 7774 makeConsultCall: function (mediaAddress, toAddress, handlers) { 7775 this.isLoaded(); 7776 var contentBody = {}; 7777 contentBody[this.getRestType()] = { 7778 "targetMediaAddress": mediaAddress, 7779 "toAddress": toAddress, 7780 "requestedAction": Dialog.Actions.CONSULT_CALL 7781 }; 7782 this._makeRequest(contentBody, handlers); 7783 return this; // Allow cascading 7784 }, 7785 7786 /** 7787 * Invoke a single step transfer request. 7788 * 7789 * @param {String} mediaAddress 7790 * The media address of the user performing the single step transfer. 7791 * @param {String} toAddress 7792 * The destination address of the single step transfer. 7793 * @param {finesse.interfaces.RequestHandlers} handlers 7794 * An object containing the handlers for the request 7795 */ 7796 initiateDirectTransfer: function (mediaAddress, toAddress, handlers) { 7797 this.isLoaded(); 7798 var contentBody = {}; 7799 contentBody[this.getRestType()] = { 7800 "targetMediaAddress": mediaAddress, 7801 "toAddress": toAddress, 7802 "requestedAction": Dialog.Actions.TRANSFER_SST 7803 }; 7804 this._makeRequest(contentBody, handlers); 7805 return this; // Allow cascading 7806 }, 7807 7808 /** 7809 * Update this dialog's wrap-up reason. 7810 * 7811 * @param {String} wrapUpReason 7812 * The new wrap-up reason for this dialog 7813 * @param {finesse.interfaces.RequestHandlers} handlers 7814 * An object containing the handlers for the request 7815 */ 7816 updateWrapUpReason: function (wrapUpReason, options) 7817 { 7818 this.isLoaded(); 7819 var mediaProperties = 7820 { 7821 "wrapUpReason": wrapUpReason 7822 }; 7823 7824 options = options || {}; 7825 options.content = {}; 7826 options.content[this.getRestType()] = 7827 { 7828 "mediaProperties": mediaProperties, 7829 "requestedAction": Dialog.Actions.UPDATE_CALL_DATA 7830 }; 7831 options.method = "PUT"; 7832 this.restRequest(this.getRestUrl(), options); 7833 7834 return this; 7835 }, 7836 7837 /** 7838 * Invoke a request to server based on the action given. 7839 * @param {String} mediaAddress 7840 * The media address of the user performing the action. 7841 * @param {finesse.restservices.Dialog.Actions} action 7842 * The action string indicating the action to invoke on dialog. 7843 * @param {finesse.interfaces.RequestHandlers} handlers 7844 * An object containing the handlers for the request 7845 */ 7846 requestAction: function (mediaAddress, action, handlers) { 7847 this.isLoaded(); 7848 var contentBody = {}; 7849 contentBody[this.getRestType()] = { 7850 "targetMediaAddress": mediaAddress, 7851 "requestedAction": action 7852 }; 7853 this._makeRequest(contentBody, handlers); 7854 return this; // Allow cascading 7855 }, 7856 7857 /** 7858 * Wrapper around "requestAction" to request PARTICIPANT_DROP action. 7859 * 7860 * @param targetMediaAddress is the address to drop 7861 * @param {finesse.interfaces.RequestHandlers} handlers 7862 * An object containing the handlers for the request 7863 */ 7864 dropParticipant: function (targetMediaAddress, handlers) { 7865 this.requestAction(targetMediaAddress, Dialog.Actions.PARTICIPANT_DROP, handlers); 7866 }, 7867 7868 /** 7869 * Invoke a request to server to send DTMF digit tones. 7870 * @param {String} mediaAddress 7871 * @param {String} action 7872 * The action string indicating the action to invoke on dialog. 7873 * @param {finesse.interfaces.RequestHandlers} handlers 7874 * An object containing the handlers for the request 7875 */ 7876 sendDTMFRequest: function (mediaAddress, handlers, digit) { 7877 this.isLoaded(); 7878 var contentBody = {}; 7879 contentBody[this.getRestType()] = { 7880 "targetMediaAddress": mediaAddress, 7881 "requestedAction": "SEND_DTMF", 7882 "actionParams": { 7883 "ActionParam": { 7884 "name": "dtmfString", 7885 "value": digit 7886 } 7887 } 7888 }; 7889 this._makeRequest(contentBody, handlers); 7890 return this; // Allow cascading 7891 }, 7892 7893 /** 7894 * Invoke a request to server to set the time for a callback. 7895 * @param {String} mediaAddress 7896 * @param {String} callbackTime 7897 * The requested time for the callback, in YYYY-MM-DDTHH:MM format 7898 * (ex: 2013-12-24T23:59) 7899 * @param {finesse.interfaces.RequestHandlers} handlers 7900 * An object containing the handlers for the request 7901 */ 7902 updateCallbackTime: function (mediaAddress, callbackTime, handlers) { 7903 this.isLoaded(); 7904 var contentBody = {}; 7905 contentBody[this.getRestType()] = { 7906 "targetMediaAddress": mediaAddress, 7907 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK, 7908 "actionParams": { 7909 "ActionParam": { 7910 "name": "callbackTime", 7911 "value": callbackTime 7912 } 7913 } 7914 }; 7915 this._makeRequest(contentBody, handlers); 7916 return this; // Allow cascading 7917 }, 7918 7919 /** 7920 * Invoke a request to server to set the number for a callback. 7921 * @param {String} mediaAddress 7922 * @param {String} callbackNumber 7923 * The requested number to call for the callback 7924 * @param {finesse.interfaces.RequestHandlers} handlers 7925 * An object containing the handlers for the request 7926 */ 7927 updateCallbackNumber: function (mediaAddress, callbackNumber, handlers) { 7928 this.isLoaded(); 7929 var contentBody = {}; 7930 contentBody[this.getRestType()] = { 7931 "targetMediaAddress": mediaAddress, 7932 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK, 7933 "actionParams": { 7934 "ActionParam": { 7935 "name": "callbackNumber", 7936 "value": callbackNumber 7937 } 7938 } 7939 }; 7940 this._makeRequest(contentBody, handlers); 7941 return this; // Allow cascading 7942 }, 7943 7944 /** 7945 * Invoke a request to server to cancel a callback. 7946 * @param {String} mediaAddress 7947 * @param {finesse.interfaces.RequestHandlers} handlers 7948 * An object containing the handlers for the request 7949 */ 7950 cancelCallback: function (mediaAddress, handlers) { 7951 this.isLoaded(); 7952 var contentBody = {}; 7953 contentBody[this.getRestType()] = { 7954 "targetMediaAddress": mediaAddress, 7955 "requestedAction": Dialog.Actions.CANCEL_SCHEDULED_CALLBACK 7956 }; 7957 this._makeRequest(contentBody, handlers); 7958 return this; // Allow cascading 7959 }, 7960 7961 /** 7962 * Invoke a request to server to reclassify the call type. 7963 * @param {String} mediaAddress 7964 * The media address of the user performing the consult call. 7965 * @param {String} classification 7966 * The classification to assign to the call. Valid values are "VOICE", "FAX", 7967 * "ANS_MACHINE", "INVALID", "BUSY" (CCX only), and "DO_NOT_CALL". 7968 * @param {finesse.interfaces.RequestHandlers} handlers 7969 * An object containing the handlers for the request 7970 */ 7971 reclassifyCall: function (mediaAddress, classification, handlers) { 7972 this.isLoaded(); 7973 var contentBody = {}; 7974 contentBody[this.getRestType()] = { 7975 "targetMediaAddress": mediaAddress, 7976 "requestedAction": Dialog.Actions.RECLASSIFY, 7977 "actionParams": { 7978 "ActionParam": { 7979 "name": "outboundClassification", 7980 "value": classification 7981 } 7982 } 7983 }; 7984 this._makeRequest(contentBody, handlers); 7985 return this; // Allow cascading 7986 }, 7987 7988 /** 7989 * Utility method to create a closure containing the requestId and the Dialogs object so 7990 * that the _pendingCallbacks map can be manipulated when the timer task is executed. 7991 * @param {String} requestId The requestId of the event 7992 * @return {Function} The function to be executed by setTimeout 7993 */ 7994 _createRequestIdReaper: function (requestId) { 7995 var that = this; 7996 return function () { 7997 that._logger.log("Dialog: clearing the requestId-to-callbacks mapping for requestId=" + requestId); 7998 delete that._pendingCallbacks[requestId]; 7999 }; 8000 }, 8001 8002 /** 8003 * Overriding implementation of the one in RestBase.js 8004 * This determines the strategy that Dialogs will take after processing an event that contains a requestId. 8005 * @param {String} requestId The requestId of the event 8006 */ 8007 _postProcessUpdateStrategy: function (requestId) { 8008 this._logger.log("Dialog: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId); 8009 var callbacksObj = this._pendingCallbacks[requestId]; 8010 if (callbacksObj && !callbacksObj.used) { 8011 this._logger.log("Dialog: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId); 8012 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT); 8013 callbacksObj.used = true; 8014 } 8015 } 8016 }); 8017 8018 Dialog.Actions = /** @lends finesse.restservices.Dialog.Actions.prototype */ { 8019 /** 8020 * Drops the Participant from the Dialog. 8021 */ 8022 DROP: "DROP", 8023 /** 8024 * Answers a Dialog. 8025 */ 8026 ANSWER: "ANSWER", 8027 /** 8028 * Holds the Dialog. 8029 */ 8030 HOLD: "HOLD", 8031 /** 8032 * Barges into a Call Dialog. 8033 */ 8034 BARGE_CALL: "BARGE_CALL", 8035 /** 8036 * Allow as Supervisor to Drop a Participant from the Dialog. 8037 */ 8038 PARTICIPANT_DROP: "PARTICIPANT_DROP", 8039 /** 8040 * Makes a new Call Dialog. 8041 */ 8042 MAKE_CALL: "MAKE_CALL", 8043 /** 8044 * Retrieves a Dialog that is on Hold. 8045 */ 8046 RETRIEVE: "RETRIEVE", 8047 /** 8048 * Sets the time or number for a callback. Can be 8049 * either a new callback, or updating an existing one. 8050 */ 8051 UPDATE_SCHEDULED_CALLBACK: "UPDATE_SCHEDULED_CALLBACK", 8052 /** 8053 * Cancels a callback. 8054 */ 8055 CANCEL_SCHEDULED_CALLBACK: "CANCEL_SCHEDULED_CALLBACK", 8056 /** 8057 * Initiates a Consult Call. 8058 */ 8059 CONSULT_CALL: "CONSULT_CALL", 8060 /** 8061 * Initiates a Transfer of a Dialog. 8062 */ 8063 TRANSFER: "TRANSFER", 8064 /** 8065 * Initiates a Single-Step Transfer of a Dialog. 8066 */ 8067 TRANSFER_SST: "TRANSFER_SST", 8068 /** 8069 * Initiates a Conference of a Dialog. 8070 */ 8071 CONFERENCE: "CONFERENCE", 8072 /** 8073 * Changes classification for a call 8074 */ 8075 RECLASSIFY: "RECLASSIFY", 8076 /** 8077 * Updates data on a Call Dialog. 8078 */ 8079 UPDATE_CALL_DATA: "UPDATE_CALL_DATA", 8080 /** 8081 * Initiates a Recording on a Call Dialog. 8082 */ 8083 START_RECORDING : "START_RECORDING", 8084 /** 8085 * Sends DTMF (dialed digits) to a Call Dialog. 8086 */ 8087 DTMF : "SEND_DTMF", 8088 /** 8089 * Accepts a Dialog that is being Previewed. 8090 */ 8091 ACCEPT: "ACCEPT", 8092 /** 8093 * Rejects a Dialog. 8094 */ 8095 REJECT: "REJECT", 8096 /** 8097 * Closes a Dialog. 8098 */ 8099 CLOSE : "CLOSE", 8100 /** 8101 * @class Set of action constants for a Dialog. These should be used for 8102 * {@link finesse.restservices.Dialog#requestAction}. 8103 * @constructs 8104 */ 8105 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 8106 }; 8107 8108 Dialog.States = /** @lends finesse.restservices.Dialog.States.prototype */ { 8109 /** 8110 * Indicates that the call is ringing at a device. 8111 */ 8112 ALERTING: "ALERTING", 8113 /** 8114 * Indicates that the phone is off the hook at a device. 8115 */ 8116 INITIATING: "INITIATING", 8117 /** 8118 * Indicates that the dialog has a least one active participant. 8119 */ 8120 ACTIVE: "ACTIVE", 8121 /** 8122 * Indicates that the dialog has no active participants. 8123 */ 8124 DROPPED: "DROPPED", 8125 /** 8126 * Indicates that the phone is dialing at the device. 8127 */ 8128 INITIATED: "INITIATED", 8129 /** 8130 * Indicates that the dialog has failed. 8131 * @see Dialog.ReasonStates 8132 */ 8133 FAILED: "FAILED", 8134 /** 8135 * Indicates that the user has accepted an OUTBOUND_PREVIEW dialog. 8136 */ 8137 ACCEPTED: "ACCEPTED", 8138 /** 8139 * @class Possible Dialog State constants. 8140 * The State flow of a typical in-bound Dialog is as follows: INITIATING, INITIATED, ALERTING, ACTIVE, DROPPED. 8141 * @constructs 8142 */ 8143 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 8144 }; 8145 8146 Dialog.ParticipantStates = /** @lends finesse.restservices.Dialog.ParticipantStates.prototype */ { 8147 /** 8148 * Indicates that an incoming call is ringing on the device. 8149 */ 8150 ALERTING: "ALERTING", 8151 /** 8152 * Indicates that an outgoing call, not yet active, exists on the device. 8153 */ 8154 INITIATING: "INITIATING", 8155 /** 8156 * Indicates that the participant is active on the call. 8157 */ 8158 ACTIVE: "ACTIVE", 8159 /** 8160 * Indicates that the participant has dropped from the call. 8161 */ 8162 DROPPED: "DROPPED", 8163 /** 8164 * Indicates that the participant has held their connection to the call. 8165 */ 8166 HELD: "HELD", 8167 /** 8168 * Indicates that the phone is dialing at a device. 8169 */ 8170 INITIATED: "INITIATED", 8171 /** 8172 * Indicates that the call failed. 8173 * @see Dialog.ReasonStates 8174 */ 8175 FAILED: "FAILED", 8176 /** 8177 * Indicates that the participant is not in active state on the call, but is wrapping up after the participant has dropped from the call. 8178 */ 8179 WRAP_UP: "WRAP_UP", 8180 /** 8181 * Indicates that the participant has accepted the dialog. This state is applicable to OUTBOUND_PREVIEW dialogs. 8182 */ 8183 ACCEPTED: "ACCEPTED", 8184 /** 8185 * @class Possible Dialog Participant State constants. 8186 * @constructs 8187 */ 8188 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 8189 }; 8190 8191 Dialog.ReasonStates = /** @lends finesse.restservices.Dialog.ReasonStates.prototype */ { 8192 /** 8193 * Dialog was Busy. This will typically be for a Failed Dialog. 8194 */ 8195 BUSY: "BUSY", 8196 /** 8197 * Dialog reached a Bad Destination. This will typically be for a Failed Dialog. 8198 */ 8199 BAD_DESTINATION: "BAD_DESTINATION", 8200 /** 8201 * All Other Reasons. This will typically be for a Failed Dialog. 8202 */ 8203 OTHER: "OTHER", 8204 /** 8205 * The Device Resource for the Dialog was not available. 8206 */ 8207 DEVICE_RESOURCE_NOT_AVAILABLE : "DEVICE_RESOURCE_NOT_AVAILABLE", 8208 /** 8209 * @class Possible dialog state reasons code constants. 8210 * @constructs 8211 */ 8212 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 8213 }; 8214 8215 window.finesse = window.finesse || {}; 8216 window.finesse.restservices = window.finesse.restservices || {}; 8217 window.finesse.restservices.Dialog = Dialog; 8218 8219 8220 return Dialog; 8221 }); 8222 8223 /** 8224 * JavaScript representation of the Finesse Dialogs collection 8225 * object which contains a list of Dialog objects. 8226 * 8227 * @requires finesse.clientservices.ClientServices 8228 * @requires Class 8229 * @requires finesse.FinesseBase 8230 * @requires finesse.restservices.RestBase 8231 * @requires finesse.restservices.Dialog 8232 */ 8233 /** @private */ 8234 define('restservices/Dialogs',[ 8235 'restservices/RestCollectionBase', 8236 'restservices/Dialog' 8237 ], 8238 function (RestCollectionBase, Dialog) { 8239 var Dialogs = RestCollectionBase.extend(/** @lends finesse.restservices.Dialogs.prototype */{ 8240 8241 /** 8242 * @class 8243 * JavaScript representation of a Dialogs collection object. Also exposes 8244 * methods to operate on the object against the server. 8245 * @augments finesse.restservices.RestCollectionBase 8246 * @constructs 8247 * @see finesse.restservices.Dialog 8248 * @example 8249 * _dialogs = _user.getDialogs( { 8250 * onCollectionAdd : _handleDialogAdd, 8251 * onCollectionDelete : _handleDialogDelete, 8252 * onLoad : _handleDialogsLoaded 8253 * }); 8254 * 8255 * _dialogCollection = _dialogs.getCollection(); 8256 * for (var dialogId in _dialogCollection) { 8257 * if (_dialogCollection.hasOwnProperty(dialogId)) { 8258 * _dialog = _dialogCollection[dialogId]; 8259 * etc... 8260 * } 8261 * } 8262 */ 8263 _fakeConstuctor: function () { 8264 /* This is here to hide the real init constructor from the public docs */ 8265 }, 8266 8267 /** 8268 * @private 8269 * @param {Object} options 8270 * An object with the following properties:<ul> 8271 * <li><b>id:</b> The id of the object being constructed</li> 8272 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 8273 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 8274 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 8275 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 8276 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 8277 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8278 * <li><b>content:</b> {String} Raw string of response</li> 8279 * <li><b>object:</b> {Object} Parsed object of response</li> 8280 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 8281 * <li><b>errorType:</b> {String} Type of error that was caught</li> 8282 * <li><b>errorMessage:</b> {String} Message associated with error</li> 8283 * </ul></li> 8284 * </ul></li> 8285 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 8286 **/ 8287 init: function (options) { 8288 this._super(options); 8289 }, 8290 8291 /** 8292 * @private 8293 * Gets the REST class for the current object - this is the Dialogs class. 8294 */ 8295 getRestClass: function () { 8296 return Dialogs; 8297 }, 8298 8299 /** 8300 * @private 8301 * Gets the REST class for the objects that make up the collection. - this 8302 * is the Dialog class. 8303 */ 8304 getRestItemClass: function () { 8305 return Dialog; 8306 }, 8307 8308 /** 8309 * @private 8310 * Gets the REST type for the current object - this is a "Dialogs". 8311 */ 8312 getRestType: function () { 8313 return "Dialogs"; 8314 }, 8315 8316 /** 8317 * @private 8318 * Gets the REST type for the objects that make up the collection - this is "Dialogs". 8319 */ 8320 getRestItemType: function () { 8321 return "Dialog"; 8322 }, 8323 8324 /** 8325 * @private 8326 * Override default to indicates that the collection doesn't support making 8327 * requests. 8328 */ 8329 supportsRequests: true, 8330 8331 /** 8332 * @private 8333 * Override default to indicates that the collection subscribes to its objects. 8334 */ 8335 supportsRestItemSubscriptions: true, 8336 8337 /** 8338 * The requestId reaper timeout in ms 8339 */ 8340 REQUESTID_REAPER_TIMEOUT: 5000, 8341 8342 /** 8343 * @private 8344 * Create a new Dialog in this collection 8345 * 8346 * @param {String} toAddress 8347 * The to address of the new Dialog 8348 * @param {String} fromAddress 8349 * The from address of the new Dialog 8350 * @param {finesse.interfaces.RequestHandlers} handlers 8351 * An object containing the (optional) handlers for the request. 8352 * @return {finesse.restservices.Dialogs} 8353 * This Dialogs object, to allow cascading. 8354 */ 8355 createNewCallDialog: function (toAddress, fromAddress, handlers) 8356 { 8357 var contentBody = {}; 8358 contentBody[this.getRestItemType()] = { 8359 "requestedAction": "MAKE_CALL", 8360 "toAddress": toAddress, 8361 "fromAddress": fromAddress 8362 }; 8363 8364 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 8365 handlers = handlers || {}; 8366 8367 this.restRequest(this.getRestUrl(), { 8368 method: 'POST', 8369 success: handlers.success, 8370 error: handlers.error, 8371 content: contentBody 8372 }); 8373 return this; // Allow cascading 8374 }, 8375 8376 /** 8377 * @private 8378 * Create a new Dialog in this collection as a result of a requested action 8379 * 8380 * @param {String} toAddress 8381 * The to address of the new Dialog 8382 * @param {String} fromAddress 8383 * The from address of the new Dialog 8384 * @param {finesse.restservices.Dialog.Actions} actionType 8385 * The associated action to request for creating this new dialog 8386 * @param {finesse.interfaces.RequestHandlers} handlers 8387 * An object containing the (optional) handlers for the request. 8388 * @return {finesse.restservices.Dialogs} 8389 * This Dialogs object, to allow cascading. 8390 */ 8391 createNewSuperviseCallDialog: function (toAddress, fromAddress, actionType, handlers) 8392 { 8393 var contentBody = {}; 8394 this._isLoaded = true; 8395 8396 contentBody[this.getRestItemType()] = { 8397 "requestedAction": actionType, 8398 "toAddress": toAddress, 8399 "fromAddress": fromAddress 8400 }; 8401 8402 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 8403 handlers = handlers || {}; 8404 8405 this.restRequest(this.getRestUrl(), { 8406 method: 'POST', 8407 success: handlers.success, 8408 error: handlers.error, 8409 content: contentBody 8410 }); 8411 return this; // Allow cascading 8412 }, 8413 8414 /** 8415 * @private 8416 * Create a new Dialog in this collection as a result of a requested action 8417 * @param {String} fromAddress 8418 * The from address of the new Dialog 8419 * @param {String} toAddress 8420 * The to address of the new Dialog 8421 * @param {finesse.restservices.Dialog.Actions} actionType 8422 * The associated action to request for creating this new dialog 8423 * @param {String} dialogUri 8424 * The associated uri of SUPERVISOR_MONITOR call 8425 * @param {finesse.interfaces.RequestHandlers} handlers 8426 * An object containing the (optional) handlers for the request. 8427 * @return {finesse.restservices.Dialogs} 8428 * This Dialogs object, to allow cascading. 8429 */ 8430 createNewBargeCall: function (fromAddress, toAddress, actionType, dialogURI, handlers) { 8431 this.isLoaded(); 8432 8433 var contentBody = {}; 8434 contentBody[this.getRestItemType()] = { 8435 "fromAddress": fromAddress, 8436 "toAddress": toAddress, 8437 "requestedAction": actionType, 8438 "associatedDialogUri": dialogURI 8439 8440 }; 8441 // (nonexistent) keys to be read as undefined 8442 handlers = handlers || {}; 8443 this.restRequest(this.getRestUrl(), { 8444 method: 'POST', 8445 success: handlers.success, 8446 error: handlers.error, 8447 content: contentBody 8448 }); 8449 return this; // Allow cascading 8450 }, 8451 8452 /** 8453 * Utility method to get the number of dialogs in this collection. 8454 * 'excludeSilentMonitor' flag is provided as an option to exclude calls with type 8455 * 'SUPERVISOR_MONITOR' from the count. 8456 * @param {Boolean} excludeSilentMonitor If true, calls with type of 'SUPERVISOR_MONITOR' will be excluded from the count. 8457 * @return {Number} The number of dialogs in this collection. 8458 */ 8459 getDialogCount: function (excludeSilentMonitor) { 8460 this.isLoaded(); 8461 8462 var dialogId, count = 0; 8463 if (excludeSilentMonitor) { 8464 for (dialogId in this._collection) { 8465 if (this._collection.hasOwnProperty(dialogId)) { 8466 if (this._collection[dialogId].getCallType() !== 'SUPERVISOR_MONITOR') { 8467 count += 1; 8468 } 8469 } 8470 } 8471 8472 return count; 8473 } else { 8474 return this.length; 8475 } 8476 }, 8477 8478 /** 8479 * Utility method to create a closure containing the requestId and the Dialogs object so 8480 * that the _pendingCallbacks map can be manipulated when the timer task is executed. 8481 * @param {String} requestId The requestId of the event 8482 * @return {Function} The function to be executed by setTimeout 8483 */ 8484 _createRequestIdReaper: function (requestId) { 8485 var that = this; 8486 return function () { 8487 that._logger.log("Dialogs: clearing the requestId-to-callbacks mapping for requestId=" + requestId); 8488 delete that._pendingCallbacks[requestId]; 8489 }; 8490 }, 8491 8492 /** 8493 * Overriding implementation of the one in RestBase.js 8494 * This determines the strategy that Dialogs will take after processing an event that contains a requestId. 8495 * @param {String} requestId The requestId of the event 8496 */ 8497 _postProcessUpdateStrategy: function (requestId) { 8498 this._logger.log("Dialogs: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId); 8499 var callbacksObj = this._pendingCallbacks[requestId]; 8500 if (callbacksObj && !callbacksObj.used) { 8501 this._logger.log("Dialogs: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId); 8502 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT); 8503 callbacksObj.used = true; 8504 } 8505 } 8506 8507 }); 8508 8509 window.finesse = window.finesse || {}; 8510 window.finesse.restservices = window.finesse.restservices || {}; 8511 window.finesse.restservices.Dialogs = Dialogs; 8512 8513 return Dialogs; 8514 }); 8515 8516 /** 8517 * JavaScript representation of the Finesse ClientLog object 8518 * 8519 * @requires finesse.clientservices.ClientServices 8520 * @requires Class 8521 * @requires finesse.FinesseBase 8522 * @requires finesse.restservices.RestBase 8523 */ 8524 8525 /** The following comment is to prevent jslint errors about 8526 * using variables before they are defined. 8527 */ 8528 /** @private */ 8529 /*global finesse*/ 8530 8531 define('restservices/ClientLog',["restservices/RestBase"], function (RestBase) { 8532 8533 var ClientLog = RestBase.extend(/** @lends finesse.restservices.ClientLog.prototype */{ 8534 /** 8535 * @private 8536 * Returns whether this object supports transport logs 8537 */ 8538 doNotLog : true, 8539 8540 explicitSubscription : true, 8541 8542 /** 8543 * @class 8544 * @private 8545 * JavaScript representation of a ClientLog object. Also exposes methods to operate 8546 * on the object against the server. 8547 * 8548 * @param {Object} options 8549 * An object with the following properties:<ul> 8550 * <li><b>id:</b> The id of the object being constructed</li> 8551 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 8552 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 8553 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 8554 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 8555 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 8556 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8557 * <li><b>content:</b> {String} Raw string of response</li> 8558 * <li><b>object:</b> {Object} Parsed object of response</li> 8559 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 8560 * <li><b>errorType:</b> {String} Type of error that was caught</li> 8561 * <li><b>errorMessage:</b> {String} Message associated with error</li> 8562 * </ul></li> 8563 * </ul></li> 8564 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 8565 * @constructs 8566 * @augments finesse.restservices.RestBase 8567 **/ 8568 init: function (options) { 8569 this._super({ 8570 id: "", 8571 data: {clientLog : null}, 8572 onAdd: options.onAdd, 8573 onChange: options.onChange, 8574 onLoad: options.onLoad, 8575 onError: options.onError, 8576 parentObj: options.parentObj 8577 }); 8578 }, 8579 8580 /** 8581 * @private 8582 * Gets the REST class for the current object - this is the ClientLog object. 8583 */ 8584 getRestClass: function () { 8585 return ClientLog; 8586 }, 8587 8588 /** 8589 * @private 8590 * Gets the REST type for the current object - this is a "ClientLog". 8591 */ 8592 getRestType: function () 8593 { 8594 return "ClientLog"; 8595 }, 8596 8597 /** 8598 * @private 8599 * Gets the node path for the current object 8600 * @returns {String} The node path 8601 */ 8602 getXMPPNodePath: function () { 8603 return this.getRestUrl(); 8604 }, 8605 8606 /** 8607 * @private 8608 * Utility method to fetch this object from the server, however we 8609 * override it for ClientLog to not do anything because GET is not supported 8610 * for ClientLog object. 8611 */ 8612 _doGET: function (handlers) { 8613 return; 8614 }, 8615 8616 /** 8617 * @private 8618 * Invoke a request to the server given a content body and handlers. 8619 * 8620 * @param {Object} contentBody 8621 * A JS object containing the body of the action request. 8622 * @param {Object} handlers 8623 * An object containing the following (optional) handlers for the request:<ul> 8624 * <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following 8625 * response object as its only parameter:<ul> 8626 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8627 * <li><b>content:</b> {String} Raw string of response</li> 8628 * <li><b>object:</b> {Object} Parsed object of response</li></ul> 8629 * <li>A error callback function for an unsuccessful request to be invoked with the 8630 * error response object as its only parameter:<ul> 8631 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8632 * <li><b>content:</b> {String} Raw string of response</li> 8633 * <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li> 8634 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 8635 * <li><b>errorType:</b> {String} Type of error that was caught</li> 8636 * <li><b>errorMessage:</b> {String} Message associated with error</li> 8637 * </ul></li> 8638 * </ul> 8639 */ 8640 sendLogs: function (contentBody, handlers) { 8641 // Protect against null dereferencing of options allowing its 8642 // (nonexistent) keys to be read as undefined 8643 handlers = handlers || {}; 8644 8645 this.restRequest(this.getRestUrl(), { 8646 method: 'POST', 8647 //success: handlers.success, 8648 error: handlers.error, 8649 content: contentBody 8650 }); 8651 } 8652 }); 8653 8654 window.finesse = window.finesse || {}; 8655 window.finesse.restservices = window.finesse.restservices || {}; 8656 window.finesse.restservices.ClientLog = ClientLog; 8657 8658 return ClientLog; 8659 }); 8660 8661 /** 8662 * JavaScript representation of the Finesse Queue object 8663 * @requires finesse.clientservices.ClientServices 8664 * @requires Class 8665 * @requires finesse.FinesseBase 8666 * @requires finesse.restservices.RestBase 8667 */ 8668 8669 /** @private */ 8670 define('restservices/Queue',[ 8671 'restservices/RestBase', 8672 'utilities/Utilities' 8673 ], 8674 function (RestBase, Utilities) { 8675 var Queue = RestBase.extend(/** @lends finesse.restservices.Queue.prototype */{ 8676 8677 /** 8678 * @class 8679 * A Queue is a list of Contacts available to a User for quick dial. 8680 * 8681 * @augments finesse.restservices.RestBase 8682 * @constructs 8683 */ 8684 _fakeConstuctor: function () { 8685 /* This is here to hide the real init constructor from the public docs */ 8686 }, 8687 8688 /** 8689 * @private 8690 * JavaScript representation of a Queue object. Also exposes methods to operate 8691 * on the object against the server. 8692 * 8693 * @constructor 8694 * @param {String} id 8695 * Not required... 8696 * @param {Object} callbacks 8697 * An object containing callbacks for instantiation and runtime 8698 * @param {Function} callbacks.onLoad(this) 8699 * Callback to invoke upon successful instantiation 8700 * @param {Function} callbacks.onLoadError(rsp) 8701 * Callback to invoke on instantiation REST request error 8702 * as passed by finesse.clientservices.ClientServices.ajax() 8703 * { 8704 * status: {Number} The HTTP status code returned 8705 * content: {String} Raw string of response 8706 * object: {Object} Parsed object of response 8707 * error: {Object} Wrapped exception that was caught 8708 * error.errorType: {String} Type of error that was caught 8709 * error.errorMessage: {String} Message associated with error 8710 * } 8711 * @param {Function} callbacks.onChange(this) 8712 * Callback to invoke upon successful update 8713 * @param {Function} callbacks.onError(rsp) 8714 * Callback to invoke on update error (refresh or event) 8715 * as passed by finesse.clientservices.ClientServices.ajax() 8716 * { 8717 * status: {Number} The HTTP status code returned 8718 * content: {String} Raw string of response 8719 * object: {Object} Parsed object of response 8720 * error: {Object} Wrapped exception that was caught 8721 * error.errorType: {String} Type of error that was caught 8722 * error.errorMessage: {String} Message associated with error 8723 * } 8724 * 8725 */ 8726 init: function (id, callbacks, restObj) { 8727 this._super(id, callbacks, restObj); 8728 }, 8729 8730 /** 8731 * @private 8732 * Gets the REST class for the current object - this is the Queue object. 8733 */ 8734 getRestClass: function () { 8735 return Queue; 8736 }, 8737 8738 /** 8739 * @private 8740 * Gets the REST type for the current object - this is a "Queue". 8741 */ 8742 getRestType: function () { 8743 return "Queue"; 8744 }, 8745 8746 /** 8747 * @private 8748 * Returns whether this object supports subscriptions 8749 */ 8750 supportsSubscriptions: function () { 8751 return true; 8752 }, 8753 8754 /** 8755 * @private 8756 * Specifies whether this object's subscriptions need to be explicitly requested 8757 */ 8758 explicitSubscription: true, 8759 8760 /** 8761 * @private 8762 * Gets the node path for the current object - this is the team Users node 8763 * @returns {String} The node path 8764 */ 8765 getXMPPNodePath: function () { 8766 return this.getRestUrl(); 8767 }, 8768 8769 /** 8770 * Getter for the queue id 8771 * @returns {String} 8772 * The id of the Queue 8773 */ 8774 getId: function () { 8775 this.isLoaded(); 8776 return this._id; 8777 }, 8778 8779 /** 8780 * Getter for the queue name 8781 * @returns {String} 8782 * The name of the Queue 8783 */ 8784 getName: function () { 8785 this.isLoaded(); 8786 return this.getData().name; 8787 }, 8788 8789 /** 8790 * Getter for the queue statistics. 8791 * Supported statistics include:<br> 8792 * - callsInQueue<br> 8793 * - startTimeOfLongestCallInQueue<br> 8794 * <br> 8795 * These statistics can be accessed via dot notation:<br> 8796 * i.e.: getStatistics().callsInQueue 8797 * @returns {Object} 8798 * The Object with different statistics as properties. 8799 */ 8800 getStatistics: function () { 8801 this.isLoaded(); 8802 return this.getData().statistics; 8803 }, 8804 8805 /** 8806 * Parses a uriString to retrieve the id portion 8807 * @param {String} uriString 8808 * @return {String} id 8809 */ 8810 _parseIdFromUriString : function (uriString) { 8811 return Utilities.getId(uriString); 8812 } 8813 8814 }); 8815 8816 window.finesse = window.finesse || {}; 8817 window.finesse.restservices = window.finesse.restservices || {}; 8818 window.finesse.restservices.Queue = Queue; 8819 8820 return Queue; 8821 }); 8822 8823 /** 8824 * JavaScript representation of the Finesse Queues collection 8825 * object which contains a list of Queue objects. 8826 * @requires finesse.clientservices.ClientServices 8827 * @requires Class 8828 * @requires finesse.FinesseBase 8829 * @requires finesse.restservices.RestBase 8830 * @requires finesse.restservices.RestCollectionBase 8831 */ 8832 8833 /** 8834 * @class 8835 * JavaScript representation of a Queues collection object. 8836 * 8837 * @constructor 8838 * @borrows finesse.restservices.RestCollectionBase as finesse.restservices.Queues 8839 */ 8840 8841 /** @private */ 8842 define('restservices/Queues',[ 8843 'restservices/RestCollectionBase', 8844 'restservices/Queue' 8845 ], 8846 function (RestCollectionBase, Queue) { 8847 var Queues = RestCollectionBase.extend(/** @lends finesse.restservices.Queues.prototype */{ 8848 8849 /** 8850 * @class 8851 * JavaScript representation of a Queues collection object. 8852 * @augments finesse.restservices.RestCollectionBase 8853 * @constructs 8854 * @see finesse.restservices.Queue 8855 * @example 8856 * _queues = _user.getQueues( { 8857 * onCollectionAdd : _handleQueueAdd, 8858 * onCollectionDelete : _handleQueueDelete, 8859 * onLoad : _handleQueuesLoaded 8860 * }); 8861 * 8862 * _queueCollection = _queues.getCollection(); 8863 * for (var queueId in _queueCollection) { 8864 * if (_queueCollection.hasOwnProperty(queueId)) { 8865 * _queue = _queueCollection[queueId]; 8866 * etc... 8867 * } 8868 * } 8869 */ 8870 _fakeConstuctor: function () { 8871 /* This is here to hide the real init constructor from the public docs */ 8872 }, 8873 8874 /** 8875 * @private 8876 * JavaScript representation of a Queues object. Also exposes 8877 * methods to operate on the object against the server. 8878 * 8879 * @param {Object} options 8880 * An object with the following properties:<ul> 8881 * <li><b>id:</b> The id of the object being constructed</li> 8882 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 8883 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 8884 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 8885 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 8886 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 8887 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8888 * <li><b>content:</b> {String} Raw string of response</li> 8889 * <li><b>object:</b> {Object} Parsed object of response</li> 8890 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 8891 * <li><b>errorType:</b> {String} Type of error that was caught</li> 8892 * <li><b>errorMessage:</b> {String} Message associated with error</li> 8893 * </ul></li> 8894 * </ul></li> 8895 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 8896 **/ 8897 init: function (options) { 8898 this._super(options); 8899 }, 8900 8901 /** 8902 * @private 8903 * Gets xmpp node path. 8904 */ 8905 getXMPPNodePath: function () { 8906 return this.getRestUrl(); 8907 }, 8908 8909 /** 8910 * @private 8911 * Gets the REST class for the current object - this is the Queues class. 8912 */ 8913 getRestClass: function () { 8914 return Queues; 8915 }, 8916 8917 /** 8918 * @private 8919 * Gets the REST class for the objects that make up the collection. - this 8920 * is the Queue class. 8921 */ 8922 getRestItemClass: function () { 8923 return Queue; 8924 }, 8925 8926 /** 8927 * @private 8928 * Gets the REST type for the current object - this is a "Queues". 8929 */ 8930 getRestType: function () { 8931 return "Queues"; 8932 }, 8933 8934 /** 8935 * @private 8936 * Gets the REST type for the objects that make up the collection - this is "Queue". 8937 */ 8938 getRestItemType: function () { 8939 return "Queue"; 8940 }, 8941 8942 explicitSubscription: true, 8943 8944 handlesItemRefresh: true 8945 }); 8946 8947 window.finesse = window.finesse || {}; 8948 window.finesse.restservices = window.finesse.restservices || {}; 8949 window.finesse.restservices.Queues = Queues; 8950 8951 return Queues; 8952 }); 8953 8954 /** 8955 * JavaScript representation of the Finesse WrapUpReason object. 8956 * 8957 * @requires finesse.clientservices.ClientServices 8958 * @requires Class 8959 * @requires finesse.FinesseBase 8960 * @requires finesse.restservices.RestBase 8961 */ 8962 8963 /** @private */ 8964 define('restservices/WrapUpReason',['restservices/RestBase'], function (RestBase) { 8965 8966 var WrapUpReason = RestBase.extend(/** @lends finesse.restservices.WrapUpReason.prototype */{ 8967 8968 /** 8969 * @class 8970 * A WrapUpReason is a code and description identifying a particular reason that a 8971 * User is in WORK (WrapUp) mode. 8972 * 8973 * @augments finesse.restservices.RestBase 8974 * @see finesse.restservices.User 8975 * @see finesse.restservices.User.States#WORK 8976 * @constructs 8977 */ 8978 _fakeConstuctor: function () { 8979 /* This is here to hide the real init constructor from the public docs */ 8980 }, 8981 8982 /** 8983 * @private 8984 * JavaScript representation of a WrapUpReason object. Also exposes 8985 * methods to operate on the object against the server. 8986 * 8987 * @param {Object} options 8988 * An object with the following properties:<ul> 8989 * <li><b>id:</b> The id of the object being constructed</li> 8990 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 8991 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 8992 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 8993 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 8994 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 8995 * <li><b>status:</b> {Number} The HTTP status code returned</li> 8996 * <li><b>content:</b> {String} Raw string of response</li> 8997 * <li><b>object:</b> {Object} Parsed object of response</li> 8998 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 8999 * <li><b>errorType:</b> {String} Type of error that was caught</li> 9000 * <li><b>errorMessage:</b> {String} Message associated with error</li> 9001 * </ul></li> 9002 * </ul></li> 9003 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 9004 **/ 9005 init: function (options) { 9006 this._super(options); 9007 }, 9008 9009 /** 9010 * @private 9011 * Gets the REST class for the current object - this is the WrapUpReason class. 9012 * @returns {Object} The WrapUpReason class. 9013 */ 9014 getRestClass: function () { 9015 return WrapUpReason; 9016 }, 9017 9018 /** 9019 * @private 9020 * Gets the REST type for the current object - this is a "WrapUpReason". 9021 * @returns {String} The WrapUpReason string. 9022 */ 9023 getRestType: function () { 9024 return "WrapUpReason"; 9025 }, 9026 9027 /** 9028 * @private 9029 * Gets the REST type for the current object - this is a "WrapUpReasons". 9030 * @returns {String} The WrapUpReasons string. 9031 */ 9032 getParentRestType: function () { 9033 return "WrapUpReasons"; 9034 }, 9035 9036 /** 9037 * @private 9038 * Override default to indicate that this object doesn't support making 9039 * requests. 9040 */ 9041 supportsRequests: false, 9042 9043 /** 9044 * @private 9045 * Override default to indicate that this object doesn't support subscriptions. 9046 */ 9047 supportsSubscriptions: false, 9048 9049 /** 9050 * Getter for the label. 9051 * @returns {String} The label. 9052 */ 9053 getLabel: function () { 9054 this.isLoaded(); 9055 return this.getData().label; 9056 }, 9057 9058 /** 9059 * @private 9060 * Getter for the forAll flag. 9061 * @returns {Boolean} True if global. 9062 */ 9063 getForAll: function () { 9064 this.isLoaded(); 9065 return this.getData().forAll; 9066 }, 9067 9068 /** 9069 * @private 9070 * Getter for the Uri value. 9071 * @returns {String} The Uri. 9072 */ 9073 getUri: function () { 9074 this.isLoaded(); 9075 return this.getData().uri; 9076 } 9077 }); 9078 9079 window.finesse = window.finesse || {}; 9080 window.finesse.restservices = window.finesse.restservices || {}; 9081 window.finesse.restservices.WrapUpReason = WrapUpReason; 9082 9083 return WrapUpReason; 9084 }); 9085 9086 /** 9087 * JavaScript representation of the Finesse WrapUpReasons collection 9088 * object which contains a list of WrapUpReason objects. 9089 * 9090 * @requires finesse.clientservices.ClientServices 9091 * @requires Class 9092 * @requires finesse.FinesseBase 9093 * @requires finesse.restservices.RestBase 9094 * @requires finesse.restservices.Dialog 9095 * @requires finesse.restservices.RestCollectionBase 9096 */ 9097 9098 /** @private */ 9099 define('restservices/WrapUpReasons',[ 9100 'restservices/RestCollectionBase', 9101 'restservices/WrapUpReason' 9102 ], 9103 function (RestCollectionBase, WrapUpReason) { 9104 9105 var WrapUpReasons = RestCollectionBase.extend(/** @lends finesse.restservices.WrapUpReasons.prototype */{ 9106 9107 /** 9108 * @class 9109 * JavaScript representation of a WrapUpReasons collection object. 9110 * @augments finesse.restservices.RestCollectionBase 9111 * @constructs 9112 * @see finesse.restservices.WrapUpReason 9113 * @example 9114 * _wrapUpReasons = _user.getWrapUpReasons ( { 9115 * onCollectionAdd : _handleWrapUpReasonAdd, 9116 * onCollectionDelete : _handleWrapUpReasonDelete, 9117 * onLoad : _handleWrapUpReasonsLoaded 9118 * }); 9119 * 9120 * _wrapUpReasonCollection = _wrapUpReasons.getCollection(); 9121 * for (var wrapUpReasonId in _wrapUpReasonCollection) { 9122 * if (_wrapUpReasonCollection.hasOwnProperty(wrapUpReasonId)) { 9123 * _wrapUpReason = _wrapUpReasonCollection[wrapUpReasonId]; 9124 * etc... 9125 * } 9126 * } 9127 */ 9128 _fakeConstuctor: function () { 9129 /* This is here to hide the real init constructor from the public docs */ 9130 }, 9131 9132 /** 9133 * @private 9134 * JavaScript representation of a WrapUpReasons collection object. Also exposes 9135 * methods to operate on the object against the server. 9136 * 9137 * @param {Object} options 9138 * An object with the following properties:<ul> 9139 * <li><b>id:</b> The id of the object being constructed</li> 9140 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 9141 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 9142 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 9143 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 9144 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 9145 * <li><b>status:</b> {Number} The HTTP status code returned</li> 9146 * <li><b>content:</b> {String} Raw string of response</li> 9147 * <li><b>object:</b> {Object} Parsed object of response</li> 9148 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 9149 * <li><b>errorType:</b> {String} Type of error that was caught</li> 9150 * <li><b>errorMessage:</b> {String} Message associated with error</li> 9151 * </ul></li> 9152 * </ul></li> 9153 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 9154 **/ 9155 init: function (options) { 9156 this._super(options); 9157 }, 9158 9159 /** 9160 * @private 9161 * Gets the REST class for the current object - this is the WrapUpReasons class. 9162 */ 9163 getRestClass: function () { 9164 return WrapUpReasons; 9165 }, 9166 9167 /** 9168 * @private 9169 * Gets the REST class for the objects that make up the collection. - this 9170 * is the WrapUpReason class. 9171 */ 9172 getRestItemClass: function () { 9173 return WrapUpReason; 9174 }, 9175 9176 /** 9177 * @private 9178 * Gets the REST type for the current object - this is a "WrapUpReasons". 9179 */ 9180 getRestType: function () { 9181 return "WrapUpReasons"; 9182 }, 9183 9184 /** 9185 * @private 9186 * Gets the REST type for the objects that make up the collection - this is "WrapUpReason". 9187 */ 9188 getRestItemType: function () { 9189 return "WrapUpReason"; 9190 }, 9191 9192 /** 9193 * @private 9194 * Override default to indicates that the collection supports making 9195 * requests. 9196 */ 9197 supportsRequests: true, 9198 9199 /** 9200 * @private 9201 * Override default to indicate that this object doesn't support subscriptions. 9202 */ 9203 supportsRestItemSubscriptions: false, 9204 9205 /** 9206 * @private 9207 * Retrieve the Wrap-Up Reason Codes. This call will re-query the server and refresh the collection. 9208 * 9209 * @returns {finesse.restservices.WrapUpReasons} 9210 * This ReadyReasonCodes object to allow cascading. 9211 */ 9212 get: function () { 9213 // set loaded to false so it will rebuild the collection after the get 9214 this._loaded = false; 9215 // reset collection 9216 this._collection = {}; 9217 // perform get 9218 this._synchronize(); 9219 return this; 9220 } 9221 9222 }); 9223 9224 window.finesse = window.finesse || {}; 9225 window.finesse.restservices = window.finesse.restservices || {}; 9226 window.finesse.restservices.WrapUpReasons = WrapUpReasons; 9227 9228 return WrapUpReasons; 9229 }); 9230 9231 /** 9232 * JavaScript representation of the Finesse Contact object. 9233 * @requires finesse.clientservices.ClientServices 9234 * @requires Class 9235 * @requires finesse.FinesseBase 9236 * @requires finesse.restservices.RestBase 9237 */ 9238 /** @private */ 9239 define('restservices/Contact',['restservices/RestBase'], function (RestBase) { 9240 9241 var Contact = RestBase.extend(/** @lends finesse.restservices.Contact.prototype */{ 9242 9243 /** 9244 * @class 9245 * A Contact is a single entry in a PhoneBook, consisting of a First and Last Name, 9246 * a Phone Number, and a Description. 9247 * 9248 * @augments finesse.restservices.RestBase 9249 * @see finesse.restservices.PhoneBook 9250 * @constructs 9251 */ 9252 _fakeConstuctor: function () { 9253 /* This is here to hide the real init constructor from the public docs */ 9254 }, 9255 9256 /** 9257 * @private 9258 * @param {Object} options 9259 * An object with the following properties:<ul> 9260 * <li><b>id:</b> The id of the object being constructed</li> 9261 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 9262 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 9263 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 9264 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 9265 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 9266 * <li><b>status:</b> {Number} The HTTP status code returned</li> 9267 * <li><b>content:</b> {String} Raw string of response</li> 9268 * <li><b>object:</b> {Object} Parsed object of response</li> 9269 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 9270 * <li><b>errorType:</b> {String} Type of error that was caught</li> 9271 * <li><b>errorMessage:</b> {String} Message associated with error</li> 9272 * </ul></li> 9273 * </ul></li> 9274 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 9275 **/ 9276 init: function (options) { 9277 this._super(options); 9278 }, 9279 9280 /** 9281 * @private 9282 * Gets the REST class for the current object - this is the Contact class. 9283 * @returns {Object} The Contact class. 9284 */ 9285 getRestClass: function () { 9286 return Contact; 9287 }, 9288 9289 /** 9290 * @private 9291 * Gets the REST type for the current object - this is a "Contact". 9292 * @returns {String} The Contact string. 9293 */ 9294 getRestType: function () { 9295 return "Contact"; 9296 }, 9297 9298 /** 9299 * @private 9300 * Override default to indicate that this object doesn't support making 9301 * requests. 9302 */ 9303 supportsRequests: false, 9304 9305 /** 9306 * @private 9307 * Override default to indicate that this object doesn't support subscriptions. 9308 */ 9309 supportsSubscriptions: false, 9310 9311 /** 9312 * Getter for the firstName. 9313 * @returns {String} The firstName. 9314 */ 9315 getFirstName: function () { 9316 this.isLoaded(); 9317 return this.getData().firstName; 9318 }, 9319 9320 /** 9321 * Getter for the lastName. 9322 * @returns {String} The lastName. 9323 */ 9324 getLastName: function () { 9325 this.isLoaded(); 9326 return this.getData().lastName; 9327 }, 9328 9329 /** 9330 * Getter for the phoneNumber. 9331 * @returns {String} The phoneNumber. 9332 */ 9333 getPhoneNumber: function () { 9334 this.isLoaded(); 9335 return this.getData().phoneNumber; 9336 }, 9337 9338 /** 9339 * Getter for the description. 9340 * @returns {String} The description. 9341 */ 9342 getDescription: function () { 9343 this.isLoaded(); 9344 return this.getData().description; 9345 }, 9346 9347 /** @private */ 9348 createPutSuccessHandler: function(contact, contentBody, successHandler){ 9349 return function (rsp) { 9350 // Update internal structure based on response. Here we 9351 // inject the contentBody from the PUT request into the 9352 // rsp.object element to mimic a GET as a way to take 9353 // advantage of the existing _processResponse method. 9354 rsp.object = contentBody; 9355 contact._processResponse(rsp); 9356 9357 //Remove the injected Contact object before cascading response 9358 rsp.object = {}; 9359 9360 //cascade response back to consumer's response handler 9361 successHandler(rsp); 9362 }; 9363 }, 9364 9365 /** @private */ 9366 createPostSuccessHandler: function (contact, contentBody, successHandler) { 9367 return function (rsp) { 9368 rsp.object = contentBody; 9369 contact._processResponse(rsp); 9370 9371 //Remove the injected Contact object before cascading response 9372 rsp.object = {}; 9373 9374 //cascade response back to consumer's response handler 9375 successHandler(rsp); 9376 }; 9377 }, 9378 9379 /** 9380 * Add 9381 * @private 9382 */ 9383 add: function (newValues, handlers) { 9384 // this.isLoaded(); 9385 var contentBody = {}; 9386 9387 contentBody[this.getRestType()] = { 9388 "firstName": newValues.firstName, 9389 "lastName": newValues.lastName, 9390 "phoneNumber": newValues.phoneNumber, 9391 "description": newValues.description 9392 }; 9393 9394 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9395 handlers = handlers || {}; 9396 9397 this.restRequest(this.getRestUrl(), { 9398 method: 'POST', 9399 success: this.createPostSuccessHandler(this, contentBody, handlers.success), 9400 error: handlers.error, 9401 content: contentBody 9402 }); 9403 9404 return this; // Allow cascading 9405 }, 9406 9407 /** 9408 * Update 9409 * @private 9410 */ 9411 update: function (newValues, handlers) { 9412 this.isLoaded(); 9413 var contentBody = {}; 9414 9415 contentBody[this.getRestType()] = { 9416 "uri": this.getId(), 9417 "firstName": newValues.firstName, 9418 "lastName": newValues.lastName, 9419 "phoneNumber": newValues.phoneNumber, 9420 "description": newValues.description 9421 }; 9422 9423 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9424 handlers = handlers || {}; 9425 9426 this.restRequest(this.getRestUrl(), { 9427 method: 'PUT', 9428 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 9429 error: handlers.error, 9430 content: contentBody 9431 }); 9432 9433 return this; // Allow cascading 9434 }, 9435 9436 9437 /** 9438 * Delete 9439 * @private 9440 */ 9441 "delete": function ( handlers) { 9442 this.isLoaded(); 9443 9444 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9445 handlers = handlers || {}; 9446 9447 this.restRequest(this.getRestUrl(), { 9448 method: 'DELETE', 9449 success: this.createPutSuccessHandler(this, {}, handlers.success), 9450 error: handlers.error, 9451 content: undefined 9452 }); 9453 9454 return this; // Allow cascading 9455 } 9456 }); 9457 9458 window.finesse = window.finesse || {}; 9459 window.finesse.restservices = window.finesse.restservices || {}; 9460 window.finesse.restservices.Contact = Contact; 9461 9462 return Contact; 9463 }); 9464 9465 /** 9466 * JavaScript representation of the Finesse Contacts collection 9467 * object which contains a list of Contact objects. 9468 * 9469 * @requires finesse.clientservices.ClientServices 9470 * @requires Class 9471 * @requires finesse.FinesseBase 9472 * @requires finesse.restservices.RestBase 9473 * @requires finesse.restservices.Dialog 9474 * @requires finesse.restservices.RestCollectionBase 9475 */ 9476 /** @private */ 9477 define('restservices/Contacts',[ 9478 'restservices/RestCollectionBase', 9479 'restservices/Contact' 9480 ], 9481 function (RestCollectionBase, Contact) { 9482 var Contacts = RestCollectionBase.extend(/** @lends finesse.restservices.Contacts.prototype */{ 9483 9484 /** 9485 * @class 9486 * JavaScript representation of a Contacts collection object. Also exposes 9487 * methods to operate on the object against the server. 9488 * @augments finesse.restservices.RestCollectionBase 9489 * @constructs 9490 * @see finesse.restservices.Contact 9491 * @see finesse.restservices.PhoneBook 9492 * @example 9493 * _contacts = _phonebook.getContacts( { 9494 * onCollectionAdd : _handleContactAdd, 9495 * onCollectionDelete : _handleContactDelete, 9496 * onLoad : _handleContactsLoaded 9497 * }); 9498 * 9499 * _contactCollection = _contacts.getCollection(); 9500 * for (var contactId in _contactCollection) { 9501 * if (_contactCollection.hasOwnProperty(contactId)) { 9502 * _contact = _contactCollection[contactId]; 9503 * etc... 9504 * } 9505 * } 9506 */ 9507 _fakeConstuctor: function () { 9508 /* This is here to hide the real init constructor from the public docs */ 9509 }, 9510 9511 /** 9512 * @private 9513 * JavaScript representation of a Contacts collection object. Also exposes 9514 * methods to operate on the object against the server. 9515 * 9516 * @param {Object} options 9517 * An object with the following properties:<ul> 9518 * <li><b>id:</b> The id of the object being constructed</li> 9519 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 9520 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 9521 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 9522 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 9523 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 9524 * <li><b>status:</b> {Number} The HTTP status code returned</li> 9525 * <li><b>content:</b> {String} Raw string of response</li> 9526 * <li><b>object:</b> {Object} Parsed object of response</li> 9527 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 9528 * <li><b>errorType:</b> {String} Type of error that was caught</li> 9529 * <li><b>errorMessage:</b> {String} Message associated with error</li> 9530 * </ul></li> 9531 * </ul></li> 9532 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 9533 **/ 9534 init: function (options) { 9535 this._super(options); 9536 }, 9537 9538 /** 9539 * @private 9540 * Gets the REST class for the current object - this is the Contacts class. 9541 */ 9542 getRestClass: function () { 9543 return Contacts; 9544 }, 9545 9546 /** 9547 * @private 9548 * Gets the REST class for the objects that make up the collection. - this 9549 * is the Contact class. 9550 */ 9551 getRestItemClass: function () { 9552 return Contact; 9553 }, 9554 9555 /** 9556 * @private 9557 * Gets the REST type for the current object - this is a "Contacts". 9558 */ 9559 getRestType: function () { 9560 return "Contacts"; 9561 }, 9562 9563 /** 9564 * @private 9565 * Gets the REST type for the objects that make up the collection - this is "Contacts". 9566 */ 9567 getRestItemType: function () { 9568 return "Contact"; 9569 }, 9570 9571 /** 9572 * @private 9573 * Override default to indicates that the collection supports making 9574 * requests. 9575 */ 9576 supportsRequests: true, 9577 9578 /** 9579 * @private 9580 * Override default to indicates that the collection subscribes to its objects. 9581 */ 9582 supportsRestItemSubscriptions: false, 9583 9584 /** 9585 * @private 9586 * Retrieve the Contacts. This call will re-query the server and refresh the collection. 9587 * 9588 * @returns {finesse.restservices.Contacts} 9589 * This Contacts object, to allow cascading. 9590 */ 9591 get: function () { 9592 // set loaded to false so it will rebuild the collection after the get 9593 this._loaded = false; 9594 // reset collection 9595 this._collection = {}; 9596 // perform get 9597 this._synchronize(); 9598 return this; 9599 } 9600 9601 }); 9602 9603 window.finesse = window.finesse || {}; 9604 window.finesse.restservices = window.finesse.restservices || {}; 9605 window.finesse.restservices.Contacts = Contacts; 9606 9607 9608 return Contacts; 9609 }); 9610 9611 /** 9612 * JavaScript representation of the Finesse PhoneBook object. 9613 * 9614 * @requires finesse.clientservices.ClientServices 9615 * @requires Class 9616 * @requires finesse.FinesseBase 9617 * @requires finesse.restservices.RestBase 9618 */ 9619 9620 /** @private */ 9621 define('restservices/PhoneBook',[ 9622 'restservices/RestBase', 9623 'restservices/Contacts' 9624 ], 9625 function (RestBase, Contacts) { 9626 var PhoneBook = RestBase.extend(/** @lends finesse.restservices.PhoneBook.prototype */{ 9627 9628 _contacts: null, 9629 9630 /** 9631 * @class 9632 * A PhoneBook is a list of Contacts available to a User for quick dial. 9633 * 9634 * @augments finesse.restservices.RestBase 9635 * @see finesse.restservices.Contacts 9636 * @constructs 9637 */ 9638 _fakeConstuctor: function () { 9639 /* This is here to hide the real init constructor from the public docs */ 9640 }, 9641 9642 /** 9643 * @private 9644 * JavaScript representation of a PhoneBook object. Also exposes 9645 * methods to operate on the object against the server. 9646 * 9647 * @param {Object} options 9648 * An object with the following properties:<ul> 9649 * <li><b>id:</b> The id of the object being constructed</li> 9650 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 9651 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 9652 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 9653 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 9654 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 9655 * <li><b>status:</b> {Number} The HTTP status code returned</li> 9656 * <li><b>content:</b> {String} Raw string of response</li> 9657 * <li><b>object:</b> {Object} Parsed object of response</li> 9658 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 9659 * <li><b>errorType:</b> {String} Type of error that was caught</li> 9660 * <li><b>errorMessage:</b> {String} Message associated with error</li> 9661 * </ul></li> 9662 * </ul></li> 9663 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 9664 **/ 9665 init: function (options) { 9666 this._super(options); 9667 }, 9668 9669 /** 9670 * @private 9671 * Gets the REST class for the current object - this is the PhoneBook class. 9672 * @returns {Object} The PhoneBook class. 9673 */ 9674 getRestClass: function () { 9675 return PhoneBook; 9676 }, 9677 9678 /** 9679 * @private 9680 * Gets the REST type for the current object - this is a "PhoneBook". 9681 * @returns {String} The PhoneBook string. 9682 */ 9683 getRestType: function () { 9684 return "PhoneBook"; 9685 }, 9686 9687 /** 9688 * @private 9689 * Override default to indicate that this object doesn't support making 9690 * requests. 9691 */ 9692 supportsRequests: false, 9693 9694 /** 9695 * @private 9696 * Override default to indicate that this object doesn't support subscriptions. 9697 */ 9698 supportsSubscriptions: false, 9699 9700 /** 9701 * Getter for the name of the Phone Book. 9702 * @returns {String} The name. 9703 */ 9704 getName: function () { 9705 this.isLoaded(); 9706 return this.getData().name; 9707 }, 9708 9709 /** 9710 * Getter for the type flag. 9711 * @returns {String} The type. 9712 */ 9713 getType: function () { 9714 this.isLoaded(); 9715 return this.getData().type; 9716 }, 9717 9718 /** 9719 * @private 9720 * Getter for the Uri value. 9721 * @returns {String} The Uri. 9722 */ 9723 getUri: function () { 9724 this.isLoaded(); 9725 return this.getData().uri; 9726 }, 9727 9728 /** 9729 * Getter for a Contacts collection object that is associated with PhoneBook. 9730 * @param {finesse.interfaces.RequestHandlers} handlers 9731 * An object containing the handlers for the request 9732 * @returns {finesse.restservices.Contacts} 9733 * A Contacts collection object. 9734 */ 9735 getContacts: function (callbacks) { 9736 var options = callbacks || {}; 9737 options.parentObj = this; 9738 this.isLoaded(); 9739 9740 if (this._contacts === null) { 9741 this._contacts = new Contacts(options); 9742 } 9743 9744 return this._contacts; 9745 }, 9746 9747 /** 9748 * Getter for <contacts> node within PhoneBook - sometimes it's just a URI, sometimes it is a Contacts collection 9749 * @returns {String} uri to contacts 9750 * or {finesse.restservices.Contacts} collection 9751 */ 9752 getEmbeddedContacts: function(){ 9753 this.isLoaded(); 9754 return this.getData().contacts; 9755 }, 9756 9757 /** @private */ 9758 createPutSuccessHandler: function(phonebook, contentBody, successHandler){ 9759 return function (rsp) { 9760 // Update internal structure based on response. Here we 9761 // inject the contentBody from the PUT request into the 9762 // rsp.object element to mimic a GET as a way to take 9763 // advantage of the existing _processResponse method. 9764 rsp.object = contentBody; 9765 phonebook._processResponse(rsp); 9766 9767 //Remove the injected PhoneBook object before cascading response 9768 rsp.object = {}; 9769 9770 //cascade response back to consumer's response handler 9771 successHandler(rsp); 9772 }; 9773 }, 9774 9775 /** @private */ 9776 createPostSuccessHandler: function (phonebook, contentBody, successHandler) { 9777 return function (rsp) { 9778 rsp.object = contentBody; 9779 phonebook._processResponse(rsp); 9780 9781 //Remove the injected PhoneBook object before cascading response 9782 rsp.object = {}; 9783 9784 //cascade response back to consumer's response handler 9785 successHandler(rsp); 9786 }; 9787 }, 9788 9789 /** 9790 * @private 9791 * Add a PhoneBook. 9792 * @param {Object} newValues 9793 * @param {String} newValues.name Name of PhoneBook 9794 * @param {String} newValues.type Type of PhoneBook 9795 * @param {finesse.interfaces.RequestHandlers} handlers 9796 * An object containing the handlers for the request 9797 * @returns {finesse.restservices.PhoneBook} 9798 * This PhoneBook object, to allow cascading 9799 */ 9800 add: function (newValues, handlers) { 9801 // this.isLoaded(); 9802 var contentBody = {}; 9803 9804 contentBody[this.getRestType()] = { 9805 "name": newValues.name, 9806 "type": newValues.type 9807 }; 9808 9809 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9810 handlers = handlers || {}; 9811 9812 this.restRequest(this.getRestUrl(), { 9813 method: 'POST', 9814 success: this.createPostSuccessHandler(this, contentBody, handlers.success), 9815 error: handlers.error, 9816 content: contentBody 9817 }); 9818 9819 return this; // Allow cascading 9820 }, 9821 9822 /** 9823 * @private 9824 * Update a PhoneBook. 9825 * @param {Object} newValues 9826 * @param {String} newValues.name Name of PhoneBook 9827 * @param {String} newValues.type Type of PhoneBook 9828 * @param {finesse.interfaces.RequestHandlers} handlers 9829 * An object containing the handlers for the request 9830 * @returns {finesse.restservices.PhoneBook} 9831 * This PhoneBook object, to allow cascading 9832 */ 9833 update: function (newValues, handlers) { 9834 this.isLoaded(); 9835 var contentBody = {}; 9836 9837 contentBody[this.getRestType()] = { 9838 "uri": this.getId(), 9839 "name": newValues.name, 9840 "type": newValues.type 9841 }; 9842 9843 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9844 handlers = handlers || {}; 9845 9846 this.restRequest(this.getRestUrl(), { 9847 method: 'PUT', 9848 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 9849 error: handlers.error, 9850 content: contentBody 9851 }); 9852 9853 return this; // Allow cascading 9854 }, 9855 9856 9857 /** 9858 * Delete a PhoneBook. 9859 * @param {finesse.interfaces.RequestHandlers} handlers 9860 * An object containing the handlers for the request 9861 * @returns {finesse.restservices.PhoneBook} 9862 * This PhoneBook object, to allow cascading 9863 */ 9864 "delete": function ( handlers) { 9865 this.isLoaded(); 9866 9867 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 9868 handlers = handlers || {}; 9869 9870 this.restRequest(this.getRestUrl(), { 9871 method: 'DELETE', 9872 success: this.createPutSuccessHandler(this, {}, handlers.success), 9873 error: handlers.error, 9874 content: undefined 9875 }); 9876 9877 return this; // Allow cascading 9878 } 9879 9880 9881 9882 }); 9883 9884 window.finesse = window.finesse || {}; 9885 window.finesse.restservices = window.finesse.restservices || {}; 9886 window.finesse.restservices.PhoneBook = PhoneBook; 9887 9888 return PhoneBook; 9889 }); 9890 9891 /** 9892 * JavaScript representation of the Finesse PhoneBooks collection 9893 * object which contains a list of PhoneBook objects. 9894 * 9895 * @requires finesse.clientservices.ClientServices 9896 * @requires Class 9897 * @requires finesse.FinesseBase 9898 * @requires finesse.restservices.RestBase 9899 * @requires finesse.restservices.Dialog 9900 * @requires finesse.restservices.RestCollectionBase 9901 */ 9902 /** @private */ 9903 define('restservices/PhoneBooks',[ 9904 'restservices/RestCollectionBase', 9905 'restservices/PhoneBook' 9906 ], 9907 function (RestCollectionBase, PhoneBook) { 9908 var PhoneBooks = RestCollectionBase.extend(/** @lends finesse.restservices.PhoneBooks.prototype */{ 9909 9910 /** 9911 * @class 9912 * JavaScript representation of a PhoneBooks collection object. 9913 * @augments finesse.restservices.RestCollectionBase 9914 * @constructs 9915 * @see finesse.restservices.PhoneBook 9916 * @see finesse.restservices.Contacts 9917 * @see finesse.restservices.Contact 9918 * @example 9919 * _phoneBooks = _user.getPhoneBooks( { 9920 * onCollectionAdd : _handlePhoneBookAdd, 9921 * onCollectionDelete : _handlePhoneBookDelete, 9922 * onLoad : _handlePhoneBooksLoaded 9923 * }); 9924 * 9925 * _phoneBookCollection = _phoneBooks.getCollection(); 9926 * for (var phoneBookId in _phoneBookCollection) { 9927 * if (_phoneBookCollection.hasOwnProperty(phoneBookId)) { 9928 * _phoneBook = _phoneBookCollection[phoneBookId]; 9929 * etc... 9930 * } 9931 * } 9932 */ 9933 _fakeConstuctor: function () { 9934 /* This is here to hide the real init constructor from the public docs */ 9935 }, 9936 9937 /** 9938 * @private 9939 * 9940 * @param {Object} options 9941 * An object with the following properties:<ul> 9942 * <li><b>id:</b> The id of the object being constructed</li> 9943 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 9944 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 9945 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 9946 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 9947 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 9948 * <li><b>status:</b> {Number} The HTTP status code returned</li> 9949 * <li><b>content:</b> {String} Raw string of response</li> 9950 * <li><b>object:</b> {Object} Parsed object of response</li> 9951 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 9952 * <li><b>errorType:</b> {String} Type of error that was caught</li> 9953 * <li><b>errorMessage:</b> {String} Message associated with error</li> 9954 * </ul></li> 9955 * </ul></li> 9956 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 9957 **/ 9958 init: function (options) { 9959 // Keep the REST response for PhoneBooks to check for 206 Partial Content. 9960 this.keepRestResponse = true; 9961 // Add in the Range header which is required for PhoneBooks API. 9962 this.extraHeaders = { "Range": "objects=1-1500" }; 9963 this._super(options); 9964 }, 9965 9966 /** 9967 * @private 9968 * Gets the REST class for the current object - this is the PhoneBooks class. 9969 */ 9970 getRestClass: function () { 9971 return PhoneBooks; 9972 }, 9973 9974 /** 9975 * @private 9976 * Gets the REST class for the objects that make up the collection. - this 9977 * is the PhoneBook class. 9978 */ 9979 getRestItemClass: function () { 9980 return PhoneBook; 9981 }, 9982 9983 /** 9984 * @private 9985 * Gets the REST type for the current object - this is a "PhoneBooks". 9986 */ 9987 getRestType: function () { 9988 return "PhoneBooks"; 9989 }, 9990 9991 /** 9992 * @private 9993 * Gets the REST type for the objects that make up the collection - this is "PhoneBooks". 9994 */ 9995 getRestItemType: function () { 9996 return "PhoneBook"; 9997 }, 9998 9999 /** 10000 * @private 10001 * Override default to indicates that the collection supports making 10002 * requests. 10003 */ 10004 supportsRequests: true, 10005 10006 /** 10007 * @private 10008 * Override default to indicates that the collection subscribes to its objects. 10009 */ 10010 supportsRestItemSubscriptions: false, 10011 10012 /** 10013 * @private 10014 * Retrieve the PhoneBooks. This call will re-query the server and refresh the collection. 10015 * 10016 * @returns {finesse.restservices.PhoneBooks} 10017 * This PhoneBooks object, to allow cascading. 10018 */ 10019 get: function () { 10020 // set loaded to false so it will rebuild the collection after the get 10021 this._loaded = false; 10022 // reset collection 10023 this._collection = {}; 10024 // perform get 10025 this._synchronize(); 10026 return this; 10027 } 10028 10029 }); 10030 10031 window.finesse = window.finesse || {}; 10032 window.finesse.restservices = window.finesse.restservices || {}; 10033 window.finesse.restservices.PhoneBooks = PhoneBooks; 10034 10035 return PhoneBooks; 10036 }); 10037 10038 /** 10039 * JavaScript representation of the Finesse WorkflowAction object. 10040 * 10041 * @requires finesse.clientservices.ClientServices 10042 * @requires Class 10043 * @requires finesse.FinesseBase 10044 * @requires finesse.restservices.RestBase 10045 */ 10046 10047 /*jslint browser: true, nomen: true, sloppy: true, forin: true */ 10048 /*global define,finesse */ 10049 10050 /** @private */ 10051 define('restservices/WorkflowAction',['restservices/RestBase'], function (RestBase) { 10052 10053 var WorkflowAction = RestBase.extend({ 10054 10055 _contacts: null, 10056 10057 actionTypes: [ 10058 { 10059 name: 'BROWSER_POP', 10060 params: [ 10061 { 10062 name: 'windowName', 10063 type: 'text' 10064 }, 10065 { 10066 name: 'path', 10067 type: 'systemVariableSingleLineEditor' 10068 } 10069 ] 10070 }, 10071 { 10072 name: 'HTTP_REQUEST', 10073 params: [ 10074 { 10075 name: 'method', 10076 type: 'dropdown', 10077 values: ['POST', 'PUT'] 10078 }, 10079 { 10080 name: 'location', 10081 type: 'dropdown', 10082 values: ['FINESSE', 'OTHER'] 10083 }, 10084 { 10085 name: 'contentType', 10086 type: 'text' 10087 }, 10088 { 10089 name: 'path', 10090 type: 'systemVariableSingleLineEditor' 10091 }, 10092 { 10093 name: 'body', 10094 type: 'systemVariableMultiLineEditor' 10095 } 10096 ] 10097 } 10098 // more action type definitions here 10099 ], 10100 10101 /** 10102 * @class 10103 * A WorkflowAction is an action (e.g. Browser Pop, Rest Request) defined in a 10104 * Workflow and triggered by a system event (Call Received, Call Ended, etc.). 10105 * 10106 * @augments finesse.restservices.RestBase 10107 * @see finesse.restservices.Workflow 10108 * @constructs 10109 */ 10110 _fakeConstuctor: function () { 10111 /* This is here to hide the real init constructor from the public docs */ 10112 }, 10113 10114 /** 10115 * @private 10116 * JavaScript representation of a WorkflowAction object. Also exposes 10117 * methods to operate on the object against the server. 10118 * 10119 * @param {Object} options 10120 * An object with the following properties:<ul> 10121 * <li><b>id:</b> The id of the object being constructed</li> 10122 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 10123 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 10124 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 10125 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 10126 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 10127 * <li><b>status:</b> {Number} The HTTP status code returned</li> 10128 * <li><b>content:</b> {String} Raw string of response</li> 10129 * <li><b>object:</b> {Object} Parsed object of response</li> 10130 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 10131 * <li><b>errorType:</b> {String} Type of error that was caught</li> 10132 * <li><b>errorMessage:</b> {String} Message associated with error</li> 10133 * </ul></li> 10134 * </ul></li> 10135 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 10136 **/ 10137 init: function (options) { 10138 this._super(options); 10139 }, 10140 10141 /** 10142 * @private 10143 * Gets the REST class for the current object - this is the WorkflowAction class. 10144 * @returns {Object} The WorkflowAction class. 10145 */ 10146 getRestClass: function () { 10147 return finesse.restservices.WorkflowAction; 10148 }, 10149 10150 /** 10151 * @private 10152 * Gets the REST type for the current object - this is a "WorkflowAction". 10153 * @returns {String} The WorkflowAction string. 10154 */ 10155 getRestType: function () { 10156 return "WorkflowAction"; 10157 }, 10158 10159 /** 10160 * @private 10161 * Override default to indicate that this object doesn't support making 10162 * requests. 10163 */ 10164 supportsRequests: false, 10165 10166 /** 10167 * @private 10168 * Override default to indicate that this object doesn't support subscriptions. 10169 */ 10170 supportsSubscriptions: false, 10171 10172 /** 10173 * Getter for the name. 10174 * @returns {String} The name. 10175 */ 10176 getName: function () { 10177 this.isLoaded(); 10178 return this.getData().name; 10179 }, 10180 10181 /** 10182 * Getter for the type flag. 10183 * @returns {String} The type. 10184 */ 10185 getType: function () { 10186 this.isLoaded(); 10187 return this.getData().type; 10188 }, 10189 10190 /** 10191 * @private 10192 * Getter for the Uri value. 10193 * @returns {String} The Uri. 10194 */ 10195 getUri: function () { 10196 this.isLoaded(); 10197 return this.getData().uri; 10198 }, 10199 10200 /** 10201 * @private 10202 * Getter for the handledBy value. 10203 * @returns {String} handledBy. 10204 */ 10205 getHandledBy: function () { 10206 this.isLoaded(); 10207 return this.getData().handledBy; 10208 }, 10209 10210 /** 10211 * Getter for the parameters. 10212 * @returns {Object} key = param name, value = param value 10213 */ 10214 getParams: function () { 10215 var map = {}, 10216 params = this.getData().params.Param, 10217 i, 10218 param; 10219 10220 for(i=0; i<params.length; i+=1){ 10221 param = params[i]; 10222 map[param.name] = param.value || ""; 10223 } 10224 10225 return map; 10226 }, 10227 10228 /** 10229 * Getter for the ActionVariables 10230 * @returns {Object} key = action variable name, value = Object{name, type, node, testValue} 10231 */ 10232 getActionVariables: function() { 10233 var map = {}, 10234 actionVariablesParent = this.getData().actionVariables, 10235 actionVariables, 10236 i, 10237 actionVariable; 10238 10239 if (actionVariablesParent === null || typeof(actionVariablesParent) === "undefined" || actionVariablesParent.length === 0){ 10240 return map; 10241 } 10242 actionVariables = actionVariablesParent.ActionVariable; 10243 10244 if(actionVariables.length > 0){ 10245 for(i=0; i<actionVariables.length; i+=1){ 10246 actionVariable = actionVariables[i]; 10247 // escape nulls to empty string 10248 actionVariable.name = actionVariable.name || ""; 10249 actionVariable.type = actionVariable.type || ""; 10250 actionVariable.node = actionVariable.node || ""; 10251 actionVariable.testValue = actionVariable.testValue || ""; 10252 map[actionVariable.name] = actionVariable; 10253 } 10254 } else { 10255 map[actionVariables.name] = actionVariables; 10256 } 10257 10258 return map; 10259 }, 10260 10261 /** @private */ 10262 createPutSuccessHandler: function(action, contentBody, successHandler){ 10263 return function (rsp) { 10264 // Update internal structure based on response. Here we 10265 // inject the contentBody from the PUT request into the 10266 // rsp.object element to mimic a GET as a way to take 10267 // advantage of the existing _processResponse method. 10268 rsp.object = contentBody; 10269 action._processResponse(rsp); 10270 10271 //Remove the injected WorkflowAction object before cascading response 10272 rsp.object = {}; 10273 10274 //cascade response back to consumer's response handler 10275 successHandler(rsp); 10276 }; 10277 }, 10278 10279 /** @private */ 10280 createPostSuccessHandler: function (action, contentBody, successHandler) { 10281 return function (rsp) { 10282 rsp.object = contentBody; 10283 action._processResponse(rsp); 10284 10285 //Remove the injected WorkflowAction object before cascading response 10286 rsp.object = {}; 10287 10288 //cascade response back to consumer's response handler 10289 successHandler(rsp); 10290 }; 10291 }, 10292 10293 /** 10294 * @private 10295 * Build params array out of all the values coming into add or update methods 10296 * paramMap is a map of params.. we need to translate it into an array of Param objects 10297 * where path and windowName are params for the BROWSER_POP type 10298 */ 10299 buildParamsForRest: function(paramMap){ 10300 var params = {"Param": []}, 10301 i; 10302 for(i in paramMap){ 10303 if(paramMap.hasOwnProperty(i)){ 10304 params.Param.push({name: i, value: paramMap[i]}); 10305 } 10306 } 10307 return params; 10308 }, 10309 10310 /** 10311 * @private 10312 * Build actionVariables array out of all the values coming into add or update methods 10313 * actionVariableMap is a map of actionVariables.. we need to translate it into an array of ActionVariable objects 10314 * where path and windowName are params for the BROWSER_POP type 10315 */ 10316 buildActionVariablesForRest: function(actionVariableMap){ 10317 var actionVariables = {"ActionVariable": []}, 10318 i, 10319 actionVariable; 10320 for(i in actionVariableMap){ 10321 if(actionVariableMap.hasOwnProperty(i)){ 10322 // {name: "callVariable1", type: "SYSTEM", node: "", testValue: "<blink>"} 10323 actionVariable = { 10324 "name": actionVariableMap[i].name, 10325 "type": actionVariableMap[i].type, 10326 "node": actionVariableMap[i].node, 10327 "testValue": actionVariableMap[i].testValue 10328 }; 10329 actionVariables.ActionVariable.push(actionVariable); 10330 } 10331 } 10332 return actionVariables; 10333 }, 10334 10335 /** 10336 * Add 10337 */ 10338 add: function (newValues, handlers) { 10339 var contentBody = {}; 10340 10341 contentBody[this.getRestType()] = { 10342 "name": newValues.name, 10343 "type": newValues.type, 10344 "handledBy": newValues.handledBy, 10345 "params": this.buildParamsForRest(newValues.params), 10346 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables) 10347 }; 10348 10349 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10350 handlers = handlers || {}; 10351 10352 this.restRequest(this.getRestUrl(), { 10353 method: 'POST', 10354 success: this.createPostSuccessHandler(this, contentBody, handlers.success), 10355 error: handlers.error, 10356 content: contentBody 10357 }); 10358 10359 return this; // Allow cascading 10360 }, 10361 10362 /** 10363 * @private 10364 * Update 10365 */ 10366 update: function (newValues, handlers) { 10367 this.isLoaded(); 10368 var contentBody = {}; 10369 10370 contentBody[this.getRestType()] = { 10371 "uri": this.getId(), 10372 "name": newValues.name, 10373 "type": newValues.type, 10374 "handledBy": newValues.handledBy, 10375 "params": this.buildParamsForRest(newValues.params), 10376 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables) 10377 }; 10378 10379 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10380 handlers = handlers || {}; 10381 10382 this.restRequest(this.getRestUrl(), { 10383 method: 'PUT', 10384 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 10385 error: handlers.error, 10386 content: contentBody 10387 }); 10388 10389 return this; // Allow cascading 10390 }, 10391 10392 10393 /** 10394 * @private 10395 * Delete 10396 */ 10397 "delete": function ( handlers) { 10398 this.isLoaded(); 10399 10400 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10401 handlers = handlers || {}; 10402 10403 this.restRequest(this.getRestUrl(), { 10404 method: 'DELETE', 10405 success: this.createPutSuccessHandler(this, {}, handlers.success), 10406 error: handlers.error, 10407 content: undefined 10408 }); 10409 10410 return this; // Allow cascading 10411 } 10412 10413 10414 10415 }); 10416 10417 window.finesse = window.finesse || {}; 10418 window.finesse.restservices = window.finesse.restservices || {}; 10419 window.finesse.restservices.WorkflowAction = WorkflowAction; 10420 10421 return WorkflowAction; 10422 }); 10423 10424 /** 10425 * JavaScript representation of the Finesse WorkflowActions collection 10426 * object which contains a list of WorkflowAction objects. 10427 * 10428 * @requires finesse.clientservices.ClientServices 10429 * @requires Class 10430 * @requires finesse.FinesseBase 10431 * @requires finesse.restservices.RestBase 10432 * @requires finesse.restservices.Dialog 10433 * @requires finesse.restservices.RestCollectionBase 10434 */ 10435 10436 /** @private */ 10437 define('restservices/WorkflowActions',[ 10438 'restservices/RestCollectionBase', 10439 'restservices/RestBase', 10440 'restservices/WorkflowAction' 10441 ], 10442 function (RestCollectionBase, RestBase, WorkflowAction) { 10443 10444 var WorkflowActions = RestCollectionBase.extend({ 10445 10446 /** 10447 * @class 10448 * JavaScript representation of a WorkflowActions collection object. 10449 * @augments finesse.restservices.RestCollectionBase 10450 * @constructs 10451 * @see finesse.restservices.WorkflowAction 10452 * @see finesse.restservices.Workflow 10453 * @see finesse.restservices.Workflows 10454 * @example 10455 * _workflowActions = _user.getWorkflowActions( { 10456 * onCollectionAdd : _handleWorkflowActionAdd, 10457 * onCollectionDelete : _handleWorkflowActionDelete, 10458 * onLoad : _handleWorkflowActionsLoaded 10459 * }); 10460 */ 10461 _fakeConstuctor: function () { 10462 /* This is here to hide the real init constructor from the public docs */ 10463 }, 10464 10465 /** 10466 * @private 10467 * JavaScript representation of a WorkflowActions collection object. Also exposes 10468 * methods to operate on the object against the server. 10469 * 10470 * @param {Object} options 10471 * An object with the following properties:<ul> 10472 * <li><b>id:</b> The id of the object being constructed</li> 10473 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 10474 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 10475 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 10476 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 10477 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 10478 * <li><b>status:</b> {Number} The HTTP status code returned</li> 10479 * <li><b>content:</b> {String} Raw string of response</li> 10480 * <li><b>object:</b> {Object} Parsed object of response</li> 10481 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 10482 * <li><b>errorType:</b> {String} Type of error that was caught</li> 10483 * <li><b>errorMessage:</b> {String} Message associated with error</li> 10484 * </ul></li> 10485 * </ul></li> 10486 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 10487 **/ 10488 init: function (options) { 10489 this._super(options); 10490 }, 10491 10492 /** 10493 * @private 10494 * Gets the REST class for the current object - this is the WorkflowActions class. 10495 */ 10496 getRestClass: function () { 10497 return WorkflowActions; 10498 }, 10499 10500 /** 10501 * @private 10502 * Gets the REST class for the objects that make up the collection. - this 10503 * is the WorkflowAction class. 10504 */ 10505 getRestItemClass: function () { 10506 return WorkflowAction; 10507 }, 10508 10509 /** 10510 * @private 10511 * Gets the REST type for the current object - this is a "WorkflowActions". 10512 */ 10513 getRestType: function () { 10514 return "WorkflowActions"; 10515 }, 10516 10517 /** 10518 * @private 10519 * Gets the REST type for the objects that make up the collection - this is "WorkflowActions". 10520 */ 10521 getRestItemType: function () { 10522 return "WorkflowAction"; 10523 }, 10524 10525 /** 10526 * @private 10527 * Override default to indicates that the collection supports making 10528 * requests. 10529 */ 10530 supportsRequests: true, 10531 10532 /** 10533 * @private 10534 * Override default to indicates that the collection subscribes to its objects. 10535 */ 10536 supportsRestItemSubscriptions: false, 10537 10538 /** 10539 * @private 10540 * Retrieve the WorkflowActions. 10541 * 10542 * @returns {finesse.restservices.WorkflowActions} 10543 * This WorkflowActions object to allow cascading. 10544 */ 10545 get: function () { 10546 // set loaded to false so it will rebuild the collection after the get 10547 this._loaded = false; 10548 // reset collection 10549 this._collection = {}; 10550 // perform get 10551 this._synchronize(); 10552 return this; 10553 } 10554 }); 10555 10556 window.finesse = window.finesse || {}; 10557 window.finesse.restservices = window.finesse.restservices || {}; 10558 window.finesse.restservices.WorkflowActions = WorkflowActions; 10559 10560 return WorkflowActions; 10561 }); 10562 10563 /** 10564 * JavaScript representation of the Finesse Workflow object. 10565 * 10566 * @requires finesse.clientservices.ClientServices 10567 * @requires Class 10568 * @requires finesse.FinesseBase 10569 * @requires finesse.restservices.RestBase 10570 */ 10571 10572 /*jslint browser: true, nomen: true, sloppy: true, forin: true */ 10573 /*global define,finesse */ 10574 10575 /** @private */ 10576 define('restservices/Workflow',[ 10577 'restservices/RestBase', 10578 'restservices/WorkflowActions' 10579 ], 10580 function (RestBase, WorkflowActions) { 10581 10582 var Workflow = RestBase.extend({ 10583 10584 /** 10585 * @class 10586 * JavaScript representation of a Workflow object. Also exposes 10587 * methods to operate on the object against the server. 10588 * 10589 * @param {Object} options 10590 * An object with the following properties:<ul> 10591 * <li><b>id:</b> The id of the object being constructed</li> 10592 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 10593 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 10594 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 10595 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 10596 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 10597 * <li><b>status:</b> {Number} The HTTP status code returned</li> 10598 * <li><b>content:</b> {String} Raw string of response</li> 10599 * <li><b>object:</b> {Object} Parsed object of response</li> 10600 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 10601 * <li><b>errorType:</b> {String} Type of error that was caught</li> 10602 * <li><b>errorMessage:</b> {String} Message associated with error</li> 10603 * </ul></li> 10604 * </ul></li> 10605 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 10606 * @constructs 10607 **/ 10608 init: function (options) { 10609 this._super(options); 10610 }, 10611 10612 /** 10613 * @private 10614 * Gets the REST class for the current object - this is the Workflow class. 10615 * @returns {Object} The Workflow class. 10616 */ 10617 getRestClass: function () { 10618 return Workflow; 10619 }, 10620 10621 /** 10622 * @private 10623 * Gets the REST type for the current object - this is a "Workflow". 10624 * @returns {String} The Workflow string. 10625 */ 10626 getRestType: function () { 10627 return "Workflow"; 10628 }, 10629 10630 /** 10631 * @private 10632 * Override default to indicate that this object doesn't support making 10633 * requests. 10634 */ 10635 supportsRequests: false, 10636 10637 /** 10638 * @private 10639 * Override default to indicate that this object doesn't support subscriptions. 10640 */ 10641 supportsSubscriptions: false, 10642 10643 /** 10644 * @private 10645 * Getter for the Uri value. 10646 * @returns {String} The Uri. 10647 */ 10648 getUri: function () { 10649 this.isLoaded(); 10650 return this.getData().uri; 10651 }, 10652 10653 /** 10654 * Getter for the name. 10655 * @returns {String} The name. 10656 */ 10657 getName: function () { 10658 this.isLoaded(); 10659 return this.getData().name; 10660 }, 10661 10662 /** 10663 * Getter for the description. 10664 * @returns {String} The description. 10665 */ 10666 getDescription: function () { 10667 this.isLoaded(); 10668 return this.getData().description; 10669 }, 10670 10671 /** 10672 * Getter for the trigger set. 10673 * @returns {String} The trigger set. 10674 */ 10675 getTriggerSet: function () { 10676 this.isLoaded(); 10677 return this.getData().TriggerSet; 10678 }, 10679 10680 /** 10681 * Getter for the condition set. 10682 * @returns {String} The condition set. 10683 */ 10684 getConditionSet: function () { 10685 this.isLoaded(); 10686 return this.getData().ConditionSet; 10687 }, 10688 10689 /** 10690 * Getter for the assigned workflowActions. 10691 * @returns {String} The workflowActions object. 10692 */ 10693 getWorkflowActions: function () { 10694 this.isLoaded(); 10695 var workflowActions = this.getData().workflowActions; 10696 if (workflowActions === null) { 10697 workflowActions = ""; 10698 } 10699 return workflowActions; 10700 }, 10701 10702 createPutSuccessHandler: function (workflow, contentBody, successHandler) { 10703 return function (rsp) { 10704 // Update internal structure based on response. Here we 10705 // inject the contentBody from the PUT request into the 10706 // rsp.object element to mimic a GET as a way to take 10707 // advantage of the existing _processResponse method. 10708 rsp.object = contentBody; 10709 workflow._processResponse(rsp); 10710 10711 //Remove the injected Workflow object before cascading response 10712 rsp.object = {}; 10713 10714 //cascade response back to consumer's response handler 10715 successHandler(rsp); 10716 }; 10717 }, 10718 10719 createPostSuccessHandler: function (workflow, contentBody, successHandler) { 10720 return function (rsp) { 10721 rsp.object = contentBody; 10722 workflow._processResponse(rsp); 10723 10724 //Remove the injected Workflow object before cascading response 10725 rsp.object = {}; 10726 10727 //cascade response back to consumer's response handler 10728 successHandler(rsp); 10729 }; 10730 }, 10731 10732 /** 10733 * @private 10734 * Add 10735 */ 10736 add: function (newValues, handlers) { 10737 // this.isLoaded(); 10738 var contentBody = {}; 10739 10740 contentBody[this.getRestType()] = { 10741 "name": newValues.name, 10742 "description": newValues.description, 10743 "TriggerSet" : newValues.TriggerSet, 10744 "ConditionSet" : newValues.ConditionSet, 10745 "workflowActions" : newValues.workflowActions 10746 }; 10747 10748 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10749 handlers = handlers || {}; 10750 10751 this.restRequest(this.getRestUrl(), { 10752 method: 'POST', 10753 success: this.createPostSuccessHandler(this, contentBody, handlers.success), 10754 error: handlers.error, 10755 content: contentBody 10756 }); 10757 10758 return this; // Allow cascading 10759 }, 10760 10761 /** 10762 * @private 10763 * Update 10764 */ 10765 update: function (newValues, handlers) { 10766 this.isLoaded(); 10767 var contentBody = {}; 10768 10769 contentBody[this.getRestType()] = { 10770 "uri": this.getId(), 10771 "name": newValues.name, 10772 "description": newValues.description, 10773 "TriggerSet" : newValues.TriggerSet, 10774 "ConditionSet" : newValues.ConditionSet, 10775 "workflowActions" : newValues.workflowActions 10776 }; 10777 10778 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10779 handlers = handlers || {}; 10780 10781 this.restRequest(this.getRestUrl(), { 10782 method: 'PUT', 10783 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 10784 error: handlers.error, 10785 content: contentBody 10786 }); 10787 10788 return this; // Allow cascading 10789 }, 10790 10791 10792 /** 10793 * @private 10794 * Delete 10795 */ 10796 "delete": function (handlers) { 10797 this.isLoaded(); 10798 10799 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 10800 handlers = handlers || {}; 10801 10802 this.restRequest(this.getRestUrl(), { 10803 method: 'DELETE', 10804 success: this.createPutSuccessHandler(this, {}, handlers.success), 10805 error: handlers.error, 10806 content: undefined 10807 }); 10808 10809 return this; // Allow cascading 10810 } 10811 10812 10813 10814 }); 10815 10816 window.finesse = window.finesse || {}; 10817 window.finesse.restservices = window.finesse.restservices || {}; 10818 window.finesse.restservices.Workflow = Workflow; 10819 10820 return Workflow; 10821 }); 10822 10823 /** 10824 * JavaScript representation of the Finesse workflows collection 10825 * object which contains a list of workflow objects. 10826 * 10827 * @requires finesse.clientservices.ClientServices 10828 * @requires Class 10829 * @requires finesse.FinesseBase 10830 * @requires finesse.restservices.RestBase 10831 * @requires finesse.restservices.Dialog 10832 * @requires finesse.restservices.RestCollectionBase 10833 */ 10834 10835 /** @private */ 10836 define('restservices/Workflows',[ 10837 'restservices/RestCollectionBase', 10838 'restservices/RestBase', 10839 'restservices/Workflow' 10840 ], 10841 function (RestCollectionBase, RestBase, Workflow) { 10842 10843 var Workflows = RestCollectionBase.extend({ 10844 10845 /** 10846 * @class 10847 * JavaScript representation of a workflows collection object. Also exposes 10848 * methods to operate on the object against the server. 10849 * 10850 * @param {Object} options 10851 * An object with the following properties:<ul> 10852 * <li><b>id:</b> The id of the object being constructed</li> 10853 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 10854 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 10855 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 10856 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 10857 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 10858 * <li><b>status:</b> {Number} The HTTP status code returned</li> 10859 * <li><b>content:</b> {String} Raw string of response</li> 10860 * <li><b>object:</b> {Object} Parsed object of response</li> 10861 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 10862 * <li><b>errorType:</b> {String} Type of error that was caught</li> 10863 * <li><b>errorMessage:</b> {String} Message associated with error</li> 10864 * </ul></li> 10865 * </ul></li> 10866 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 10867 * @constructs 10868 **/ 10869 init: function (options) { 10870 this._super(options); 10871 }, 10872 10873 /** 10874 * @private 10875 * Gets the REST class for the current object - this is the workflows class. 10876 */ 10877 getRestClass: function () { 10878 return Workflows; 10879 }, 10880 10881 /** 10882 * @private 10883 * Gets the REST class for the objects that make up the collection. - this 10884 * is the workflow class. 10885 */ 10886 getRestItemClass: function () { 10887 return Workflow; 10888 }, 10889 10890 /** 10891 * @private 10892 * Gets the REST type for the current object - this is a "workflows". 10893 */ 10894 getRestType: function () { 10895 return "Workflows"; 10896 }, 10897 10898 /** 10899 * @private 10900 * Gets the REST type for the objects that make up the collection - this is "workflows". 10901 */ 10902 getRestItemType: function () { 10903 return "Workflow"; 10904 }, 10905 10906 /** 10907 * @private 10908 * Override default to indicates that the collection supports making requests. 10909 */ 10910 supportsRequests: true, 10911 10912 /** 10913 * @private 10914 * Override default to indicates that the collection does not subscribe to its objects. 10915 */ 10916 supportsRestItemSubscriptions: false, 10917 10918 /** 10919 * @private 10920 * Retrieve the workflows. This call will re-query the server and refresh the collection. 10921 * 10922 * @returns {finesse.restservices.workflows} 10923 * This workflows object to allow cascading. 10924 */ 10925 get: function () { 10926 // set loaded to false so it will rebuild the collection after the get 10927 this._loaded = false; 10928 // reset collection 10929 this._collection = {}; 10930 // perform get 10931 this._synchronize(); 10932 return this; 10933 } 10934 }); 10935 10936 window.finesse = window.finesse || {}; 10937 window.finesse.restservices = window.finesse.restservices || {}; 10938 window.finesse.restservices.Workflows = Workflows; 10939 10940 return Workflows; 10941 }); 10942 10943 /** 10944 * JavaScript representation of the Finesse MediaPropertiesLayout object for the Admin webapp. 10945 * @requires finesse.clientservices.ClientServices 10946 * @requires Class 10947 * @requires finesse.FinesseBase 10948 * @requires finesse.restservices.RestBase 10949 */ 10950 10951 /** The following comment is to prevent jslint errors about 10952 * using variables before they are defined. 10953 */ 10954 /*global finesse*/ 10955 10956 /** 10957 * @class 10958 * JavaScript representation of a MediaPropertiesLayout object for the Admin webapp. Also exposes 10959 * methods to operate on the object against the server. 10960 * 10961 * @constructor 10962 * @param {String} id 10963 * Not required... 10964 * @param {Object} callbacks 10965 * An object containing callbacks for instantiation and runtime 10966 * @param {Function} callbacks.onLoad(this) 10967 * Callback to invoke upon successful instantiation, passes in MediaPropertiesLayout object 10968 * @param {Function} callbacks.onLoadError(rsp) 10969 * Callback to invoke on instantiation REST request error 10970 * as passed by finesse.clientservices.ClientServices.ajax() 10971 * { 10972 * status: {Number} The HTTP status code returned 10973 * content: {String} Raw string of response 10974 * object: {Object} Parsed object of response 10975 * error: {Object} Wrapped exception that was caught 10976 * error.errorType: {String} Type of error that was caught 10977 * error.errorMessage: {String} Message associated with error 10978 * } 10979 * @param {Function} callbacks.onChange(this) 10980 * Callback to invoke upon successful update, passes in MediaPropertiesLayout object 10981 * @param {Function} callbacks.onError(rsp) 10982 * Callback to invoke on update error (refresh or event) 10983 * as passed by finesse.clientservices.ClientServices.ajax() 10984 * { 10985 * status: {Number} The HTTP status code returned 10986 * content: {String} Raw string of response 10987 * object: {Object} Parsed object of response 10988 * error: {Object} Wrapped exception that was caught 10989 * error.errorType: {String} Type of error that was caught 10990 * error.errorMessage: {String} Message associated with error 10991 * } 10992 */ 10993 10994 /** @private */ 10995 define('restservices/MediaPropertiesLayout',['restservices/RestBase'], function (RestBase) { 10996 var MediaPropertiesLayout = RestBase.extend(/** @lends finesse.restservices.MediaPropertiesLayout.prototype */{ 10997 10998 /** 10999 * @class 11000 * The MediaPropertiesLayout handles which call variables are associated with Dialogs. 11001 * 11002 * @augments finesse.restservices.RestBase 11003 * @see finesse.restservices.Dialog#getMediaProperties 11004 * @see finesse.restservices.User#getMediaPropertiesLayout 11005 * @constructs 11006 */ 11007 _fakeConstuctor: function () { 11008 /* This is here to hide the real init constructor from the public docs */ 11009 }, 11010 11011 /** 11012 * @private 11013 * JavaScript representation of a MediaPropertiesLayout object. Also exposes 11014 * methods to operate on the object against the server. 11015 * 11016 * @param {Object} options 11017 * An object with the following properties:<ul> 11018 * <li><b>id:</b> The id of the object being constructed</li> 11019 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 11020 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 11021 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 11022 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 11023 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 11024 * <li><b>status:</b> {Number} The HTTP status code returned</li> 11025 * <li><b>content:</b> {String} Raw string of response</li> 11026 * <li><b>object:</b> {Object} Parsed object of response</li> 11027 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 11028 * <li><b>errorType:</b> {String} Type of error that was caught</li> 11029 * <li><b>errorMessage:</b> {String} Message associated with error</li> 11030 * </ul></li> 11031 * </ul></li> 11032 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 11033 **/ 11034 init: function (options) { 11035 this._super(options); 11036 }, 11037 11038 /** 11039 * @private 11040 * Gets the REST class for the current object - this is the MediaPropertiesLayout object. 11041 */ 11042 getRestClass: function () { 11043 return MediaPropertiesLayout; 11044 }, 11045 11046 /** 11047 * @private 11048 * Gets the REST type for the current object - this is a "MediaPropertiesLayout". 11049 */ 11050 getRestType: function () { 11051 return "MediaPropertiesLayout"; 11052 }, 11053 11054 /** 11055 * @private 11056 * Returns whether this object supports subscriptions 11057 */ 11058 supportsSubscriptions: false, 11059 11060 /** 11061 * Getter for the name. 11062 * @returns {String} The name. 11063 */ 11064 getName: function () { 11065 this.isLoaded(); 11066 return this._data.name; 11067 }, 11068 11069 /** 11070 * Getter for the description. 11071 * @returns {String} The description. 11072 */ 11073 getDescription: function () { 11074 this.isLoaded(); 11075 return this._data.description || ""; 11076 }, 11077 11078 /** 11079 * Getter for the layout type (should be DEFAULT or CUSTOM). 11080 * @returns {String} The layout type. 11081 */ 11082 getType: function () { 11083 this.isLoaded(); 11084 return this._data.type || ""; 11085 }, 11086 11087 /** 11088 * Retrieve the media properties layout. This call will re-query the server and refresh the layout object. 11089 * @returns {finesse.restservices.MediaPropertiesLayout} 11090 * This MediaPropertiesLayout object to allow cascading 11091 */ 11092 get: function () { 11093 this._synchronize(); 11094 11095 return this; //Allow cascading 11096 }, 11097 11098 /** 11099 * Gets the data for this object. 11100 * 11101 * Performs safe conversion from raw API data to ensure that the returned layout object 11102 * always has a header with correct entry fields, and exactly two columns with lists of entries. 11103 * 11104 * @returns {finesse.restservices.MediaPropertiesLayout.Object} Data in columns (unless only one defined). 11105 */ 11106 getData: function () { 11107 11108 var layout = this._data, result, _addColumnData; 11109 11110 result = this.getEmptyData(); 11111 result.name = layout.name; 11112 result.description = layout.description; 11113 result.type = layout.type; 11114 11115 /** 11116 * @private 11117 */ 11118 _addColumnData = function (entryData, colIndex) { 11119 11120 if (!entryData) { 11121 //If there's no entry data at all, rewrite entryData to be an empty collection of entries 11122 entryData = {}; 11123 } else if (entryData.mediaProperty) { 11124 //If entryData contains the keys for a single entry rather than being a collection of entries, 11125 //rewrite it to be a collection containing a single entry 11126 entryData = { "": entryData }; 11127 } 11128 11129 //Add each of the entries in the list to the column 11130 jQuery.each(entryData, function (i, entryData) { 11131 11132 //If the entry has no displayName specified, explicitly set it to the empty string 11133 if (!entryData.displayName) { 11134 entryData.displayName = ""; 11135 } 11136 11137 result.columns[colIndex].push(entryData); 11138 11139 }); 11140 11141 }; 11142 11143 //The header should only contain a single entry 11144 if (layout.header && layout.header.entry) { 11145 11146 //If the entry has no displayName specified, explicitly set it to the empty string 11147 if (!layout.header.entry.displayName) { 11148 layout.header.entry.displayName = ""; 11149 } 11150 11151 result.header = layout.header.entry; 11152 11153 } else { 11154 11155 throw "MediaPropertiesLayout.getData() - Header does not contain an entry"; 11156 11157 } 11158 11159 //If the column object contains an entry object that wasn't part of a list of entries, 11160 //it must be a single right-hand entry object (left-hand entry object would be part of a list.) 11161 //Force the entry object to be the 2nd element in an otherwise-empty list. 11162 if (layout.column && layout.column.entry) { 11163 layout.column = [ 11164 null, 11165 { "entry": layout.column.entry } 11166 ]; 11167 } 11168 11169 if (layout.column && layout.column.length > 0 && layout.column.length <= 2) { 11170 11171 //Render left column entries 11172 if (layout.column[0] && layout.column[0].entry) { 11173 _addColumnData(layout.column[0].entry, 0); 11174 } 11175 11176 //Render right column entries 11177 if (layout.column[1] && layout.column[1].entry) { 11178 _addColumnData(layout.column[1].entry, 1); 11179 } 11180 11181 } 11182 11183 return result; 11184 11185 }, 11186 11187 /** 11188 * @private 11189 * Empty/template version of getData(). 11190 * 11191 * Used by getData(), and by callers of getData() in error cases. 11192 */ 11193 getEmptyData: function () { 11194 11195 return { 11196 header : { 11197 displayName: null, 11198 mediaProperty: null 11199 }, 11200 columns : [[], []] 11201 }; 11202 11203 }, 11204 11205 /** 11206 * Update the layout of this MediaPropertiesLayout 11207 * @param {Object} layout 11208 * The object representation of the layout you are setting 11209 * @param {finesse.interfaces.RequestHandlers} handlers 11210 * An object containing the handlers for the request 11211 * @returns {finesse.restservices.MediaPropertiesLayout} 11212 * This MediaPropertiesLayout object to allow cascading 11213 * @private 11214 */ 11215 update: function (newLayoutObject, handlers) { 11216 var contentBody = {}; 11217 11218 // Make sure type is kept the same 11219 newLayoutObject.type = this.getType(); 11220 11221 contentBody[this.getRestType()] = newLayoutObject; 11222 11223 //Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 11224 handlers = handlers || {}; 11225 11226 this.restRequest(this.getRestUrl(), { 11227 method: 'PUT', 11228 success: handlers.success, 11229 error: handlers.error, 11230 content: contentBody 11231 }); 11232 11233 return this; // Allow cascading 11234 }, 11235 11236 /** 11237 * Create a new MediaPropertiesLayout object with the layout passed in 11238 * @param {Object} layout 11239 * The object representation of the layout you are creating 11240 * @param {finesse.interfaces.RequestHandlers} handlers 11241 * An object containing the handlers for the request 11242 * @returns {finesse.restservices.MediaPropertiesLayout} 11243 * This MediaPropertiesLayout object to allow cascading 11244 * @private 11245 */ 11246 add: function (layout, handlers) { 11247 var contentBody = {}; 11248 11249 contentBody[this.getRestType()] = layout; 11250 11251 handlers = handlers || {}; 11252 11253 this.restRequest(this.getRestUrl(), { 11254 method: 'POST', 11255 success: handlers.success, 11256 error: handlers.error, 11257 content: contentBody 11258 }); 11259 11260 return this; // Allow cascading 11261 }, 11262 11263 /** 11264 * Delete this MediaPropertiesLayout 11265 * @param {finesse.interfaces.RequestHandlers} handlers 11266 * An object containing the handlers for the request 11267 * @returns {finesse.restservices.MediaPropertiesLayout} 11268 * This MediaPropertiesLayout object to allow cascading 11269 * @private 11270 */ 11271 "delete": function (handlers) { 11272 handlers = handlers || {}; 11273 11274 this.restRequest(this.getRestUrl(), { 11275 method: 'DELETE', 11276 success: handlers.success, 11277 error: handlers.error, 11278 content: undefined 11279 }); 11280 11281 return this; // Allow cascading 11282 } 11283 11284 }); 11285 11286 MediaPropertiesLayout.Object = /** @lends finesse.restservices.MediaPropertiesLayout.Object.prototype */ { 11287 /** 11288 * @class Format of MediaPropertiesLayout Object.<br> 11289 * Object { <ul> 11290 * <li>header : { <ul> 11291 * <li>dispayName {String} 11292 * <li>mediaProperty {String}</ul>} 11293 * <li>columns : { <ul> 11294 * <li>[ [] , [] ] 11295 * </ul> 11296 * where column arrays consists of the same Object format as header.<br> 11297 * }</ul> 11298 * }<br> 11299 * @constructs 11300 */ 11301 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 11302 11303 }; 11304 11305 window.finesse = window.finesse || {}; 11306 window.finesse.restservices = window.finesse.restservices || {}; 11307 window.finesse.restservices.MediaPropertiesLayout = MediaPropertiesLayout; 11308 11309 return MediaPropertiesLayout; 11310 }); 11311 11312 /** 11313 * JavaScript representation of the Finesse MediaPropertiesLayout object for a User 11314 * 11315 * @requires MediaPropertiesLayout 11316 * @requires ClientServices 11317 * @requires finesse.FinesseBase 11318 * @requires finesse.restservices.RestBase 11319 */ 11320 11321 /** The following comment is to prevent jslint errors about 11322 * using variables before they are defined. 11323 */ 11324 /*global finesse*/ 11325 11326 /** @private */ 11327 define('restservices/UserMediaPropertiesLayout',['restservices/MediaPropertiesLayout'], function (MediaPropertiesLayout) { 11328 var UserMediaPropertiesLayout = MediaPropertiesLayout.extend(/** @lends finesse.restservices.UserMediaPropertiesLayout.prototype */{ 11329 11330 /** 11331 * @class 11332 * JavaScript representation of a UserMediaPropertiesLayout collection object. Also exposes 11333 * methods to operate on the object against the server. 11334 * 11335 * @param {Object} options 11336 * An object with the following properties:<ul> 11337 * <li><b>id:</b> The id of the object being constructed</li> 11338 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 11339 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 11340 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 11341 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 11342 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 11343 * <li><b>status:</b> {Number} The HTTP status code returned</li> 11344 * <li><b>content:</b> {String} Raw string of response</li> 11345 * <li><b>object:</b> {Object} Parsed object of response</li> 11346 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 11347 * <li><b>errorType:</b> {String} Type of error that was caught</li> 11348 * <li><b>errorMessage:</b> {String} Message associated with error</li> 11349 * </ul></li> 11350 * </ul></li> 11351 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 11352 * @constructs 11353 **/ 11354 init: function (options) { 11355 this._super(options); 11356 }, 11357 11358 /** 11359 * @private 11360 * Gets the REST class for the current object - this is the UserMediaPropertiesLayout class. 11361 */ 11362 getRestClass: function () { 11363 return UserMediaPropertiesLayout; 11364 }, 11365 11366 /** 11367 * Overrides the parent class. Returns the url for the UserMediaPropertiesLayout resource 11368 */ 11369 getRestUrl: function () { 11370 return ("/finesse/api/User/" + this.getId() + "/" + this.getRestType()); 11371 }, 11372 11373 /** 11374 * @private 11375 * Override to throw an error because we cannot do an update on the User's 11376 * MediaPropertiesLayout node 11377 */ 11378 update: function (layout, handlers) { 11379 throw new Error("update(): Cannot update layout for User's MediaPropertiesLayout"); 11380 }, 11381 11382 /** 11383 * @private 11384 * Override to throw an error because we cannot create a new layout on the User's 11385 * MediaPropertiesLayout node 11386 */ 11387 add: function (layout, handlers) { 11388 throw new Error("add(): Cannot create a new layout for User's MediaPropertiesLayout"); 11389 }, 11390 11391 /** 11392 * @private 11393 * Override to throw an error because we cannot delete the layout on the User's 11394 * MediaPropertiesLayout node 11395 */ 11396 "delete": function (layout, handlers) { 11397 throw new Error("delete(): Cannot delete the layout for User's MediaPropertiesLayout"); 11398 } 11399 11400 }); 11401 11402 window.finesse = window.finesse || {}; 11403 window.finesse.restservices = window.finesse.restservices || {}; 11404 window.finesse.restservices.UserMediaPropertiesLayout = UserMediaPropertiesLayout; 11405 11406 return UserMediaPropertiesLayout; 11407 }); 11408 11409 /** 11410 * JavaScript representation of the Finesse mediaPropertiesLayouts collection 11411 * object which contains a list of mediaPropertiesLayout objects. 11412 * 11413 * @requires finesse.clientservices.ClientServices 11414 * @requires Class 11415 * @requires finesse.FinesseBase 11416 * @requires finesse.restservices.RestBase 11417 * @requires finesse.restservices.Dialog 11418 * @requires finesse.restservices.RestCollectionBase 11419 */ 11420 11421 /** @private */ 11422 define('restservices/MediaPropertiesLayouts',[ 11423 'restservices/RestCollectionBase', 11424 'restservices/RestBase', 11425 'restservices/MediaPropertiesLayout' 11426 ], 11427 function (RestCollectionBase, RestBase, MediaPropertiesLayout) { 11428 11429 var MediaPropertiesLayouts = RestCollectionBase.extend({ 11430 11431 /** 11432 * @class 11433 * JavaScript representation of a mediaPropertiesLayouts collection object. Also exposes 11434 * methods to operate on the object against the server. 11435 * 11436 * @param {Object} options 11437 * An object with the following properties:<ul> 11438 * <li><b>id:</b> The id of the object being constructed</li> 11439 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 11440 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 11441 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 11442 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 11443 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 11444 * <li><b>status:</b> {Number} The HTTP status code returned</li> 11445 * <li><b>content:</b> {String} Raw string of response</li> 11446 * <li><b>object:</b> {Object} Parsed object of response</li> 11447 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 11448 * <li><b>errorType:</b> {String} Type of error that was caught</li> 11449 * <li><b>errorMessage:</b> {String} Message associated with error</li> 11450 * </ul></li> 11451 * </ul></li> 11452 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 11453 * @constructs 11454 **/ 11455 init: function (options) { 11456 this._super(options); 11457 }, 11458 11459 /** 11460 * @private 11461 * Gets the REST class for the current object - this is the mediaPropertiesLayouts class. 11462 */ 11463 getRestClass: function () { 11464 return MediaPropertiesLayouts; 11465 }, 11466 11467 /** 11468 * @private 11469 * Gets the REST class for the objects that make up the collection. - this 11470 * is the mediaPropertiesLayout class. 11471 */ 11472 getRestItemClass: function () { 11473 return MediaPropertiesLayout; 11474 }, 11475 11476 /** 11477 * @private 11478 * Gets the REST type for the current object - this is a "mediaPropertiesLayouts". 11479 */ 11480 getRestType: function () { 11481 return "MediaPropertiesLayouts"; 11482 }, 11483 11484 /** 11485 * @private 11486 * Gets the REST type for the objects that make up the collection - this is "mediaPropertiesLayouts". 11487 */ 11488 getRestItemType: function () { 11489 return "MediaPropertiesLayout"; 11490 }, 11491 11492 /** 11493 * @private 11494 * Override default to indicates that the collection supports making requests. 11495 */ 11496 supportsRequests: true, 11497 11498 /** 11499 * @private 11500 * Override default to indicates that the collection does not subscribe to its objects. 11501 */ 11502 supportsRestItemSubscriptions: false, 11503 11504 /** 11505 * @private 11506 * Retrieve the MediaPropertiesLayouts. This call will re-query the server and refresh the collection. 11507 * 11508 * @returns {finesse.restservices.MediaPropertiesLayouts} 11509 * This MediaPropertiesLayouts object to allow cascading. 11510 */ 11511 get: function () { 11512 // set loaded to false so it will rebuild the collection after the get 11513 this._loaded = false; 11514 // reset collection 11515 this._collection = {}; 11516 // perform get 11517 this._synchronize(); 11518 return this; 11519 } 11520 }); 11521 11522 window.finesse = window.finesse || {}; 11523 window.finesse.restservices = window.finesse.restservices || {}; 11524 window.finesse.restservices.MediaPropertiesLayouts = MediaPropertiesLayouts; 11525 11526 return MediaPropertiesLayouts; 11527 }); 11528 11529 /** 11530 * JavaScript representation of the Finesse MediaPropertiesLayout object for a User 11531 * 11532 * @requires MediaPropertiesLayout 11533 * @requires ClientServices 11534 * @requires finesse.FinesseBase 11535 * @requires finesse.restservices.RestBase 11536 */ 11537 11538 /** The following comment is to prevent jslint errors about 11539 * using variables before they are defined. 11540 */ 11541 /*global finesse*/ 11542 11543 /** @private */ 11544 define('restservices/UserMediaPropertiesLayouts',[ 11545 'restservices/MediaPropertiesLayouts', 11546 'restservices/UserMediaPropertiesLayout' 11547 ], 11548 function (MediaPropertiesLayouts, UserMediaPropertiesLayout) { 11549 var UserMediaPropertiesLayouts = MediaPropertiesLayouts.extend(/** @lends finesse.restservices.UserMediaPropertiesLayouts.prototype */{ 11550 11551 /** 11552 * @class 11553 * JavaScript representation of a UserMediaPropertiesLayouts collection object. Also exposes 11554 * methods to operate on the object against the server. 11555 * 11556 * @param {Object} options 11557 * An object with the following properties:<ul> 11558 * <li><b>id:</b> The id of the object being constructed</li> 11559 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 11560 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 11561 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 11562 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 11563 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 11564 * <li><b>status:</b> {Number} The HTTP status code returned</li> 11565 * <li><b>content:</b> {String} Raw string of response</li> 11566 * <li><b>object:</b> {Object} Parsed object of response</li> 11567 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 11568 * <li><b>errorType:</b> {String} Type of error that was caught</li> 11569 * <li><b>errorMessage:</b> {String} Message associated with error</li> 11570 * </ul></li> 11571 * </ul></li> 11572 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 11573 * @constructs 11574 **/ 11575 init: function (options) { 11576 this._super(options); 11577 }, 11578 11579 /** 11580 * @private 11581 * Gets the REST class for the current object - this is the UserMediaPropertiesLayouts class. 11582 */ 11583 getRestClass: function () { 11584 return UserMediaPropertiesLayouts; 11585 }, 11586 11587 /** 11588 * @private 11589 * Gets the REST class for the objects that make up the collection. - this 11590 * is the UserMediaPropertiesLayout class. 11591 */ 11592 getRestItemClass: function() { 11593 return UserMediaPropertiesLayout; 11594 } 11595 }); 11596 11597 window.finesse = window.finesse || {}; 11598 window.finesse.restservices = window.finesse.restservices || {}; 11599 window.finesse.restservices.UserMediaPropertiesLayouts = UserMediaPropertiesLayouts; 11600 11601 return UserMediaPropertiesLayouts; 11602 }); 11603 11604 /** 11605 * JavaScript representation of the Finesse User object 11606 * 11607 * @requires finesse.clientservices.ClientServices 11608 * @requires Class 11609 * @requires finesse.FinesseBase 11610 * @requires finesse.restservices.RestBase 11611 */ 11612 11613 /** @private */ 11614 define('restservices/User',[ 11615 'restservices/RestBase', 11616 'restservices/Dialogs', 11617 'restservices/ClientLog', 11618 'restservices/Queues', 11619 'restservices/WrapUpReasons', 11620 'restservices/PhoneBooks', 11621 'restservices/Workflows', 11622 'restservices/UserMediaPropertiesLayout', 11623 'restservices/UserMediaPropertiesLayouts', 11624 'utilities/Utilities' 11625 ], 11626 function (RestBase, Dialogs, ClientLog, Queues, WrapUpReasons, PhoneBooks, Workflows, UserMediaPropertiesLayout, UserMediaPropertiesLayouts, Utilities) { 11627 11628 var User = RestBase.extend(/** @lends finesse.restservices.User.prototype */{ 11629 11630 _dialogs : null, 11631 _clientLogObj : null, 11632 _wrapUpReasons : null, 11633 _phoneBooks : null, 11634 _workflows : null, 11635 _mediaPropertiesLayout : null, 11636 _mediaPropertiesLayouts : null, 11637 _queues : null, 11638 11639 /** 11640 * @class 11641 * The User represents a Finesse Agent or Supervisor. 11642 * 11643 * @param {Object} options 11644 * An object with the following properties:<ul> 11645 * <li><b>id:</b> The id of the object being constructed</li> 11646 * <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li> 11647 * <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li> 11648 * <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li> 11649 * <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li> 11650 * <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul> 11651 * <li><b>status:</b> {Number} The HTTP status code returned</li> 11652 * <li><b>content:</b> {String} Raw string of response</li> 11653 * <li><b>object:</b> {Object} Parsed object of response</li> 11654 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 11655 * <li><b>errorType:</b> {String} Type of error that was caught</li> 11656 * <li><b>errorMessage:</b> {String} Message associated with error</li> 11657 * </ul></li> 11658 * </ul></li> 11659 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 11660 * @augments finesse.restservices.RestBase 11661 * @constructs 11662 * @example 11663 * _user = new finesse.restservices.User({ 11664 * id: _id, 11665 * onLoad : _handleUserLoad, 11666 * onChange : _handleUserChange 11667 * }); 11668 **/ 11669 init: function (options) { 11670 this._super(options); 11671 }, 11672 11673 Callbacks: {}, 11674 11675 /** 11676 * @private 11677 * Gets the REST class for the current object - this is the User object. 11678 */ 11679 getRestClass: function () { 11680 return User; 11681 }, 11682 11683 /** 11684 * @private 11685 * Gets the REST type for the current object - this is a "User". 11686 */ 11687 getRestType: function () { 11688 return "User"; 11689 }, 11690 /** 11691 * @private 11692 * overloading this to return URI 11693 */ 11694 getXMPPNodePath: function () { 11695 return this.getRestUrl(); 11696 }, 11697 /** 11698 * @private 11699 * Returns whether this object supports subscriptions 11700 */ 11701 supportsSubscriptions: function () { 11702 return true; 11703 }, 11704 11705 /** 11706 * Getter for the firstName of this User. 11707 * @returns {String} 11708 * The firstName for this User 11709 */ 11710 getFirstName: function () { 11711 this.isLoaded(); 11712 return Utilities.convertNullToEmptyString(this.getData().firstName); 11713 }, 11714 11715 /** 11716 * Getter for the lastName of this User. 11717 * @returns {String} 11718 * The lastName for this User 11719 */ 11720 getLastName: function () { 11721 this.isLoaded(); 11722 return Utilities.convertNullToEmptyString(this.getData().lastName); 11723 }, 11724 11725 /** 11726 * Getter for the extension of this User. 11727 * @returns {String} 11728 * The extension, if any, of this User 11729 */ 11730 getExtension: function () { 11731 this.isLoaded(); 11732 return Utilities.convertNullToEmptyString(this.getData().extension); 11733 }, 11734 11735 /** 11736 * Getter for the id of the Team of this User 11737 * @returns {String} 11738 * The current (or last fetched) id of the Team of this User 11739 */ 11740 getTeamId: function () { 11741 this.isLoaded(); 11742 return this.getData().teamId; 11743 }, 11744 11745 /** 11746 * Getter for the name of the Team of this User 11747 * @returns {String} 11748 * The current (or last fetched) name of the Team of this User 11749 */ 11750 getTeamName: function () { 11751 this.isLoaded(); 11752 return this.getData().teamName; 11753 }, 11754 11755 /** 11756 * Is user an agent? 11757 * @returns {Boolean} True if user has role of agent, else false. 11758 */ 11759 hasAgentRole: function () { 11760 this.isLoaded(); 11761 return this.hasRole("Agent"); 11762 }, 11763 11764 /** 11765 * Is user a supervisor? 11766 * @returns {Boolean} True if user has role of supervisor, else false. 11767 */ 11768 hasSupervisorRole: function () { 11769 this.isLoaded(); 11770 return this.hasRole("Supervisor"); 11771 }, 11772 11773 /** 11774 * @private 11775 * Checks to see if user has "theRole" 11776 * @returns {Boolean} 11777 */ 11778 hasRole: function (theRole) { 11779 this.isLoaded(); 11780 var result = false, i, roles, len; 11781 11782 roles = this.getData().roles.role; 11783 len = roles.length; 11784 if (typeof roles === 'string') { 11785 if (roles === theRole) { 11786 result = true; 11787 } 11788 } else { 11789 for (i = 0; i < len ; i = i + 1) { 11790 if (roles[i] === theRole) { 11791 result = true; 11792 break; 11793 } 11794 } 11795 } 11796 11797 return result; 11798 }, 11799 11800 /** 11801 * Getter for the pending state of this User. 11802 * @returns {String} 11803 * The pending state of this User 11804 * @see finesse.restservices.User.States 11805 */ 11806 getPendingState: function () { 11807 this.isLoaded(); 11808 return Utilities.convertNullToEmptyString(this.getData().pendingState); 11809 }, 11810 11811 /** 11812 * Getter for the state of this User. 11813 * @returns {String} 11814 * The current (or last fetched) state of this User 11815 * @see finesse.restservices.User.States 11816 */ 11817 getState: function () { 11818 this.isLoaded(); 11819 return this.getData().state; 11820 }, 11821 11822 /** 11823 * Getter for the state change time of this User. 11824 * @returns {String} 11825 * The state change time of this User 11826 */ 11827 getStateChangeTime: function () { 11828 this.isLoaded(); 11829 return this.getData().stateChangeTime; 11830 }, 11831 11832 /** 11833 * Getter for the wrap-up mode of this User. 11834 * @see finesse.restservices.User.WrapUpMode 11835 * @returns {String} The wrap-up mode of this user that is a value of {@link finesse.restservices.User.WrapUpMode} 11836 */ 11837 getWrapUpOnIncoming: function () { 11838 this.isLoaded(); 11839 return this.getData().settings.wrapUpOnIncoming; 11840 }, 11841 11842 /** 11843 * Is User required to go into wrap-up? 11844 * @see finesse.restservices.User.WrapUpMode 11845 * @return {Boolean} 11846 * True if this agent is required to go into wrap-up. 11847 */ 11848 isWrapUpRequired: function () { 11849 return (this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED || 11850 this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED_WITH_WRAP_UP_DATA); 11851 }, 11852 11853 /** 11854 * Checks to see if the user is considered a mobile agent by checking for 11855 * the existence of the mobileAgent node. 11856 * @returns {Boolean} 11857 * True if this agent is a mobile agent. 11858 */ 11859 isMobileAgent: function () { 11860 this.isLoaded(); 11861 var ma = this.getData().mobileAgent; 11862 return ma !== null && typeof ma === "object"; 11863 }, 11864 11865 /** 11866 * Getter for the mobile agent work mode. 11867 * @returns {finesse.restservices.User.WorkMode} 11868 * If available, return the mobile agent work mode, otherwise null. 11869 */ 11870 getMobileAgentMode: function () { 11871 this.isLoaded(); 11872 if (this.isMobileAgent()) { 11873 return this.getData().mobileAgent.mode; 11874 } 11875 return null; 11876 }, 11877 11878 /** 11879 * Getter for the mobile agent dial number. 11880 * @returns {String} 11881 * If available, return the mobile agent dial number, otherwise null. 11882 */ 11883 getMobileAgentDialNumber: function () { 11884 this.isLoaded(); 11885 if (this.isMobileAgent()) { 11886 return this.getData().mobileAgent.dialNumber; 11887 } 11888 return null; 11889 }, 11890 11891 /** 11892 * Getter for a Dialogs collection object that is associated with User. 11893 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11894 * applicable when Object has not been previously created). 11895 * @returns {finesse.restservices.Dialogs} 11896 * A Dialogs collection object. 11897 */ 11898 getDialogs: function (callbacks) { 11899 var options = callbacks || {}; 11900 options.parentObj = this; 11901 this.isLoaded(); 11902 11903 if (this._dialogs === null) { 11904 this._dialogs = new Dialogs(options); 11905 } 11906 11907 return this._dialogs; 11908 }, 11909 11910 /** 11911 * @private 11912 * Getter for a ClientLog object that is associated with User. 11913 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11914 * applicable when Object has not been previously created). 11915 * @returns {finesse.restservices.ClientLog} 11916 * A ClientLog collection object. 11917 */ 11918 getClientLog: function (callbacks) { 11919 var options = callbacks || {}; 11920 options.parentObj = this; 11921 this.isLoaded(); 11922 11923 if (this._clientLogObj === null) { 11924 this._clientLogObj = new ClientLog(options); 11925 } 11926 else { 11927 if(options.onLoad && typeof options.onLoad === "function") { 11928 options.onLoad(this._clientLogObj); 11929 } 11930 } 11931 return this._clientLogObj; 11932 }, 11933 11934 /** 11935 * Getter for a Queues collection object that is associated with User. 11936 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11937 * applicable when Object has not been previously created). 11938 * @returns {finesse.restservices.Queues} 11939 * A Queues collection object. 11940 */ 11941 getQueues: function (callbacks) { 11942 var options = callbacks || {}; 11943 options.parentObj = this; 11944 this.isLoaded(); 11945 11946 if (this._queues === null) { 11947 this._queues = new Queues(options); 11948 } 11949 11950 return this._queues; 11951 }, 11952 11953 /** 11954 * Getter for a WrapUpReasons collection object that is associated with User. 11955 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11956 * applicable when Object has not been previously created). 11957 * @returns {finesse.restservices.WrapUpReasons} 11958 * A WrapUpReasons collection object. 11959 */ 11960 getWrapUpReasons: function (callbacks) { 11961 var options = callbacks || {}; 11962 options.parentObj = this; 11963 this.isLoaded(); 11964 11965 if (this._wrapUpReasons === null) { 11966 this._wrapUpReasons = new WrapUpReasons(options); 11967 } 11968 11969 return this._wrapUpReasons; 11970 }, 11971 11972 /** 11973 * Getter for a PhoneBooks collection object that is associated with User. 11974 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11975 * applicable when Object has not been previously created). 11976 * @returns {finesse.restservices.PhoneBooks} 11977 * A PhoneBooks collection object. 11978 */ 11979 getPhoneBooks: function (callbacks) { 11980 var options = callbacks || {}; 11981 options.parentObj = this; 11982 this.isLoaded(); 11983 11984 if (this._phoneBooks === null) { 11985 this._phoneBooks = new PhoneBooks(options); 11986 } 11987 11988 return this._phoneBooks; 11989 }, 11990 11991 /** 11992 * @private 11993 * Loads the Workflows collection object that is associated with User and 11994 * 'returns' them to the caller via the handlers. 11995 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 11996 * applicable when Object has not been previously created). 11997 * @see finesse.restservices.Workflow 11998 * @see finesse.restservices.Workflows 11999 * @see finesse.restservices.RestCollectionBase 12000 */ 12001 loadWorkflows: function (callbacks) { 12002 var options = callbacks || {}; 12003 options.parentObj = this; 12004 this.isLoaded(); 12005 12006 if (this._workflows === null) { 12007 this._workflows = new Workflows(options); 12008 } else { 12009 this._workflows.refresh(); 12010 } 12011 12012 }, 12013 12014 /** 12015 * Getter for a UserMediaPropertiesLayout object that is associated with User. 12016 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 12017 * applicable when Object has not been previously created). 12018 * @returns {finesse.restservices.UserMediaPropertiesLayout} 12019 * The UserMediaPropertiesLayout object associated with this user 12020 */ 12021 getMediaPropertiesLayout: function (callbacks) { 12022 var options = callbacks || {}; 12023 options.parentObj = this; 12024 options.id = this._id; 12025 12026 this.isLoaded(); 12027 if (this._mediaPropertiesLayout === null) { 12028 this._mediaPropertiesLayout = new UserMediaPropertiesLayout(options); 12029 } 12030 return this._mediaPropertiesLayout; 12031 }, 12032 12033 /** 12034 * Getter for a UserMediaPropertiesLayouts object that is associated with User. 12035 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 12036 * applicable when Object has not been previously created). 12037 * @returns {finesse.restservices.UserMediaPropertiesLayout} 12038 * The UserMediaPropertiesLayout object associated with this user 12039 */ 12040 getMediaPropertiesLayouts: function (callbacks) { 12041 var options = callbacks || {}; 12042 options.parentObj = this; 12043 12044 this.isLoaded(); 12045 if (this._mediaPropertiesLayouts === null) { 12046 this._mediaPropertiesLayouts = new UserMediaPropertiesLayouts(options); 12047 } 12048 return this._mediaPropertiesLayouts; 12049 }, 12050 12051 /** 12052 * Getter for the supervised Teams this User (Supervisor) supervises, if any. 12053 * @see finesse.restservices.Team 12054 * @returns {Array} 12055 * An array of Teams supervised by this User (Supervisor) 12056 */ 12057 getSupervisedTeams: function () { 12058 this.isLoaded(); 12059 12060 try { 12061 return Utilities.getArray(this.getData().teams.Team); 12062 } catch (e) { 12063 return []; 12064 } 12065 12066 }, 12067 12068 /** 12069 * Perform an agent login for this user, associating him with the 12070 * specified extension. 12071 * @param {Object} params 12072 * An object containing properties for agent login. 12073 * @param {String} params.extension 12074 * The extension to associate with this user 12075 * @param {Object} [params.mobileAgent] 12076 * A mobile agent object containing the mode and dial number properties. 12077 * @param {finesse.interfaces.RequestHandlers} params.handlers 12078 * @see finesse.interfaces.RequestHandlers 12079 * @returns {finesse.restservices.User} 12080 * This User object, to allow cascading 12081 * @private 12082 */ 12083 _login: function (params) { 12084 var handlers, contentBody = {}, 12085 restType = this.getRestType(); 12086 12087 // Protect against null dereferencing. 12088 params = params || {}; 12089 12090 contentBody[restType] = { 12091 "state": User.States.LOGIN, 12092 "extension": params.extension 12093 }; 12094 12095 // Create mobile agent node if available. 12096 if (typeof params.mobileAgent === "object") { 12097 contentBody[restType].mobileAgent = { 12098 "mode": params.mobileAgent.mode, 12099 "dialNumber": params.mobileAgent.dialNumber 12100 }; 12101 } 12102 12103 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 12104 handlers = params.handlers || {}; 12105 12106 this.restRequest(this.getRestUrl(), { 12107 method: 'PUT', 12108 success: handlers.success, 12109 error: handlers.error, 12110 content: contentBody 12111 }); 12112 12113 return this; // Allow cascading 12114 }, 12115 12116 /** 12117 * Perform an agent login for this user, associating him with the 12118 * specified extension. 12119 * @param {String} extension 12120 * The extension to associate with this user 12121 * @param {finesse.interfaces.RequestHandlers} handlers 12122 * An object containing the handlers for the request 12123 * @returns {finesse.restservices.User} 12124 * This User object, to allow cascading 12125 */ 12126 login: function (extension, handlers) { 12127 this.isLoaded(); 12128 var params = { 12129 "extension": extension, 12130 "handlers": handlers 12131 }; 12132 return this._login(params); 12133 }, 12134 12135 /** 12136 * Perform an agent login for this user, associating him with the 12137 * specified extension. 12138 * @param {String} extension 12139 * The extension to associate with this user 12140 * @param {String} mode 12141 * The mobile agent work mode as defined in finesse.restservices.User.WorkMode. 12142 * @param {String} extension 12143 * The external dial number desired to be used by the mobile agent. 12144 * @param {finesse.interfaces.RequestHandlers} handlers 12145 * An object containing the handlers for the request 12146 * @returns {finesse.restservices.User} 12147 * This User object, to allow cascading 12148 */ 12149 loginMobileAgent: function (extension, mode, dialNumber, handlers) { 12150 this.isLoaded(); 12151 var params = { 12152 "extension": extension, 12153 "mobileAgent": { 12154 "mode": mode, 12155 "dialNumber": dialNumber 12156 }, 12157 "handlers": handlers 12158 }; 12159 return this._login(params); 12160 }, 12161 12162 /** 12163 * Perform an agent logout for this user. 12164 * @param {String} reasonCode 12165 * The reason this user is logging out. Pass null for no reason. 12166 * @param {finesse.interfaces.RequestHandlers} handlers 12167 * An object containing the handlers for the request 12168 * @returns {finesse.restservices.User} 12169 * This User object, to allow cascading 12170 */ 12171 logout: function (reasonCode, handlers) { 12172 return this.setState("LOGOUT", reasonCode, handlers); 12173 }, 12174 12175 /** 12176 * Set the state of the user. 12177 * @param {String} newState 12178 * The state you are setting 12179 * @param {ReasonCode} reasonCode 12180 * The reason this user is logging out. Pass null for no reason. 12181 * @param {finesse.interfaces.RequestHandlers} handlers 12182 * An object containing the handlers for the request 12183 * @see finesse.restservices.User.States 12184 * @returns {finesse.restservices.User} 12185 * This User object, to allow cascading 12186 */ 12187 setState: function (newState, reasonCode, handlers) { 12188 this.isLoaded(); 12189 12190 var options, contentBody = {}; 12191 12192 if (!reasonCode) { 12193 contentBody[this.getRestType()] = { 12194 "state": newState 12195 }; 12196 } else { 12197 contentBody[this.getRestType()] = { 12198 "state": newState, 12199 "reasonCodeId": reasonCode.id 12200 }; 12201 } 12202 12203 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 12204 handlers = handlers || {}; 12205 12206 options = { 12207 method: 'PUT', 12208 success: handlers.success, 12209 error: handlers.error, 12210 content: contentBody 12211 }; 12212 12213 // After removing the selective 202 handling, we should be able to just use restRequest 12214 this.restRequest(this.getRestUrl(), options); 12215 12216 return this; // Allow cascading 12217 }, 12218 12219 /** 12220 * Make call to a particular phone number. 12221 * 12222 * @param {String} 12223 * The number to call 12224 * @param {finesse.interfaces.RequestHandlers} handlers 12225 * An object containing the handlers for the request 12226 * @returns {finesse.restservices.User} 12227 * This User object, to allow cascading 12228 */ 12229 makeCall: function (number, handlers) { 12230 this.isLoaded(); 12231 12232 this.getDialogs().createNewCallDialog(number, this.getExtension(), handlers); 12233 12234 return this; // Allow cascading 12235 }, 12236 12237 /** 12238 * Make a silent monitor call to a particular agent's phone number. 12239 * 12240 * @param {String} 12241 * The number to call 12242 * @param {finesse.interfaces.RequestHandlers} handlers 12243 * An object containing the handlers for the request 12244 * @returns {finesse.restservices.User} 12245 * This User object, to allow cascading 12246 */ 12247 makeSMCall: function (number, handlers) { 12248 this.isLoaded(); 12249 12250 var actionType = "SILENT_MONITOR"; 12251 12252 this.getDialogs().createNewSuperviseCallDialog(number, this.getExtension(), actionType, handlers); 12253 12254 return this; // Allow cascading 12255 }, 12256 12257 12258 /** 12259 * Make a silent monitor call to a particular agent's phone number. 12260 * 12261 * @param {String} 12262 * The number to call 12263 * @param {String} dialogUri 12264 * The associated dialog uri of SUPERVISOR_MONITOR call 12265 * @param {finesse.interfaces.RequestHandlers} handlers 12266 * An object containing the handlers for the request 12267 * @see finesse.restservices.dialog 12268 * @returns {finesse.restservices.User} 12269 * This User object, to allow cascading 12270 */ 12271 makeBargeCall:function (number, dialogURI, handlers) { 12272 this.isLoaded(); 12273 var actionType = "BARGE_CALL"; 12274 this.getDialogs().createNewBargeCall( this.getExtension(), number, actionType, dialogURI,handlers); 12275 12276 return this; // Allow cascading 12277 }, 12278 12279 /** 12280 * Returns true if the user's current state will result in a pending state change. A pending state 12281 * change is a request to change state that does not result in an immediate state change. For 12282 * example if an agent attempts to change to the NOT_READY state while in the TALKING state, the 12283 * agent will not change state until the call ends. 12284 * 12285 * The current set of states that result in pending state changes is as follows: 12286 * TALKING 12287 * HOLD 12288 * RESERVED_OUTBOUND_PREVIEW 12289 * @returns {Boolean} True if there is a pending state change. 12290 * @see finesse.restservices.User.States 12291 */ 12292 isPendingStateChange: function () { 12293 var state = this.getState(); 12294 return state && ((state === User.States.TALKING) || (state === User.States.HOLD) || (state === User.States.RESERVED_OUTBOUND_PREVIEW)); 12295 }, 12296 12297 /** 12298 * Returns true if the user's current state is WORK or WORK_READY. This is used so 12299 * that a pending state is not cleared when moving into wrap up (work) mode. 12300 * Note that we don't add this as a pending state, since changes while in wrap up 12301 * occur immediately (and we don't want any "pending state" to flash on screen. 12302 * 12303 * @see finesse.restservices.User.States 12304 * @returns {Boolean} True if user is in wrap-up mode. 12305 */ 12306 isWrapUp: function () { 12307 var state = this.getState(); 12308 return state && ((state === User.States.WORK) || (state === User.States.WORK_READY)); 12309 }, 12310 12311 /** 12312 * @private 12313 * Parses a uriString to retrieve the id portion 12314 * @param {String} uriString 12315 * @return {String} id 12316 */ 12317 _parseIdFromUriString : function (uriString) { 12318 return Utilities.getId(uriString); 12319 }, 12320 12321 /** 12322 * Gets the user's Reason Code label. 12323 * Works for both Not Ready and Logout reason codes 12324 * @return {String} the reason code label, or empty string if none 12325 */ 12326 getReasonCodeLabel : function () { 12327 this.isLoaded(); 12328 12329 if (this.getData().reasonCode) { 12330 return this.getData().reasonCode.label; 12331 } else { 12332 return ""; 12333 } 12334 }, 12335 12336 /** 12337 * Gets the user's Not Ready reason code. 12338 * @return {String} Reason Code Id, or undefined if not set or indeterminate 12339 */ 12340 getNotReadyReasonCodeId : function () { 12341 this.isLoaded(); 12342 12343 var reasoncodeIdResult, finesseServerReasonCodeId; 12344 finesseServerReasonCodeId = this.getData().reasonCodeId; 12345 12346 //FinesseServer will give "-l" => we will set to undefined (for convenience) 12347 if (finesseServerReasonCodeId !== "-1") { 12348 reasoncodeIdResult = finesseServerReasonCodeId; 12349 } 12350 12351 return reasoncodeIdResult; 12352 }, 12353 12354 /** 12355 * Performs a GET against the Finesse server looking up the reasonCodeId specified. 12356 * Note that there is no return value; use the success handler to process a 12357 * valid return. 12358 * @param {finesse.interfaces.RequestHandlers} handlers 12359 * An object containing the handlers for the request 12360 * @param {String} reasonCodeId The id for the reason code to lookup 12361 * 12362 */ 12363 getReasonCodeById : function (handlers, reasonCodeId) 12364 { 12365 var self = this, contentBody, reasonCode, url; 12366 contentBody = {}; 12367 12368 url = this.getRestUrl() + "/ReasonCode/" + reasonCodeId; 12369 this.restRequest(url, { 12370 method: 'GET', 12371 success: function (rsp) { 12372 reasonCode = { 12373 uri: rsp.object.ReasonCode.uri, 12374 label: rsp.object.ReasonCode.label, 12375 id: self._parseIdFromUriString(rsp.object.ReasonCode.uri) 12376 }; 12377 handlers.success(reasonCode); 12378 }, 12379 error: function (rsp) { 12380 handlers.error(rsp); 12381 }, 12382 content: contentBody 12383 }); 12384 }, 12385 12386 /** 12387 * Performs a GET against Finesse server retrieving all the specified type of reason codes. 12388 * @param {String} type (LOGOUT or NOT_READY) 12389 * @param {finesse.interfaces.RequestHandlers} handlers 12390 * An object containing the handlers for the request 12391 */ 12392 _getReasonCodesByType : function (type, handlers) 12393 { 12394 var self = this, contentBody = {}, url, reasonCodes, i, reasonCodeArray; 12395 12396 url = this.getRestUrl() + "/ReasonCodes?category=" + type; 12397 this.restRequest(url, { 12398 method: 'GET', 12399 success: function (rsp) { 12400 reasonCodes = []; 12401 12402 reasonCodeArray = rsp.object.ReasonCodes.ReasonCode; 12403 if (reasonCodeArray === undefined) { 12404 reasonCodes = undefined; 12405 } else if (reasonCodeArray[0] !== undefined) { 12406 for (i = 0; i < reasonCodeArray.length; i = i + 1) { 12407 reasonCodes[i] = { 12408 label: rsp.object.ReasonCodes.ReasonCode[i].label, 12409 id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode[i].uri) 12410 }; 12411 } 12412 } else { 12413 reasonCodes[0] = { 12414 label: rsp.object.ReasonCodes.ReasonCode.label, 12415 id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode.uri) 12416 }; 12417 } 12418 handlers.success(reasonCodes); 12419 }, 12420 error: function (rsp) { 12421 handlers.error(rsp); 12422 }, 12423 content: contentBody 12424 }); 12425 }, 12426 12427 /** 12428 * Performs a GET against Finesse server retrieving all the Signout reason codes. 12429 * Note that there is no return value; use the success handler to process a 12430 * valid return. 12431 * @param {finesse.interfaces.RequestHandlers} handlers 12432 * An object containing the handlers for the request 12433 */ 12434 getSignoutReasonCodes : function (handlers) 12435 { 12436 this._getReasonCodesByType("LOGOUT", handlers); 12437 }, 12438 12439 /** 12440 * Performs a GET against Finesse server retrieving all the Not Ready reason codes. 12441 * Note that there is no return value; use the success handler to process a 12442 * valid return. 12443 * @param {finesse.interfaces.RequestHandlers} handlers 12444 * An object containing the handlers for the request 12445 */ 12446 getNotReadyReasonCodes : function (handlers) 12447 { 12448 this._getReasonCodesByType("NOT_READY", handlers); 12449 } 12450 }); 12451 12452 User.States = /** @lends finesse.restservices.User.States.prototype */ { 12453 /** 12454 * User Login. Note that while this is an action, is not technically a state, since a 12455 * logged-in User will always be in a specific state (READY, NOT_READY, TALKING, etc.). 12456 */ 12457 LOGIN: "LOGIN", 12458 /** 12459 * User is logged out. 12460 */ 12461 LOGOUT: "LOGOUT", 12462 /** 12463 * User is not ready. Note that in UCCX implementations, the user is in this state while on a non-routed call. 12464 */ 12465 NOT_READY: "NOT_READY", 12466 /** 12467 * User is ready for calls. 12468 */ 12469 READY: "READY", 12470 /** 12471 * User has a call coming in, but has not answered it. 12472 */ 12473 RESERVED: "RESERVED", 12474 /** 12475 * User has an outbound call being made, but has not been connected to it. 12476 */ 12477 RESERVED_OUTBOUND: "RESERVED_OUTBOUND", 12478 /** 12479 * User has an outbound call's preview information being displayed, but has not acted on it. 12480 */ 12481 RESERVED_OUTBOUND_PREVIEW: "RESERVED_OUTBOUND_PREVIEW", 12482 /** 12483 * User is on a call. Note that in UCCX implementations, this is for routed calls only. 12484 */ 12485 TALKING: "TALKING", 12486 /** 12487 * User is on hold. Note that in UCCX implementations, the user remains in TALKING state while on hold. 12488 */ 12489 HOLD: "HOLD", 12490 /** 12491 * User is wrap-up/work mode. This mode is typically configured to time out, after which the user becomes NOT_READY. 12492 */ 12493 WORK: "WORK", 12494 /** 12495 * This is the same as WORK, except that after time out user becomes READY. 12496 */ 12497 WORK_READY: "WORK_READY", 12498 /** 12499 * @class Possible User state values. 12500 * @constructs 12501 */ 12502 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 12503 12504 }; 12505 12506 User.WorkMode = { /** @lends finesse.restservices.User.WorkMode.prototype */ 12507 /** 12508 * Mobile agent is connected (dialed) for each incoming call received. 12509 */ 12510 CALL_BY_CALL: "CALL_BY_CALL", 12511 /** 12512 * Mobile agent is connected (dialed) at login. 12513 */ 12514 NAILED_CONNECTION: "NAILED_CONNECTION", 12515 /** 12516 * @class Possible Mobile Agent Work Mode Types. 12517 * @constructs 12518 */ 12519 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 12520 12521 }; 12522 12523 User.WrapUpMode = { /** @lends finesse.restservices.User.WrapUpMode.prototype */ 12524 /** 12525 * Agent must go into wrap-up when call ends 12526 */ 12527 REQUIRED: "REQUIRED", 12528 /** 12529 * Agent must go into wrap-up when call ends and must enter wrap-up data 12530 */ 12531 REQUIRED_WITH_WRAP_UP_DATA: "REQUIRED_WITH_WRAP_UP_DATA", 12532 /** 12533 * Agent can choose to go into wrap-up on a call-by-call basis when the call ends 12534 */ 12535 OPTIONAL: "OPTIONAL", 12536 /** 12537 * Agent is not allowed to go into wrap-up when call ends. 12538 */ 12539 NOT_ALLOWED: "NOT_ALLOWED", 12540 /** 12541 * @class Possible Wrap-up Mode Types. 12542 * @constructs 12543 */ 12544 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 12545 12546 }; 12547 12548 window.finesse = window.finesse || {}; 12549 window.finesse.restservices = window.finesse.restservices || {}; 12550 window.finesse.restservices.User = User; 12551 12552 return User; 12553 }); 12554 12555 /** 12556 * JavaScript representation of the Finesse Users collection 12557 * object which contains a list of Users objects. 12558 * 12559 * @requires finesse.clientservices.ClientServices 12560 * @requires Class 12561 * @requires finesse.FinesseBase 12562 * @requires finesse.restservices.RestBase 12563 * @requires finesse.restservices.RestCollectionBase 12564 * @requires finesse.restservices.User 12565 */ 12566 12567 /** @private */ 12568 define('restservices/Users',[ 12569 'restservices/RestCollectionBase', 12570 'restservices/RestBase', 12571 'restservices/User' 12572 ], 12573 function (RestCollectionBase, RestBase, User) { 12574 12575 var Users = RestCollectionBase.extend(/** @lends finesse.restservices.Users.prototype */{ 12576 12577 /** 12578 * @class 12579 * JavaScript representation of a Users collection object. 12580 * While there is no method provided to retrieve all Users, this collection is 12581 * used to return the Users in a supervised Team. 12582 * @augments finesse.restservices.RestCollectionBase 12583 * @constructs 12584 * @see finesse.restservices.Team 12585 * @see finesse.restservices.User 12586 * @see finesse.restservices.User#getSupervisedTeams 12587 * @example 12588 * // Note: The following method gets an Array of Teams, not a Collection. 12589 * _teams = _user.getSupervisedTeams(); 12590 * if (_teams.length > 0) { 12591 * _team0Users = _teams[0].getUsers(); 12592 * } 12593 */ 12594 _fakeConstuctor: function () { 12595 /* This is here to hide the real init constructor from the public docs */ 12596 }, 12597 12598 /** 12599 * @private 12600 * JavaScript representation of the Finesse Users collection 12601 * object which contains a list of Users objects. 12602 * 12603 * @param {Object} options 12604 * An object with the following properties:<ul> 12605 * <li><b>id:</b> The id of the object being constructed</li> 12606 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 12607 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 12608 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 12609 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 12610 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 12611 * <li><b>status:</b> {Number} The HTTP status code returned</li> 12612 * <li><b>content:</b> {String} Raw string of response</li> 12613 * <li><b>object:</b> {Object} Parsed object of response</li> 12614 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 12615 * <li><b>errorType:</b> {String} Type of error that was caught</li> 12616 * <li><b>errorMessage:</b> {String} Message associated with error</li> 12617 * </ul></li> 12618 * </ul></li> 12619 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 12620 **/ 12621 init: function (options) { 12622 this._super(options); 12623 }, 12624 12625 /** 12626 * @private 12627 * Gets the REST class for the current object - this is the Users class. 12628 */ 12629 getRestClass: function () { 12630 return Users; 12631 }, 12632 12633 /** 12634 * @private 12635 * Gets the REST class for the objects that make up the collection. - this 12636 * is the User class. 12637 */ 12638 getRestItemClass: function () { 12639 return User; 12640 }, 12641 12642 /** 12643 * @private 12644 * Gets the REST type for the current object - this is a "Users". 12645 */ 12646 getRestType: function () { 12647 return "Users"; 12648 }, 12649 12650 /** 12651 * @private 12652 * Gets the REST type for the objects that make up the collection - this is "User". 12653 */ 12654 getRestItemType: function () { 12655 return "User"; 12656 }, 12657 12658 /** 12659 * @private 12660 * Gets the node path for the current object - this is the team Users node 12661 * @returns {String} The node path 12662 */ 12663 getXMPPNodePath: function () { 12664 return this.getRestUrl(); 12665 }, 12666 12667 /** 12668 * @private 12669 * Overloading _doGET to reroute the GET to /Team/id from /Team/id/Users 12670 * This needs to be done because the GET /Team/id/Users API is missing 12671 * @returns {Users} This Users (collection) object to allow cascading 12672 */ 12673 _doGET: function (handlers) { 12674 var _this = this; 12675 handlers = handlers || {}; 12676 // Only do this for /Team/id/Users 12677 if (this._restObj && this._restObj.getRestType() === "Team") { 12678 this._restObj._doGET({ 12679 success: function (rspObj) { 12680 // Making sure the response was a valid Team 12681 if (_this._restObj._validate(rspObj.object)) { 12682 // Shimmying the response to look like a Users collection by extracting it from the Team response 12683 rspObj.object[_this.getRestType()] = rspObj.object[_this._restObj.getRestType()][_this.getRestType().toLowerCase()]; 12684 handlers.success(rspObj); 12685 } else { 12686 handlers.error(rspObj); 12687 } 12688 }, 12689 error: handlers.error 12690 }); 12691 return this; // Allow cascading 12692 } else { 12693 return this._super(handlers); 12694 } 12695 }, 12696 12697 /** 12698 * @private 12699 * Override default to indicates that the collection doesn't support making 12700 * requests. 12701 */ 12702 supportsRequests: false, 12703 12704 /** 12705 * @private 12706 * Indicates that this collection handles the subscription for its items 12707 */ 12708 handlesItemSubscription: true, 12709 12710 /** 12711 * @private 12712 * Override default to indicate that we need to subscribe explicitly 12713 */ 12714 explicitSubscription: true 12715 12716 }); 12717 12718 window.finesse = window.finesse || {}; 12719 window.finesse.restservices = window.finesse.restservices || {}; 12720 window.finesse.restservices.Users = Users; 12721 12722 return Users; 12723 }); 12724 12725 /** 12726 * JavaScript representation of the Finesse Team Not Ready Reason Code Assignment object. 12727 * 12728 * @requires finesse.clientservices.ClientServices 12729 * @requires Class 12730 * @requires finesse.FinesseBase 12731 * @requires finesse.restservices.RestBase 12732 */ 12733 12734 /** @private */ 12735 define('restservices/TeamNotReadyReasonCode',['restservices/RestBase'], function (RestBase) { 12736 12737 var TeamNotReadyReasonCode = RestBase.extend( { 12738 12739 /** 12740 * @class 12741 * JavaScript representation of a Team Not Ready ReasonCode object. Also exposes 12742 * methods to operate on the object against the server. 12743 * 12744 * @param {Object} options 12745 * An object with the following properties:<ul> 12746 * <li><b>id:</b> The id of the object being constructed</li> 12747 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 12748 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 12749 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 12750 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 12751 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 12752 * <li><b>status:</b> {Number} The HTTP status code returned</li> 12753 * <li><b>content:</b> {String} Raw string of response</li> 12754 * <li><b>object:</b> {Object} Parsed object of response</li> 12755 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 12756 * <li><b>errorType:</b> {String} Type of error that was caught</li> 12757 * <li><b>errorMessage:</b> {String} Message associated with error</li> 12758 * </ul></li> 12759 * </ul></li> 12760 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 12761 * @constructs 12762 **/ 12763 init: function (options) { 12764 this._super(options); 12765 }, 12766 12767 /** 12768 * @private 12769 * Gets the REST class for the current object - this is the TeamNotReadyReasonCode class. 12770 * @returns {Object} The TeamNotReadyReasonCode class. 12771 */ 12772 getRestClass: function () { 12773 return TeamNotReadyReasonCode; 12774 }, 12775 12776 /** 12777 * @private 12778 * Gets the REST type for the current object - this is a "ReasonCode". 12779 * @returns {String} The ReasonCode string. 12780 */ 12781 getRestType: function () { 12782 return "ReasonCode"; 12783 }, 12784 12785 /** 12786 * @private 12787 * Override default to indicate that this object doesn't support making 12788 * requests. 12789 */ 12790 supportsRequests: false, 12791 12792 /** 12793 * @private 12794 * Override default to indicate that this object doesn't support subscriptions. 12795 */ 12796 supportsSubscriptions: false, 12797 12798 /** 12799 * Getter for the category. 12800 * @returns {String} The category. 12801 */ 12802 getCategory: function () { 12803 this.isLoaded(); 12804 return this.getData().category; 12805 }, 12806 12807 /** 12808 * Getter for the code. 12809 * @returns {String} The code. 12810 */ 12811 getCode: function () { 12812 this.isLoaded(); 12813 return this.getData().code; 12814 }, 12815 12816 /** 12817 * Getter for the label. 12818 * @returns {String} The label. 12819 */ 12820 getLabel: function () { 12821 this.isLoaded(); 12822 return this.getData().label; 12823 }, 12824 12825 /** 12826 * Getter for the forAll value. 12827 * @returns {String} The forAll. 12828 */ 12829 getForAll: function () { 12830 this.isLoaded(); 12831 return this.getData().forAll; 12832 }, 12833 12834 /** 12835 * Getter for the Uri value. 12836 * @returns {String} The Uri. 12837 */ 12838 getUri: function () { 12839 this.isLoaded(); 12840 return this.getData().uri; 12841 } 12842 12843 }); 12844 12845 window.finesse = window.finesse || {}; 12846 window.finesse.restservices = window.finesse.restservices || {}; 12847 window.finesse.restservices.TeamNotReadyReasonCode = TeamNotReadyReasonCode; 12848 12849 return TeamNotReadyReasonCode; 12850 }); 12851 12852 /** 12853 * JavaScript representation of the Finesse TeamNotReadyReasonCodes collection 12854 * object which contains a list of TeamNotReadyReasonCode objects. 12855 * 12856 * @requires finesse.clientservices.ClientServices 12857 * @requires Class 12858 * @requires finesse.FinesseBase 12859 * @requires finesse.restservices.RestBase 12860 * @requires finesse.restservices.Dialog 12861 * @requires finesse.restservices.RestCollectionBase 12862 */ 12863 12864 /** @private */ 12865 define('restservices/TeamNotReadyReasonCodes',[ 12866 'restservices/RestCollectionBase', 12867 'restservices/RestBase', 12868 'restservices/TeamNotReadyReasonCode' 12869 ], 12870 function (RestCollectionBase, RestBase, TeamNotReadyReasonCode) { 12871 12872 var TeamNotReadyReasonCodes = RestCollectionBase.extend( { 12873 12874 /** 12875 * @class 12876 * JavaScript representation of a TeamNotReadyReasonCodes collection object. Also exposes 12877 * methods to operate on the object against the server. 12878 * 12879 * @param {Object} options 12880 * An object with the following properties:<ul> 12881 * <li><b>id:</b> The id of the object being constructed</li> 12882 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 12883 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 12884 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 12885 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 12886 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 12887 * <li><b>status:</b> {Number} The HTTP status code returned</li> 12888 * <li><b>content:</b> {String} Raw string of response</li> 12889 * <li><b>object:</b> {Object} Parsed object of response</li> 12890 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 12891 * <li><b>errorType:</b> {String} Type of error that was caught</li> 12892 * <li><b>errorMessage:</b> {String} Message associated with error</li> 12893 * </ul></li> 12894 * </ul></li> 12895 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 12896 * @augments finesse.restservices.RestCollectionBase 12897 * @constructs 12898 **/ 12899 init: function (options) { 12900 this._super(options); 12901 }, 12902 12903 /** 12904 * @private 12905 * Gets the REST class for the current object - this is the TeamNotReadyReasonCodes class. 12906 */ 12907 getRestClass: function () { 12908 return TeamNotReadyReasonCodes; 12909 }, 12910 12911 /** 12912 * @private 12913 * Gets the REST class for the objects that make up the collection. - this 12914 * is the TeamNotReadyReasonCode class. 12915 */ 12916 getRestItemClass: function () { 12917 return TeamNotReadyReasonCode; 12918 }, 12919 12920 /** 12921 * @private 12922 * Gets the REST type for the current object - this is a "ReasonCodes". 12923 */ 12924 getRestType: function () { 12925 return "ReasonCodes"; 12926 }, 12927 12928 /** 12929 * @private 12930 * Overrides the parent class. Returns the url for the NotReadyReasonCodes resource 12931 */ 12932 getRestUrl: function () { 12933 // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY"); 12934 var restObj = this._restObj, 12935 restUrl = ""; 12936 //Prepend the base REST object if one was provided. 12937 //Otherwise prepend with the default webapp name. 12938 if (restObj instanceof RestBase) { 12939 restUrl += restObj.getRestUrl(); 12940 } 12941 else { 12942 restUrl += "/finesse/api"; 12943 } 12944 //Append the REST type. 12945 restUrl += "/ReasonCodes?category=NOT_READY"; 12946 //Append ID if it is not undefined, null, or empty. 12947 if (this._id) { 12948 restUrl += "/" + this._id; 12949 } 12950 return restUrl; 12951 }, 12952 12953 /** 12954 * @private 12955 * Gets the REST type for the objects that make up the collection - this is "ReasonCode". 12956 */ 12957 getRestItemType: function () { 12958 return "ReasonCode"; 12959 }, 12960 12961 /** 12962 * @private 12963 * Override default to indicates that the collection supports making 12964 * requests. 12965 */ 12966 supportsRequests: true, 12967 12968 /** 12969 * @private 12970 * Override default to indicate that this object doesn't support subscriptions. 12971 */ 12972 supportsRestItemSubscriptions: false, 12973 12974 /** 12975 * @private 12976 * Retrieve the Not Ready Reason Codes. 12977 * 12978 * @returns {TeamNotReadyReasonCodes} 12979 * This TeamNotReadyReasonCodes object to allow cascading. 12980 */ 12981 get: function () { 12982 // set loaded to false so it will rebuild the collection after the get 12983 this._loaded = false; 12984 // reset collection 12985 this._collection = {}; 12986 // perform get 12987 this._synchronize(); 12988 return this; 12989 }, 12990 12991 /** 12992 * @private 12993 * Set up the PutSuccessHandler for TeamNotReadyReasonCodes 12994 * @param {Object} reasonCodes 12995 * @param {String} contentBody 12996 * @param successHandler 12997 * @return {function} 12998 */ 12999 createPutSuccessHandler: function (reasonCodes, contentBody, successHandler) { 13000 return function (rsp) { 13001 // Update internal structure based on response. Here we 13002 // inject the contentBody from the PUT request into the 13003 // rsp.object element to mimic a GET as a way to take 13004 // advantage of the existing _processResponse method. 13005 rsp.object = contentBody; 13006 reasonCodes._processResponse(rsp); 13007 13008 //Remove the injected contentBody object before cascading response 13009 rsp.object = {}; 13010 13011 //cascade response back to consumer's response handler 13012 successHandler(rsp); 13013 }; 13014 }, 13015 13016 /** 13017 * @private 13018 * Perform the REST API PUT call to update the reason code assignments for the team 13019 * @param {string[]} newValues 13020 * @param handlers 13021 */ 13022 update: function (newValues, handlers) { 13023 this.isLoaded(); 13024 var contentBody = {}, contentBodyInner = [], i, innerObject = {}; 13025 13026 contentBody[this.getRestType()] = { 13027 }; 13028 13029 for (i in newValues) { 13030 if (newValues.hasOwnProperty(i)) { 13031 innerObject = { 13032 "uri": newValues[i] 13033 }; 13034 contentBodyInner.push(innerObject); 13035 } 13036 } 13037 13038 contentBody[this.getRestType()] = { 13039 "ReasonCode" : contentBodyInner 13040 }; 13041 13042 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 13043 handlers = handlers || {}; 13044 13045 this.restRequest(this.getRestUrl(), { 13046 method: 'PUT', 13047 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 13048 error: handlers.error, 13049 content: contentBody 13050 }); 13051 13052 return this; // Allow cascading 13053 } 13054 }); 13055 13056 window.finesse = window.finesse || {}; 13057 window.finesse.restservices = window.finesse.restservices || {}; 13058 window.finesse.restservices.TeamNotReadyReasonCodes = TeamNotReadyReasonCodes; 13059 13060 return TeamNotReadyReasonCodes; 13061 }); 13062 13063 /** 13064 * JavaScript representation of the Finesse Team Wrap Up Reason object. 13065 * 13066 * @requires finesse.clientservices.ClientServices 13067 * @requires Class 13068 * @requires finesse.FinesseBase 13069 * @requires finesse.restservices.RestBase 13070 */ 13071 /** @private */ 13072 define('restservices/TeamWrapUpReason',['restservices/RestBase'], function (RestBase) { 13073 13074 var TeamWrapUpReason = RestBase.extend({ 13075 13076 /** 13077 * @class 13078 * JavaScript representation of a TeamWrapUpReason object. Also exposes 13079 * methods to operate on the object against the server. 13080 * 13081 * @param {Object} options 13082 * An object with the following properties:<ul> 13083 * <li><b>id:</b> The id of the object being constructed</li> 13084 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 13085 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 13086 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 13087 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 13088 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 13089 * <li><b>status:</b> {Number} The HTTP status code returned</li> 13090 * <li><b>content:</b> {String} Raw string of response</li> 13091 * <li><b>object:</b> {Object} Parsed object of response</li> 13092 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 13093 * <li><b>errorType:</b> {String} Type of error that was caught</li> 13094 * <li><b>errorMessage:</b> {String} Message associated with error</li> 13095 * </ul></li> 13096 * </ul></li> 13097 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 13098 * @constructs 13099 **/ 13100 init: function (options) { 13101 this._super(options); 13102 }, 13103 13104 /** 13105 * @private 13106 * Gets the REST class for the current object - this is the TeamWrapUpReason class. 13107 * @returns {Object} The TeamWrapUpReason class. 13108 */ 13109 getRestClass: function () { 13110 return TeamWrapUpReason; 13111 }, 13112 13113 /** 13114 * @private 13115 * Gets the REST type for the current object - this is a "WrapUpReason". 13116 * @returns {String} The WrapUpReason string. 13117 */ 13118 getRestType: function () { 13119 return "WrapUpReason"; 13120 }, 13121 13122 /** 13123 * @private 13124 * Override default to indicate that this object doesn't support making 13125 * requests. 13126 */ 13127 supportsRequests: false, 13128 13129 /** 13130 * @private 13131 * Override default to indicate that this object doesn't support subscriptions. 13132 */ 13133 supportsSubscriptions: false, 13134 13135 /** 13136 * Getter for the label. 13137 * @returns {String} The label. 13138 */ 13139 getLabel: function () { 13140 this.isLoaded(); 13141 return this.getData().label; 13142 }, 13143 13144 /** 13145 * @private 13146 * Getter for the forAll value. 13147 * @returns {Boolean} True if global 13148 */ 13149 getForAll: function () { 13150 this.isLoaded(); 13151 return this.getData().forAll; 13152 }, 13153 13154 /** 13155 * @private 13156 * Getter for the Uri value. 13157 * @returns {String} The Uri. 13158 */ 13159 getUri: function () { 13160 this.isLoaded(); 13161 return this.getData().uri; 13162 } 13163 }); 13164 13165 window.finesse = window.finesse || {}; 13166 window.finesse.restservices = window.finesse.restservices || {}; 13167 window.finesse.restservices.TeamWrapUpReason = TeamWrapUpReason; 13168 13169 return TeamWrapUpReason; 13170 }); 13171 13172 /** 13173 * JavaScript representation of the Finesse Team Wrap-Up Reasons collection 13174 * object which contains a list of Wrap-Up Reasons objects. 13175 * 13176 * @requires finesse.clientservices.ClientServices 13177 * @requires Class 13178 * @requires finesse.FinesseBase 13179 * @requires finesse.restservices.RestBase 13180 * @requires finesse.restservices.Dialog 13181 * @requires finesse.restservices.RestCollectionBase 13182 */ 13183 /** @private */ 13184 define('restservices/TeamWrapUpReasons',[ 13185 'restservices/RestCollectionBase', 13186 'restservices/RestBase', 13187 'restservices/TeamWrapUpReason' 13188 ], 13189 function (RestCollectionBase, RestBase, TeamWrapUpReason) { 13190 13191 var TeamWrapUpReasons = RestCollectionBase.extend({ 13192 13193 /** 13194 * @class 13195 * JavaScript representation of a TeamWrapUpReasons collection object. Also exposes 13196 * methods to operate on the object against the server. 13197 * 13198 * @param {Object} options 13199 * An object with the following properties:<ul> 13200 * <li><b>id:</b> The id of the object being constructed</li> 13201 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 13202 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 13203 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 13204 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 13205 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 13206 * <li><b>status:</b> {Number} The HTTP status code returned</li> 13207 * <li><b>content:</b> {String} Raw string of response</li> 13208 * <li><b>object:</b> {Object} Parsed object of response</li> 13209 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 13210 * <li><b>errorType:</b> {String} Type of error that was caught</li> 13211 * <li><b>errorMessage:</b> {String} Message associated with error</li> 13212 * </ul></li> 13213 * </ul></li> 13214 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 13215 * @constructs 13216 **/ 13217 init: function (options) { 13218 this._super(options); 13219 }, 13220 13221 /** 13222 * @private 13223 * Gets the REST class for the current object - this is the TeamWrapUpReasons class. 13224 */ 13225 getRestClass: function () { 13226 return TeamWrapUpReasons; 13227 }, 13228 13229 /** 13230 * @private 13231 * Gets the REST class for the objects that make up the collection. - this 13232 * is the TeamWrapUpReason class. 13233 */ 13234 getRestItemClass: function () { 13235 return TeamWrapUpReason; 13236 }, 13237 13238 /** 13239 * @private 13240 * Gets the REST type for the current object - this is a "WrapUpReasons". 13241 */ 13242 getRestType: function () { 13243 return "WrapUpReasons"; 13244 }, 13245 13246 /** 13247 * @private 13248 * Gets the REST type for the objects that make up the collection - this is "WrapUpReason". 13249 */ 13250 getRestItemType: function () { 13251 return "WrapUpReason"; 13252 }, 13253 13254 /** 13255 * @private 13256 * Override default to indicates that the collection supports making 13257 * requests. 13258 */ 13259 supportsRequests: true, 13260 13261 /** 13262 * @private 13263 * Override default to indicate that this object doesn't support subscriptions. 13264 */ 13265 supportsRestItemSubscriptions: false, 13266 13267 /** 13268 * Retrieve the Team Wrap Up Reasons. 13269 * 13270 * @returns {finesse.restservices.TeamWrapUpReasons} 13271 * This TeamWrapUpReasons object to allow cascading. 13272 */ 13273 get: function () { 13274 // set loaded to false so it will rebuild the collection after the get 13275 this._loaded = false; 13276 // reset collection 13277 this._collection = {}; 13278 // perform get 13279 this._synchronize(); 13280 return this; 13281 }, 13282 13283 /** 13284 * Set up the PutSuccessHandler for TeamWrapUpReasons 13285 * @param {Object} wrapUpReasons 13286 * @param {Object} contentBody 13287 * @param successHandler 13288 * @returns response 13289 */ 13290 createPutSuccessHandler: function (wrapUpReasons, contentBody, successHandler) { 13291 return function (rsp) { 13292 // Update internal structure based on response. Here we 13293 // inject the contentBody from the PUT request into the 13294 // rsp.object element to mimic a GET as a way to take 13295 // advantage of the existing _processResponse method. 13296 rsp.object = contentBody; 13297 13298 wrapUpReasons._processResponse(rsp); 13299 13300 //Remove the injected contentBody object before cascading response 13301 rsp.object = {}; 13302 13303 //cascade response back to consumer's response handler 13304 successHandler(rsp); 13305 }; 13306 }, 13307 13308 /** 13309 * Perform the REST API PUT call to update the reason code assignments for the team 13310 * @param {String Array} newValues 13311 * @param handlers 13312 * @returns {Object} this 13313 */ 13314 update: function (newValues, handlers) { 13315 this.isLoaded(); 13316 var contentBody = {}, contentBodyInner = [], i, innerObject = {}; 13317 13318 contentBody[this.getRestType()] = { 13319 }; 13320 13321 for (i in newValues) { 13322 if (newValues.hasOwnProperty(i)) { 13323 innerObject = { 13324 "uri": newValues[i] 13325 }; 13326 contentBodyInner.push(innerObject); 13327 } 13328 } 13329 13330 contentBody[this.getRestType()] = { 13331 "WrapUpReason" : contentBodyInner 13332 }; 13333 13334 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 13335 handlers = handlers || {}; 13336 13337 this.restRequest(this.getRestUrl(), { 13338 method: 'PUT', 13339 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 13340 error: handlers.error, 13341 content: contentBody 13342 }); 13343 13344 return this; // Allow cascading 13345 } 13346 }); 13347 13348 window.finesse = window.finesse || {}; 13349 window.finesse.restservices = window.finesse.restservices || {}; 13350 window.finesse.restservices.TeamWrapUpReasons = TeamWrapUpReasons; 13351 13352 return TeamWrapUpReasons; 13353 }); 13354 13355 /** 13356 * JavaScript representation of a TeamSignOutReasonCode. 13357 * 13358 * @requires finesse.clientservices.ClientServices 13359 * @requires Class 13360 * @requires finesse.FinesseBase 13361 * @requires finesse.restservices.RestBase 13362 */ 13363 13364 /** @private */ 13365 define('restservices/TeamSignOutReasonCode',['restservices/RestBase'], function (RestBase) { 13366 var TeamSignOutReasonCode = RestBase.extend({ 13367 13368 /** 13369 * @class 13370 * JavaScript representation of a TeamSignOutReasonCode object. Also exposes 13371 * methods to operate on the object against the server. 13372 * 13373 * @param {Object} options 13374 * An object with the following properties:<ul> 13375 * <li><b>id:</b> The id of the object being constructed</li> 13376 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 13377 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 13378 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 13379 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 13380 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 13381 * <li><b>status:</b> {Number} The HTTP status code returned</li> 13382 * <li><b>content:</b> {String} Raw string of response</li> 13383 * <li><b>object:</b> {Object} Parsed object of response</li> 13384 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 13385 * <li><b>errorType:</b> {String} Type of error that was caught</li> 13386 * <li><b>errorMessage:</b> {String} Message associated with error</li> 13387 * </ul></li> 13388 * </ul></li> 13389 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 13390 * @constructs 13391 * @ignore 13392 **/ 13393 init: function (options) { 13394 this._super(options); 13395 }, 13396 13397 /** 13398 * @private 13399 * Gets the REST class for the current object - this is the TeamSignOutReasonCode class. 13400 * @returns {Object} The TeamSignOutReasonCode class. 13401 */ 13402 getRestClass: function () { 13403 return TeamSignOutReasonCode; 13404 }, 13405 13406 /** 13407 * @private 13408 * Gets the REST type for the current object - this is a "ReasonCode". 13409 * @returns {String} The ReasonCode string. 13410 */ 13411 getRestType: function () { 13412 return "ReasonCode"; 13413 }, 13414 13415 /** 13416 * @private 13417 * Override default to indicate that this object doesn't support making 13418 * requests. 13419 */ 13420 supportsRequests: false, 13421 13422 /** 13423 * @private 13424 * Override default to indicate that this object doesn't support subscriptions. 13425 */ 13426 supportsSubscriptions: false, 13427 13428 /** 13429 * Getter for the category. 13430 * @returns {String} The category. 13431 */ 13432 getCategory: function () { 13433 this.isLoaded(); 13434 return this.getData().category; 13435 }, 13436 13437 /** 13438 * Getter for the code. 13439 * @returns {String} The code. 13440 */ 13441 getCode: function () { 13442 this.isLoaded(); 13443 return this.getData().code; 13444 }, 13445 13446 /** 13447 * Getter for the label. 13448 * @returns {String} The label. 13449 */ 13450 getLabel: function () { 13451 this.isLoaded(); 13452 return this.getData().label; 13453 }, 13454 13455 /** 13456 * Getter for the forAll value. 13457 * @returns {String} The forAll. 13458 */ 13459 getForAll: function () { 13460 this.isLoaded(); 13461 return this.getData().forAll; 13462 }, 13463 13464 /** 13465 * Getter for the Uri value. 13466 * @returns {String} The Uri. 13467 */ 13468 getUri: function () { 13469 this.isLoaded(); 13470 return this.getData().uri; 13471 } 13472 13473 }); 13474 13475 window.finesse = window.finesse || {}; 13476 window.finesse.restservices = window.finesse.restservices || {}; 13477 window.finesse.restservices.TeamSignOutReasonCode = TeamSignOutReasonCode; 13478 13479 return TeamSignOutReasonCode; 13480 }); 13481 13482 /** 13483 * JavaScript representation of the TeamSignOutReasonCodes collection 13484 * object which contains a list of TeamSignOutReasonCode objects. 13485 * 13486 * @requires finesse.clientservices.ClientServices 13487 * @requires Class 13488 * @requires finesse.FinesseBase 13489 * @requires finesse.restservices.RestBase 13490 * @requires finesse.restservices.Dialog 13491 * @requires finesse.restservices.RestCollectionBase 13492 */ 13493 13494 /** @private */ 13495 define('restservices/TeamSignOutReasonCodes',[ 13496 'restservices/RestCollectionBase', 13497 'restservices/RestBase', 13498 'restservices/TeamSignOutReasonCode' 13499 ], 13500 function (RestCollectionBase, RestBase, TeamSignOutReasonCode) { 13501 13502 var TeamSignOutReasonCodes = RestCollectionBase.extend({ 13503 /** 13504 * @class 13505 * JavaScript representation of a TeamSignOutReasonCodes collection object. Also exposes 13506 * methods to operate on the object against the server. 13507 * 13508 * @param {Object} options 13509 * An object with the following properties:<ul> 13510 * <li><b>id:</b> The id of the object being constructed</li> 13511 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 13512 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 13513 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 13514 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 13515 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 13516 * <li><b>status:</b> {Number} The HTTP status code returned</li> 13517 * <li><b>content:</b> {String} Raw string of response</li> 13518 * <li><b>object:</b> {Object} Parsed object of response</li> 13519 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 13520 * <li><b>errorType:</b> {String} Type of error that was caught</li> 13521 * <li><b>errorMessage:</b> {String} Message associated with error</li> 13522 * </ul></li> 13523 * </ul></li> 13524 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 13525 * @constructs 13526 **/ 13527 init: function (options) { 13528 this._super(options); 13529 }, 13530 13531 /** 13532 * @private 13533 * Gets the REST class for the current object - this is the TeamSignOutReasonCodes class. 13534 */ 13535 getRestClass: function () { 13536 return TeamSignOutReasonCodes; 13537 }, 13538 13539 /** 13540 * @private 13541 * Gets the REST class for the objects that make up the collection. - this 13542 * is the TeamSignOutReasonCode class. 13543 */ 13544 getRestItemClass: function () { 13545 return TeamSignOutReasonCode; 13546 }, 13547 13548 /** 13549 * @private 13550 * Gets the REST type for the current object - this is a "ReasonCodes". 13551 */ 13552 getRestType: function () { 13553 return "ReasonCodes"; 13554 }, 13555 13556 /** 13557 * Overrides the parent class. Returns the url for the SignOutReasonCodes resource 13558 */ 13559 getRestUrl: function () { 13560 var restObj = this._restObj, restUrl = ""; 13561 13562 //Prepend the base REST object if one was provided. 13563 //Otherwise prepend with the default webapp name. 13564 if (restObj instanceof RestBase) { 13565 restUrl += restObj.getRestUrl(); 13566 } else { 13567 restUrl += "/finesse/api"; 13568 } 13569 //Append the REST type. 13570 restUrl += "/ReasonCodes?category=LOGOUT"; 13571 //Append ID if it is not undefined, null, or empty. 13572 if (this._id) { 13573 restUrl += "/" + this._id; 13574 } 13575 return restUrl; 13576 }, 13577 13578 /** 13579 * @private 13580 * Gets the REST type for the objects that make up the collection - this is "ReasonCode". 13581 */ 13582 getRestItemType: function () { 13583 return "ReasonCode"; 13584 }, 13585 13586 /** 13587 * @private 13588 * Override default to indicates that the collection supports making requests. 13589 */ 13590 supportsRequests: true, 13591 13592 /** 13593 * @private 13594 * Override default to indicates that the collection does not subscribe to its objects. 13595 */ 13596 supportsRestItemSubscriptions: false, 13597 13598 /** 13599 * Retrieve the Sign Out Reason Codes. 13600 * 13601 * @returns {finesse.restservices.TeamSignOutReasonCodes} 13602 * This TeamSignOutReasonCodes object to allow cascading. 13603 */ 13604 get: function () { 13605 // set loaded to false so it will rebuild the collection after the get 13606 this._loaded = false; 13607 // reset collection 13608 this._collection = {}; 13609 // perform get 13610 this._synchronize(); 13611 return this; 13612 }, 13613 13614 /* We only use PUT and GET on Reason Code team assignments 13615 * @param {Object} contact 13616 * @param {Object} contentBody 13617 * @param {Function} successHandler 13618 */ 13619 createPutSuccessHandler: function (contact, contentBody, successHandler) { 13620 return function (rsp) { 13621 // Update internal structure based on response. Here we 13622 // inject the contentBody from the PUT request into the 13623 // rsp.object element to mimic a GET as a way to take 13624 // advantage of the existing _processResponse method. 13625 rsp.object = contentBody; 13626 contact._processResponse(rsp); 13627 13628 //Remove the injected contentBody object before cascading response 13629 rsp.object = {}; 13630 13631 //cascade response back to consumer's response handler 13632 successHandler(rsp); 13633 }; 13634 }, 13635 13636 /** 13637 * Update - This should be all that is needed. 13638 * @param {Object} newValues 13639 * @param {Object} handlers 13640 * @returns {finesse.restservices.TeamSignOutReasonCodes} 13641 * This TeamSignOutReasonCodes object to allow cascading. 13642 */ 13643 update: function (newValues, handlers) { 13644 this.isLoaded(); 13645 var contentBody = {}, contentBodyInner = [], i, innerObject = {}; 13646 13647 contentBody[this.getRestType()] = { 13648 }; 13649 13650 for (i in newValues) { 13651 if (newValues.hasOwnProperty(i)) { 13652 innerObject = { 13653 "uri": newValues[i] 13654 }; 13655 contentBodyInner.push(innerObject); 13656 } 13657 } 13658 13659 contentBody[this.getRestType()] = { 13660 "ReasonCode" : contentBodyInner 13661 }; 13662 13663 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 13664 handlers = handlers || {}; 13665 13666 this.restRequest(this.getRestUrl(), { 13667 method: 'PUT', 13668 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 13669 error: handlers.error, 13670 content: contentBody 13671 }); 13672 13673 return this; // Allow cascading 13674 } 13675 13676 }); 13677 13678 window.finesse = window.finesse || {}; 13679 window.finesse.restservices = window.finesse.restservices || {}; 13680 window.finesse.restservices.TeamSignOutReasonCodes = TeamSignOutReasonCodes; 13681 13682 return TeamSignOutReasonCodes; 13683 }); 13684 13685 /** 13686 * JavaScript representation of the Finesse PhoneBook Assignment object. 13687 * 13688 * @requires finesse.clientservices.ClientServices 13689 * @requires Class 13690 * @requires finesse.FinesseBase 13691 * @requires finesse.restservices.RestBase 13692 */ 13693 13694 /** 13695 * The following comment prevents JSLint errors concerning undefined global variables. 13696 * It tells JSLint that these identifiers are defined elsewhere. 13697 */ 13698 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */ 13699 13700 /** The following comment is to prevent jslint errors about 13701 * using variables before they are defined. 13702 */ 13703 /*global $, jQuery, Handlebars, dojox, dojo, finesse */ 13704 13705 /** @private */ 13706 define('restservices/TeamPhoneBook',['restservices/RestBase'], function (RestBase) { 13707 var TeamPhoneBook = RestBase.extend({ 13708 13709 /** 13710 * @class 13711 * JavaScript representation of a PhoneBook object. Also exposes 13712 * methods to operate on the object against the server. 13713 * 13714 * @param {Object} options 13715 * An object with the following properties:<ul> 13716 * <li><b>id:</b> The id of the object being constructed</li> 13717 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 13718 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 13719 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 13720 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 13721 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 13722 * <li><b>status:</b> {Number} The HTTP status code returned</li> 13723 * <li><b>content:</b> {String} Raw string of response</li> 13724 * <li><b>object:</b> {Object} Parsed object of response</li> 13725 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 13726 * <li><b>errorType:</b> {String} Type of error that was caught</li> 13727 * <li><b>errorMessage:</b> {String} Message associated with error</li> 13728 * </ul></li> 13729 * </ul></li> 13730 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 13731 * @constructs 13732 **/ 13733 init: function (options) { 13734 this._super(options); 13735 }, 13736 13737 /** 13738 * @private 13739 * Gets the REST class for the current object - this is the PhoneBooks class. 13740 * @returns {Object} The PhoneBooks class. 13741 */ 13742 getRestClass: function () { 13743 return TeamPhoneBook; 13744 }, 13745 13746 /** 13747 * @private 13748 * Gets the REST type for the current object - this is a "PhoneBook". 13749 * @returns {String} The PhoneBook string. 13750 */ 13751 getRestType: function () { 13752 return "PhoneBook"; 13753 }, 13754 13755 /** 13756 * @private 13757 * Override default to indicate that this object doesn't support making 13758 * requests. 13759 */ 13760 supportsRequests: false, 13761 13762 /** 13763 * @private 13764 * Override default to indicate that this object doesn't support subscriptions. 13765 */ 13766 supportsSubscriptions: false, 13767 13768 /** 13769 * Getter for the name. 13770 * @returns {String} The name. 13771 */ 13772 getName: function () { 13773 this.isLoaded(); 13774 return this.getData().name; 13775 }, 13776 13777 /** 13778 * Getter for the Uri value. 13779 * @returns {String} The Uri. 13780 */ 13781 getUri: function () { 13782 this.isLoaded(); 13783 return this.getData().uri; 13784 } 13785 13786 }); 13787 13788 window.finesse = window.finesse || {}; 13789 window.finesse.restservices = window.finesse.restservices || {}; 13790 window.finesse.restservices.TeamPhoneBook = TeamPhoneBook; 13791 13792 return TeamPhoneBook; 13793 }); 13794 13795 /** 13796 * JavaScript representation of the Finesse PhoneBook Assignments collection 13797 * object which contains a list of Not Ready Reason Codes objects. 13798 * 13799 * @requires finesse.clientservices.ClientServices 13800 * @requires Class 13801 * @requires finesse.FinesseBase 13802 * @requires finesse.restservices.RestBase 13803 * @requires finesse.restservices.Dialog 13804 * @requires finesse.restservices.RestCollectionBase 13805 */ 13806 13807 /** 13808 * The following comment prevents JSLint errors concerning undefined global variables. 13809 * It tells JSLint that these identifiers are defined elsewhere. 13810 */ 13811 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */ 13812 13813 /** The following comment is to prevent jslint errors about 13814 * using variables before they are defined. 13815 */ 13816 /*global $, jQuery, Handlebars, dojox, dojo, finesse */ 13817 13818 /** @private */ 13819 define('restservices/TeamPhoneBooks',[ 13820 'restservices/RestCollectionBase', 13821 'restservices/RestBase', 13822 'restservices/TeamPhoneBook' 13823 ], 13824 function (RestCollectionBase, RestBase, TeamPhoneBook) { 13825 var TeamPhoneBooks = RestCollectionBase.extend({ 13826 13827 /** 13828 * @class 13829 * JavaScript representation of a TeamPhoneBooks collection object. Also exposes 13830 * methods to operate on the object against the server. 13831 * 13832 * @param {Object} options 13833 * An object with the following properties:<ul> 13834 * <li><b>id:</b> The id of the object being constructed</li> 13835 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 13836 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 13837 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 13838 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 13839 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 13840 * <li><b>status:</b> {Number} The HTTP status code returned</li> 13841 * <li><b>content:</b> {String} Raw string of response</li> 13842 * <li><b>object:</b> {Object} Parsed object of response</li> 13843 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 13844 * <li><b>errorType:</b> {String} Type of error that was caught</li> 13845 * <li><b>errorMessage:</b> {String} Message associated with error</li> 13846 * </ul></li> 13847 * </ul></li> 13848 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 13849 * @constructs 13850 **/ 13851 init: function (options) { 13852 this._super(options); 13853 }, 13854 13855 /** 13856 * @private 13857 * Gets the REST class for the current object - this is the TeamPhoneBooks class. 13858 */ 13859 getRestClass: function () { 13860 return TeamPhoneBooks; 13861 }, 13862 13863 /** 13864 * @private 13865 * Gets the REST class for the objects that make up the collection. - this 13866 * is the TeamPhoneBooks class. 13867 */ 13868 getRestItemClass: function () { 13869 return TeamPhoneBook; 13870 }, 13871 13872 /** 13873 * @private 13874 * Gets the REST type for the current object - this is a "ReasonCodes". 13875 */ 13876 getRestType: function () { 13877 return "PhoneBooks"; 13878 }, 13879 13880 /** 13881 * Overrides the parent class. Returns the url for the PhoneBooks resource 13882 */ 13883 getRestUrl: function () { 13884 // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY"); 13885 var restObj = this._restObj, 13886 restUrl = ""; 13887 //Prepend the base REST object if one was provided. 13888 if (restObj instanceof RestBase) { 13889 restUrl += restObj.getRestUrl(); 13890 } 13891 //Otherwise prepend with the default webapp name. 13892 else { 13893 restUrl += "/finesse/api"; 13894 } 13895 //Append the REST type. 13896 restUrl += "/PhoneBooks"; 13897 //Append ID if it is not undefined, null, or empty. 13898 if (this._id) { 13899 restUrl += "/" + this._id; 13900 } 13901 return restUrl; 13902 }, 13903 13904 /** 13905 * @private 13906 * Gets the REST type for the objects that make up the collection - this is "ReasonCode". 13907 */ 13908 getRestItemType: function () { 13909 return "PhoneBook"; 13910 }, 13911 13912 /** 13913 * @private 13914 * Override default to indicates that the collection supports making 13915 * requests. 13916 */ 13917 supportsRequests: true, 13918 13919 /** 13920 * @private 13921 * Override default to indicates that the collection subscribes to its objects. 13922 */ 13923 supportsRestItemSubscriptions: false, 13924 13925 /** 13926 * Retrieve the Not Ready Reason Codes. 13927 * 13928 * @returns {finesse.restservices.TeamPhoneBooks} 13929 * This TeamPhoneBooks object to allow cascading. 13930 */ 13931 get: function () { 13932 // set loaded to false so it will rebuild the collection after the get 13933 /** @private */ 13934 this._loaded = false; 13935 // reset collection 13936 /** @private */ 13937 this._collection = {}; 13938 // perform get 13939 this._synchronize(); 13940 return this; 13941 }, 13942 13943 /* We only use PUT and GET on Reason Code team assignments 13944 */ 13945 createPutSuccessHandler: function(contact, contentBody, successHandler){ 13946 return function (rsp) { 13947 // Update internal structure based on response. Here we 13948 // inject the contentBody from the PUT request into the 13949 // rsp.object element to mimic a GET as a way to take 13950 // advantage of the existing _processResponse method. 13951 rsp.object = contentBody; 13952 contact._processResponse(rsp); 13953 13954 //Remove the injected Contact object before cascading response 13955 rsp.object = {}; 13956 13957 //cascade response back to consumer's response handler 13958 successHandler(rsp); 13959 }; 13960 }, 13961 13962 /** 13963 * Update - This should be all that is needed. 13964 */ 13965 update: function (newValues, handlers) { 13966 this.isLoaded(); 13967 var contentBody = {}, contentBodyInner = [], i, innerObject; 13968 13969 contentBody[this.getRestType()] = { 13970 }; 13971 13972 for (i in newValues) { 13973 if (newValues.hasOwnProperty(i)) { 13974 innerObject = {}; 13975 innerObject = { 13976 "uri": newValues[i] 13977 }; 13978 contentBodyInner.push(innerObject); 13979 } 13980 } 13981 13982 contentBody[this.getRestType()] = { 13983 "PhoneBook" : contentBodyInner 13984 }; 13985 13986 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 13987 handlers = handlers || {}; 13988 13989 this.restRequest(this.getRestUrl(), { 13990 method: 'PUT', 13991 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 13992 error: handlers.error, 13993 content: contentBody 13994 }); 13995 13996 return this; // Allow cascading 13997 } 13998 13999 }); 14000 14001 window.finesse = window.finesse || {}; 14002 window.finesse.restservices = window.finesse.restservices || {}; 14003 window.finesse.restservices.TeamPhoneBooks = TeamPhoneBooks; 14004 14005 return TeamPhoneBooks; 14006 }); 14007 14008 /** 14009 * JavaScript representation of the Finesse LayoutConfig object 14010 * @requires ClientServices 14011 * @requires finesse.FinesseBase 14012 * @requires finesse.restservices.RestBase 14013 */ 14014 14015 /** @private */ 14016 define('restservices/LayoutConfig',['restservices/RestBase'], function (RestBase) { 14017 /** @private */ 14018 var LayoutConfig = RestBase.extend({ 14019 14020 /** 14021 * @class 14022 * JavaScript representation of a LayoutConfig object. Also exposes methods to operate 14023 * on the object against the server. 14024 * 14025 * @param {String} id 14026 * Not required... 14027 * @param {Object} callbacks 14028 * An object containing callbacks for instantiation and runtime 14029 * @param {Function} callbacks.onLoad(this) 14030 * Callback to invoke upon successful instantiation 14031 * @param {Function} callbacks.onLoadError(rsp) 14032 * Callback to invoke on instantiation REST request error 14033 * as passed by finesse.clientservices.ClientServices.ajax() 14034 * { 14035 * status: {Number} The HTTP status code returned 14036 * content: {String} Raw string of response 14037 * object: {Object} Parsed object of response 14038 * error: {Object} Wrapped exception that was caught 14039 * error.errorType: {String} Type of error that was caught 14040 * error.errorMessage: {String} Message associated with error 14041 * } 14042 * @param {Function} callbacks.onChange(this) 14043 * Callback to invoke upon successful update 14044 * @param {Function} callbacks.onError(rsp) 14045 * Callback to invoke on update error (refresh or event) 14046 * as passed by finesse.clientservices.ClientServices.ajax() 14047 * { 14048 * status: {Number} The HTTP status code returned 14049 * content: {String} Raw string of response 14050 * object: {Object} Parsed object of response 14051 * error: {Object} Wrapped exception that was caught 14052 * error.errorType: {String} Type of error that was caught 14053 * error.errorMessage: {String} Message associated with error 14054 * } 14055 * 14056 * @constructs 14057 */ 14058 init: function (callbacks) { 14059 this._super("", callbacks); 14060 //when post is performed and id is empty 14061 /*if (id === "") { 14062 this._loaded = true; 14063 }*/ 14064 this._layoutxml = {}; 14065 }, 14066 14067 /** 14068 * Returns REST class of LayoutConfig object 14069 */ 14070 getRestClass: function () { 14071 return LayoutConfig; 14072 }, 14073 14074 /** 14075 * The type of this REST object is LayoutConfig 14076 */ 14077 getRestType: function () { 14078 return "LayoutConfig"; 14079 }, 14080 14081 /** 14082 * Gets the REST URL of this object. 14083 * 14084 * If the parent has an id, the id is appended. 14085 * On occasions of POST, it will not have an id. 14086 */ 14087 getRestUrl: function () { 14088 var layoutUri = "/finesse/api/" + this.getRestType() + "/default"; 14089 /*if (this._id) { 14090 layoutUri = layoutUri + "/" + this._id; 14091 }*/ 14092 return layoutUri; 14093 }, 14094 14095 /** 14096 * This API does not support subscription 14097 */ 14098 supportsSubscriptions: false, 14099 14100 keepRestResponse: true, 14101 14102 14103 /** 14104 * Gets finesselayout.xml retrieved from the API call 14105 */ 14106 getLayoutxml: function () { 14107 this.isLoaded(); 14108 var layoutxml = this.getData().layoutxml; 14109 14110 // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with update()) 14111 layoutxml = layoutxml.replace(/&/g,"&"); 14112 14113 return layoutxml; 14114 }, 14115 14116 /** 14117 * Gets the type of this LayoutConfig object 14118 */ 14119 /* 14120 getType: function () { 14121 this.isLoaded(); 14122 return this.getData().type; 14123 },*/ 14124 14125 /** 14126 * Retrieve the LayoutConfig settings. 14127 * If the id is not provided the API call will fail. 14128 * @returns {LayoutConfig} 14129 * This LayoutConfig object to allow cascading. 14130 */ 14131 get: function () { 14132 this._synchronize(); 14133 return this; 14134 }, 14135 14136 /** 14137 * Closure handle updating of the internal data for the LayoutConfig object 14138 * upon a successful update (PUT) request before calling the intended 14139 * success handler provided by the consumer 14140 * 14141 * @param {Object} 14142 * layoutconfig Reference to this LayoutConfig object 14143 * @param {Object} 14144 * LayoutConfig Object that contains the settings to be 14145 * submitted in the api request 14146 * @param {Function} 14147 * successHandler The success handler specified by the consumer 14148 * of this object 14149 * @returns {LayoutConfig} This LayoutConfig object to allow cascading 14150 */ 14151 14152 createPutSuccessHandler: function (layoutconfig, contentBody, successHandler) { 14153 return function (rsp) { 14154 // Update internal structure based on response. Here we 14155 // inject the contentBody from the PUT request into the 14156 // rsp.object element to mimic a GET as a way to take 14157 // advantage of the existing _processResponse method. 14158 rsp.content = contentBody; 14159 rsp.object.LayoutConfig = {}; 14160 rsp.object.LayoutConfig.finesseLayout = contentBody; 14161 layoutconfig._processResponse(rsp); 14162 14163 //Remove the injected layoutConfig object before cascading response 14164 rsp.object.LayoutConfig = {}; 14165 14166 //cascade response back to consumer's response handler 14167 successHandler(rsp); 14168 }; 14169 }, 14170 14171 /** 14172 * Update LayoutConfig 14173 * @param {Object} finesselayout 14174 * The XML for FinesseLayout being stored 14175 * 14176 * @param {Object} handlers 14177 * An object containing callback handlers for the request. Optional. 14178 * @param {Function} options.success(rsp) 14179 * A callback function to be invoked for a successful request. 14180 * { 14181 * status: {Number} The HTTP status code returned 14182 * content: {String} Raw string of response 14183 * object: {Object} Parsed object of response 14184 * } 14185 * @param {Function} options.error(rsp) 14186 * A callback function to be invoked for an unsuccessful request. 14187 * { 14188 * status: {Number} The HTTP status code returned 14189 * content: {String} Raw string of response 14190 * object: {Object} Parsed object of response (HTTP errors) 14191 * error: {Object} Wrapped exception that was caught 14192 * error.errorType: {String} Type of error that was caught 14193 * error.errorMessage: {String} Message associated with error 14194 * } 14195 * @returns {finesse.restservices.LayoutConfig} 14196 * This LayoutConfig object to allow cascading 14197 */ 14198 14199 update: function (layoutxml, handlers) { 14200 this.isLoaded(); 14201 14202 14203 var contentBody = {}, 14204 //Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters) 14205 re = /<gadget>\s*(\S+)\s*<\/gadget>/g; 14206 14207 // We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml()) 14208 layoutxml = layoutxml.replace(/&(?!amp;)/g, "&"); 14209 14210 //used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace) 14211 layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>"); 14212 14213 contentBody[this.getRestType()] = { 14214 "layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true) 14215 }; 14216 14217 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 14218 handlers = handlers || {}; 14219 14220 this.restRequest(this.getRestUrl(), { 14221 method: 'PUT', 14222 success: this.createPutSuccessHandler(this, layoutxml, handlers.success), 14223 error: handlers.error, 14224 content: contentBody 14225 }); 14226 14227 return this; // Allow cascading 14228 } 14229 14230 /** 14231 *TODO createPostSuccessHandler needs to be debugged to make it working 14232 * Closure handle creating new LayoutConfig object 14233 * upon a successful create (POST) request before calling the intended 14234 * success handler provided by the consumer 14235 * 14236 * @param {Object} 14237 * layoutconfig Reference to this LayoutConfig object 14238 * @param {Object} 14239 * LayoutConfig Object that contains the settings to be 14240 * submitted in the api request 14241 * @param {Function} 14242 * successHandler The success handler specified by the consumer 14243 * of this object 14244 * @returns {finesse.restservices.LayoutConfig} This LayoutConfig object to allow cascading 14245 */ 14246 /* 14247 createPostSuccessHandler: function (layoutconfig, contentBody, successHandler) { 14248 return function (rsp) { 14249 14250 rsp.object = contentBody; 14251 layoutconfig._processResponse(rsp); 14252 14253 //Remove the injected layoutConfig object before cascading response 14254 rsp.object = {}; 14255 14256 //cascade response back to consumer's response handler 14257 successHandler(rsp); 14258 }; 14259 }, */ 14260 14261 /** 14262 * TODO Method needs to be debugged to make POST working 14263 * Add LayoutConfig 14264 * @param {Object} finesselayout 14265 * The XML for FinesseLayout being stored 14266 * 14267 * @param {Object} handlers 14268 * An object containing callback handlers for the request. Optional. 14269 * @param {Function} options.success(rsp) 14270 * A callback function to be invoked for a successful request. 14271 * { 14272 * status: {Number} The HTTP status code returned 14273 * content: {String} Raw string of response 14274 * object: {Object} Parsed object of response 14275 * } 14276 * @param {Function} options.error(rsp) 14277 * A callback function to be invoked for an unsuccessful request. 14278 * { 14279 * status: {Number} The HTTP status code returned 14280 * content: {String} Raw string of response 14281 * object: {Object} Parsed object of response (HTTP errors) 14282 * error: {Object} Wrapped exception that was caught 14283 * error.errorType: {String} Type of error that was caught 14284 * error.errorMessage: {String} Message associated with error 14285 * } 14286 * @returns {finesse.restservices.LayoutConfig} 14287 * This LayoutConfig object to allow cascading 14288 */ 14289 /* 14290 add: function (layoutxml, handlers) { 14291 this.isLoaded(); 14292 var contentBody = {}; 14293 14294 14295 contentBody[this.getRestType()] = { 14296 "layoutxml": layoutxml, 14297 "type": "current" 14298 }; 14299 14300 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 14301 handlers = handlers || {}; 14302 14303 this.restRequest(this.getRestUrl(), { 14304 method: 'POST', 14305 success: this.createPostSuccessHandler(this, contentBody, handlers.success), 14306 error: handlers.error, 14307 content: contentBody 14308 }); 14309 14310 return this; // Allow cascading 14311 } */ 14312 }); 14313 14314 window.finesse = window.finesse || {}; 14315 window.finesse.restservices = window.finesse.restservices || {}; 14316 window.finesse.restservices.LayoutConfig = LayoutConfig; 14317 14318 return LayoutConfig; 14319 14320 }); 14321 14322 /** 14323 * JavaScript representation of the Finesse LayoutConfig object for a Team. 14324 * 14325 * @requires finesse.clientservices.ClientServices 14326 * @requires Class 14327 * @requires finesse.FinesseBase 14328 * @requires finesse.restservices.RestBase 14329 * @requires finesse.utilities.Utilities 14330 * @requires finesse.restservices.LayoutConfig 14331 */ 14332 14333 /** The following comment is to prevent jslint errors about 14334 * using variables before they are defined. 14335 */ 14336 /*global Exception */ 14337 14338 /** @private */ 14339 define('restservices/TeamLayoutConfig',[ 14340 'restservices/RestBase', 14341 'utilities/Utilities', 14342 'restservices/LayoutConfig' 14343 ], 14344 function (RestBase, Utilities, LayoutConfig) { 14345 14346 var TeamLayoutConfig = RestBase.extend({ 14347 // Keep the restresponse so we can parse the layoutxml out of it in getLayoutXML() 14348 keepRestResponse: true, 14349 14350 /** 14351 * @class 14352 * JavaScript representation of a LayoutConfig object for a Team. Also exposes 14353 * methods to operate on the object against the server. 14354 * 14355 * @param {Object} options 14356 * An object with the following properties:<ul> 14357 * <li><b>id:</b> The id of the object being constructed</li> 14358 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 14359 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 14360 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 14361 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 14362 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 14363 * <li><b>status:</b> {Number} The HTTP status code returned</li> 14364 * <li><b>content:</b> {String} Raw string of response</li> 14365 * <li><b>object:</b> {Object} Parsed object of response</li> 14366 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 14367 * <li><b>errorType:</b> {String} Type of error that was caught</li> 14368 * <li><b>errorMessage:</b> {String} Message associated with error</li> 14369 * </ul></li> 14370 * </ul></li> 14371 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 14372 * @constructs 14373 **/ 14374 init: function (options) { 14375 this._super(options); 14376 }, 14377 14378 /** 14379 * @private 14380 * Gets the REST class for the current object - this is the LayoutConfigs class. 14381 * @returns {Object} The LayoutConfigs class. 14382 */ 14383 getRestClass: function () { 14384 return TeamLayoutConfig; 14385 }, 14386 14387 /** 14388 * @private 14389 * Gets the REST type for the current object - this is a "LayoutConfig". 14390 * @returns {String} The LayoutConfig string. 14391 */ 14392 getRestType: function () { 14393 return "TeamLayoutConfig"; 14394 }, 14395 14396 /** 14397 * @private 14398 * Override default to indicate that this object doesn't support making 14399 * requests. 14400 */ 14401 supportsRequests: false, 14402 14403 /** 14404 * @private 14405 * Override default to indicate that this object doesn't support subscriptions. 14406 */ 14407 supportsSubscriptions: false, 14408 14409 /** 14410 * Getter for the category. 14411 * @returns {String} The category. 14412 */ 14413 getLayoutXML: function () { 14414 this.isLoaded(); 14415 var layoutxml = this.getData().layoutxml; 14416 14417 // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with put()) 14418 layoutxml = layoutxml.replace(/&/g,"&"); 14419 14420 return layoutxml; 14421 }, 14422 14423 /** 14424 * Getter for the code. 14425 * @returns {String} The code. 14426 */ 14427 getUseDefault: function () { 14428 this.isLoaded(); 14429 return this.getData().useDefault; 14430 }, 14431 14432 /** 14433 * Retrieve the TeamLayoutConfig. 14434 * 14435 * @returns {finesse.restservices.TeamLayoutConfig} 14436 */ 14437 get: function () { 14438 // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway 14439 this._id = "0"; 14440 // set loaded to false so it will rebuild the collection after the get 14441 this._loaded = false; 14442 // reset collection 14443 this._collection = {}; 14444 // perform get 14445 this._synchronize(); 14446 return this; 14447 }, 14448 14449 createPutSuccessHandler: function(contact, contentBody, successHandler){ 14450 return function (rsp) { 14451 // Update internal structure based on response. Here we 14452 // inject the contentBody from the PUT request into the 14453 // rsp.object element to mimic a GET as a way to take 14454 // advantage of the existing _processResponse method. 14455 rsp.object = contentBody; 14456 contact._processResponse(rsp); 14457 14458 //Remove the injected Contact object before cascading response 14459 rsp.object = {}; 14460 14461 //cascade response back to consumer's response handler 14462 successHandler(rsp); 14463 }; 14464 }, 14465 14466 put: function (newValues, handlers) { 14467 // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway 14468 this._id = "0"; 14469 this.isLoaded(); 14470 14471 // We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml()) 14472 var layoutxml = newValues.layoutXML.replace(/&(?!amp;)/g, "&"), 14473 contentBody = {}, 14474 //Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters) 14475 re = /<gadget>\s*(\S+)\s*<\/gadget>/g; 14476 14477 //used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace) 14478 layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>"); 14479 14480 contentBody[this.getRestType()] = { 14481 "useDefault": newValues.useDefault, 14482 // The LayoutConfig restservice javascript class only translates ampersands, so we'll do that also 14483 "layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true) 14484 }; 14485 14486 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 14487 handlers = handlers || {}; 14488 14489 this.restRequest(this.getRestUrl(), { 14490 method: 'PUT', 14491 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 14492 error: handlers.error, 14493 content: contentBody 14494 }); 14495 14496 return this; // Allow cascading 14497 }, 14498 14499 getRestUrl: function(){ 14500 // return team's url + /LayoutConfig 14501 // eg: /api/Team/1/LayoutConfig 14502 if(this._restObj === undefined){ 14503 throw new Exception("TeamLayoutConfig instances must have a parent team object."); 14504 } 14505 return this._restObj.getRestUrl() + '/LayoutConfig'; 14506 } 14507 14508 }); 14509 14510 window.finesse = window.finesse || {}; 14511 window.finesse.restservices = window.finesse.restservices || {}; 14512 window.finesse.restservices.TeamLayoutConfig = TeamLayoutConfig; 14513 14514 return TeamLayoutConfig; 14515 }); 14516 14517 /** 14518 * JavaScript representation of a TeamWorkflow. 14519 * 14520 * @requires finesse.clientservices.ClientServices 14521 * @requires Class 14522 * @requires finesse.FinesseBase 14523 * @requires finesse.restservices.RestBase 14524 */ 14525 /** @private */ 14526 define('restservices/TeamWorkflow',['restservices/RestBase'], function (RestBase) { 14527 14528 var TeamWorkflow = RestBase.extend({ 14529 14530 /** 14531 * @class 14532 * JavaScript representation of a TeamWorkflow object. Also exposes 14533 * methods to operate on the object against the server. 14534 * 14535 * @param {Object} options 14536 * An object with the following properties:<ul> 14537 * <li><b>id:</b> The id of the object being constructed</li> 14538 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 14539 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 14540 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 14541 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 14542 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 14543 * <li><b>status:</b> {Number} The HTTP status description returned</li> 14544 * <li><b>content:</b> {String} Raw string of response</li> 14545 * <li><b>object:</b> {Object} Parsed object of response</li> 14546 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 14547 * <li><b>errorType:</b> {String} Type of error that was caught</li> 14548 * <li><b>errorMessage:</b> {String} Message associated with error</li> 14549 * </ul></li> 14550 * </ul></li> 14551 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 14552 * @constructs 14553 **/ 14554 init: function (options) { 14555 this._super(options); 14556 }, 14557 14558 /** 14559 * @private 14560 * Gets the REST class for the current object - this is the TeamWorkflow class. 14561 * @returns {Object} The TeamWorkflow class. 14562 */ 14563 getRestClass: function () { 14564 return TeamWorkflow; 14565 }, 14566 14567 /** 14568 * @private 14569 * Gets the REST type for the current object - this is a "Workflow". 14570 * @returns {String} The Workflow string. 14571 */ 14572 getRestType: function () { 14573 return "Workflow"; 14574 }, 14575 14576 /** 14577 * @private 14578 * Override default to indicate that this object doesn't support making 14579 * requests. 14580 */ 14581 supportsRequests: false, 14582 14583 /** 14584 * @private 14585 * Override default to indicate that this object doesn't support subscriptions. 14586 */ 14587 supportsSubscriptions: false, 14588 14589 /** 14590 * Getter for the name. 14591 * @returns {String} The name. 14592 */ 14593 getName: function () { 14594 this.isLoaded(); 14595 return this.getData().name; 14596 }, 14597 14598 /** 14599 * Getter for the description. 14600 * @returns {String} The description. 14601 */ 14602 getDescription: function () { 14603 this.isLoaded(); 14604 return this.getData().description; 14605 }, 14606 14607 /** 14608 * Getter for the Uri value. 14609 * @returns {String} The Uri. 14610 */ 14611 getUri: function () { 14612 this.isLoaded(); 14613 return this.getData().uri; 14614 } 14615 14616 }); 14617 14618 window.finesse = window.finesse || {}; 14619 window.finesse.restservices = window.finesse.restservices || {}; 14620 window.finesse.restservices.TeamWorkflow = TeamWorkflow; 14621 14622 return TeamWorkflow; 14623 }); 14624 14625 /** 14626 * JavaScript representation of the TeamWorkflows collection 14627 * object which contains a list of TeamWorkflow objects. 14628 * 14629 * @requires finesse.clientservices.ClientServices 14630 * @requires Class 14631 * @requires finesse.FinesseBase 14632 * @requires finesse.restservices.RestBase 14633 * @requires finesse.restservices.Dialog 14634 * @requires finesse.restservices.RestCollectionBase 14635 */ 14636 /** @private */ 14637 define('restservices/TeamWorkflows',[ 14638 'restservices/RestCollectionBase', 14639 'restservices/TeamWorkflow', 14640 'restservices/RestBase' 14641 ], 14642 function (RestCollectionBase, TeamWorkflow, RestBase) { 14643 14644 var TeamWorkflows = RestCollectionBase.extend({ 14645 14646 /** 14647 * @class 14648 * JavaScript representation of a TeamWorkflows collection object. Also exposes 14649 * methods to operate on the object against the server. 14650 * 14651 * @param {Object} options 14652 * An object with the following properties:<ul> 14653 * <li><b>id:</b> The id of the object being constructed</li> 14654 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 14655 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 14656 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 14657 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 14658 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 14659 * <li><b>status:</b> {Number} The HTTP status code returned</li> 14660 * <li><b>content:</b> {String} Raw string of response</li> 14661 * <li><b>object:</b> {Object} Parsed object of response</li> 14662 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 14663 * <li><b>errorType:</b> {String} Type of error that was caught</li> 14664 * <li><b>errorMessage:</b> {String} Message associated with error</li> 14665 * </ul></li> 14666 * </ul></li> 14667 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 14668 * @constructs 14669 **/ 14670 init: function (options) { 14671 this._super(options); 14672 }, 14673 14674 /** 14675 * @private 14676 * Gets the REST class for the current object - this is the TeamWorkflows class. 14677 */ 14678 getRestClass: function () { 14679 return TeamWorkflows; 14680 }, 14681 14682 /** 14683 * @private 14684 * Gets the REST class for the objects that make up the collection. - this 14685 * is the TeamWorkflow class. 14686 */ 14687 getRestItemClass: function () { 14688 return TeamWorkflow; 14689 }, 14690 14691 /** 14692 * @private 14693 * Gets the REST type for the current object - this is a "Workflows". 14694 */ 14695 getRestType: function () { 14696 return "Workflows"; 14697 }, 14698 14699 /** 14700 * Overrides the parent class. Returns the url for the Workflows resource 14701 */ 14702 getRestUrl: function () { 14703 var restObj = this._restObj, restUrl = ""; 14704 14705 //Prepend the base REST object if one was provided. 14706 //Otherwise prepend with the default webapp name. 14707 if (restObj instanceof RestBase) { 14708 restUrl += restObj.getRestUrl(); 14709 } else { 14710 restUrl += "/finesse/api/Team"; 14711 } 14712 //Append ID if it is not undefined, null, or empty. 14713 if (this._id) { 14714 restUrl += "/" + this._id; 14715 } 14716 //Append the REST type. 14717 restUrl += "/Workflows"; 14718 14719 return restUrl; 14720 }, 14721 14722 /** 14723 * @private 14724 * Gets the REST type for the objects that make up the collection - this is "Workflow". 14725 */ 14726 getRestItemType: function () { 14727 return "Workflow"; 14728 }, 14729 14730 /** 14731 * @private 14732 * Override default to indicates that the collection supports making requests. 14733 */ 14734 supportsRequests: true, 14735 14736 /** 14737 * @private 14738 * Override default to indicates that the collection does not subscribe to its objects. 14739 */ 14740 supportsRestItemSubscriptions: false, 14741 14742 /** 14743 * Retrieve the Sign Out Reason Codes. 14744 * 14745 * @returns {finesse.restservices.TeamWorkflows} 14746 * This TeamWorkflows object to allow cascading. 14747 */ 14748 get: function () { 14749 // set loaded to false so it will rebuild the collection after the get 14750 this._loaded = false; 14751 // reset collection 14752 this._collection = {}; 14753 // perform get 14754 this._synchronize(); 14755 return this; 14756 }, 14757 14758 /* We only use PUT and GET on Reason Code team assignments 14759 * @param {Object} contact 14760 * @param {Object} contentBody 14761 * @param {Function} successHandler 14762 */ 14763 createPutSuccessHandler: function (contact, contentBody, successHandler) { 14764 return function (rsp) { 14765 // Update internal structure based on response. Here we 14766 // inject the contentBody from the PUT request into the 14767 // rsp.object element to mimic a GET as a way to take 14768 // advantage of the existing _processResponse method. 14769 rsp.object = contentBody; 14770 contact._processResponse(rsp); 14771 14772 //Remove the injected contentBody object before cascading response 14773 rsp.object = {}; 14774 14775 //cascade response back to consumer's response handler 14776 successHandler(rsp); 14777 }; 14778 }, 14779 14780 /** 14781 * Update - This should be all that is needed. 14782 * @param {Object} newValues 14783 * @param {Object} handlers 14784 * @returns {finesse.restservices.TeamWorkflows} 14785 * This TeamWorkflows object to allow cascading. 14786 */ 14787 update: function (newValues, handlers) { 14788 this.isLoaded(); 14789 var contentBody = {}, contentBodyInner = [], i, innerObject = {}; 14790 14791 contentBody[this.getRestType()] = { 14792 }; 14793 14794 for (i in newValues) { 14795 if (newValues.hasOwnProperty(i)) { 14796 innerObject = { 14797 "uri": newValues[i] 14798 }; 14799 contentBodyInner.push(innerObject); 14800 } 14801 } 14802 14803 contentBody[this.getRestType()] = { 14804 "Workflow" : contentBodyInner 14805 }; 14806 14807 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined 14808 handlers = handlers || {}; 14809 14810 this.restRequest(this.getRestUrl(), { 14811 method: 'PUT', 14812 success: this.createPutSuccessHandler(this, contentBody, handlers.success), 14813 error: handlers.error, 14814 content: contentBody 14815 }); 14816 14817 return this; // Allow cascading 14818 } 14819 14820 }); 14821 14822 window.finesse = window.finesse || {}; 14823 window.finesse.restservices = window.finesse.restservices || {}; 14824 window.finesse.restservices.TeamWorkflows = TeamWorkflows; 14825 14826 return TeamWorkflows; 14827 }); 14828 14829 /** 14830 * JavaScript representation of the Finesse Team REST object. 14831 * 14832 * @requires finesse.clientservices.ClientServices 14833 * @requires Class 14834 * @requires finesse.FinesseBase 14835 * @requires finesse.restservices.RestBase 14836 * @requires finesse.restservices.RestCollectionBase 14837 * @requires finesse.restservices.User 14838 * @requires finesse.restservices.Users 14839 */ 14840 14841 /** 14842 * The following comment prevents JSLint errors concerning undefined global variables. 14843 * It tells JSLint that these identifiers are defined elsewhere. 14844 */ 14845 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */ 14846 14847 /** The following comment is to prevent jslint errors about 14848 * using variables before they are defined. 14849 */ 14850 /*global $, jQuery, Handlebars, dojox, dojo, finesse */ 14851 14852 /** @private */ 14853 define('restservices/Team',[ 14854 'restservices/RestBase', 14855 'utilities/Utilities', 14856 'restservices/Users', 14857 'restservices/TeamNotReadyReasonCodes', 14858 'restservices/TeamWrapUpReasons', 14859 'restservices/TeamSignOutReasonCodes', 14860 'restservices/TeamPhoneBooks', 14861 'restservices/TeamLayoutConfig', 14862 'restservices/TeamWorkflows' 14863 ], 14864 function (RestBase, Utilities, Users, TeamNotReadyReasonCodes, TeamWrapUpReasons, TeamSignOutReasonCodes, TeamPhoneBooks, TeamLayoutConfig, TeamWorkflows) { 14865 var Team = RestBase.extend(/** @lends finesse.restservices.Team.prototype */{ 14866 14867 _teamLayoutConfig: null, 14868 14869 /** 14870 * @class 14871 * A Team is a set of Agent Users, typically supervised by one or more Supervisor Users. 14872 * 14873 * @augments finesse.restservices.RestBase 14874 * @see finesse.restservices.User#getSupervisedTeams 14875 * @see finesse.restservices.Users 14876 * @constructs 14877 */ 14878 _fakeConstuctor: function () { 14879 /* This is here to hide the real init constructor from the public docs */ 14880 }, 14881 14882 /** 14883 * @private 14884 * @class 14885 * JavaScript representation of a Team object. Also exposes methods to operate 14886 * on the object against the server. 14887 * 14888 * @param {Object} options 14889 * An object with the following properties:<ul> 14890 * <li><b>id:</b> The id of the object being constructed</li> 14891 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 14892 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 14893 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 14894 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 14895 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 14896 * <li><b>status:</b> {Number} The HTTP status code returned</li> 14897 * <li><b>content:</b> {String} Raw string of response</li> 14898 * <li><b>object:</b> {Object} Parsed object of response</li> 14899 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 14900 * <li><b>errorType:</b> {String} Type of error that was caught</li> 14901 * <li><b>errorMessage:</b> {String} Message associated with error</li> 14902 * </ul></li> 14903 * </ul></li> 14904 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 14905 **/ 14906 init: function (options) { 14907 this._super(options); 14908 }, 14909 14910 /** 14911 * @private 14912 * Gets the REST class for the current object - this is the Team class. 14913 * @returns {Object} The Team constructor. 14914 */ 14915 getRestClass: function () { 14916 return finesse.restesrvices.Team; 14917 }, 14918 14919 /** 14920 * @private 14921 * Gets the REST type for the current object - this is a "Team". 14922 * @returns {String} The Team string. 14923 */ 14924 getRestType: function () { 14925 return "Team"; 14926 }, 14927 14928 /** 14929 * @private 14930 * Override default to indicate that this object doesn't support making 14931 * requests. 14932 */ 14933 supportsSubscriptions: false, 14934 14935 /** 14936 * Getter for the team id. 14937 * @returns {String} The team id. 14938 */ 14939 getId: function () { 14940 this.isLoaded(); 14941 return this.getData().id; 14942 }, 14943 14944 /** 14945 * Getter for the team name. 14946 * @returns {String} The team name 14947 */ 14948 getName: function () { 14949 this.isLoaded(); 14950 return this.getData().name; 14951 }, 14952 14953 /** 14954 * @private 14955 * Getter for the team uri. 14956 * @returns {String} The team uri 14957 */ 14958 getUri: function () { 14959 this.isLoaded(); 14960 return this.getData().uri; 14961 }, 14962 14963 /** 14964 * Constructs and returns a collection of Users. 14965 * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers. 14966 * @returns {finesse.restservices.Users} Users collection of User objects. 14967 */ 14968 getUsers: function (options) { 14969 this.isLoaded(); 14970 options = options || {}; 14971 14972 options.parentObj = this; 14973 // We are using getData() instead of getData.Users because the superclass (RestCollectionBase) 14974 // for Users needs the "Users" key to validate the provided payload matches the class type. 14975 options.data = this.getData(); 14976 14977 return new Users(options); 14978 }, 14979 14980 /** 14981 * @private 14982 * Getter for a teamNotReadyReasonCodes collection object that is associated with Team. 14983 * @param callbacks 14984 * @returns {teamNotReadyReasonCodes} 14985 * A teamNotReadyReasonCodes collection object. 14986 */ 14987 getTeamNotReadyReasonCodes: function (callbacks) { 14988 var options = callbacks || {}; 14989 options.parentObj = this; 14990 this.isLoaded(); 14991 14992 if (!this._teamNotReadyReasonCodes) { 14993 this._teamNotReadyReasonCodes = new TeamNotReadyReasonCodes(options); 14994 } 14995 14996 return this._teamNotReadyReasonCodes; 14997 }, 14998 14999 /** 15000 * @private 15001 * Getter for a teamWrapUpReasons collection object that is associated with Team. 15002 * @param callbacks 15003 * @returns {teamWrapUpReasons} 15004 * A teamWrapUpReasons collection object. 15005 */ 15006 getTeamWrapUpReasons: function (callbacks) { 15007 var options = callbacks || {}; 15008 options.parentObj = this; 15009 this.isLoaded(); 15010 15011 if (!this._teamWrapUpReasons) { 15012 this._teamWrapUpReasons = new TeamWrapUpReasons(options); 15013 } 15014 15015 return this._teamWrapUpReasons; 15016 }, 15017 15018 /** 15019 * @private 15020 * Getter for a teamSignOutReasonCodes collection object that is associated with Team. 15021 * @param callbacks 15022 * @returns {teamSignOutReasonCodes} 15023 * A teamSignOutReasonCodes collection object. 15024 */ 15025 15026 getTeamSignOutReasonCodes: function (callbacks) { 15027 var options = callbacks || {}; 15028 options.parentObj = this; 15029 this.isLoaded(); 15030 15031 if (!this._teamSignOutReasonCodes) { 15032 this._teamSignOutReasonCodes = new TeamSignOutReasonCodes(options); 15033 } 15034 15035 return this._teamSignOutReasonCodes; 15036 }, 15037 15038 /** 15039 * @private 15040 * Getter for a teamPhoneBooks collection object that is associated with Team. 15041 * @param callbacks 15042 * @returns {teamPhoneBooks} 15043 * A teamPhoneBooks collection object. 15044 */ 15045 getTeamPhoneBooks: function (callbacks) { 15046 var options = callbacks || {}; 15047 options.parentObj = this; 15048 this.isLoaded(); 15049 15050 if (!this._phonebooks) { 15051 this._phonebooks = new TeamPhoneBooks(options); 15052 } 15053 15054 return this._phonebooks; 15055 }, 15056 15057 /** 15058 * @private 15059 * Getter for a teamWorkflows collection object that is associated with Team. 15060 * @param callbacks 15061 * @returns {teamWorkflows} 15062 * A teamWorkflows collection object. 15063 */ 15064 getTeamWorkflows: function (callbacks) { 15065 var options = callbacks || {}; 15066 options.parentObj = this; 15067 this.isLoaded(); 15068 15069 if (!this._workflows) { 15070 this._workflows = new TeamWorkflows(options); 15071 } 15072 15073 return this._workflows; 15074 }, 15075 15076 /** 15077 * @private 15078 * Getter for a teamLayoutConfig object that is associated with Team. 15079 * @param callbacks 15080 * @returns {teamLayoutConfig} 15081 */ 15082 getTeamLayoutConfig: function (callbacks) { 15083 var options = callbacks || {}; 15084 options.parentObj = this; 15085 this.isLoaded(); 15086 15087 if (this._teamLayoutConfig === null) { 15088 this._teamLayoutConfig = new TeamLayoutConfig(options); 15089 } 15090 15091 return this._teamLayoutConfig; 15092 } 15093 15094 }); 15095 15096 window.finesse = window.finesse || {}; 15097 window.finesse.restservices = window.finesse.restservices || {}; 15098 window.finesse.restservices.Team = Team; 15099 15100 return Team; 15101 }); 15102 15103 /** 15104 * JavaScript representation of the Finesse Teams collection. 15105 * object which contains a list of Team objects 15106 * @requires finesse.clientservices.ClientServices 15107 * @requires Class 15108 * @requires finesse.FinesseBase 15109 * @requires finesse.restservices.RestBase 15110 * @requires finesse.restservices.RestCollectionBase 15111 */ 15112 15113 /** @private */ 15114 define('restservices/Teams',[ 15115 'restservices/RestCollectionBase', 15116 'restservices/Team' 15117 ], 15118 function (RestCollectionBase, Team) { 15119 /** @private */ 15120 var Teams = RestCollectionBase.extend({ 15121 15122 /** 15123 * @class 15124 * JavaScript representation of a Teams collection object. Also exposes methods to operate 15125 * on the object against the server. 15126 * 15127 * @param {Object} options 15128 * An object with the following properties:<ul> 15129 * <li><b>id:</b> The id of the object being constructed</li> 15130 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 15131 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 15132 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 15133 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 15134 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 15135 * <li><b>status:</b> {Number} The HTTP status code returned</li> 15136 * <li><b>content:</b> {String} Raw string of response</li> 15137 * <li><b>object:</b> {Object} Parsed object of response</li> 15138 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 15139 * <li><b>errorType:</b> {String} Type of error that was caught</li> 15140 * <li><b>errorMessage:</b> {String} Message associated with error</li> 15141 * </ul></li> 15142 * </ul></li> 15143 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 15144 * @constructs 15145 **/ 15146 init: function (options) { 15147 this._super(options); 15148 }, 15149 15150 /** 15151 * @private 15152 * Gets the REST class for the current object - this is the Teams class. 15153 * @returns {Object} The Teams constructor. 15154 */ 15155 getRestClass: function () { 15156 return Teams; 15157 }, 15158 15159 /** 15160 * @private 15161 * Gets the REST class for the objects that make up the collection. - this 15162 * is the Team class. 15163 */ 15164 getRestItemClass: function () { 15165 return Team; 15166 }, 15167 15168 /** 15169 * @private 15170 * Gets the REST type for the current object - this is a "Teams". 15171 * @returns {String} The Teams string. 15172 */ 15173 getRestType: function () { 15174 return "Teams"; 15175 }, 15176 15177 /** 15178 * @private 15179 * Gets the REST type for the objects that make up the collection - this is "Team". 15180 */ 15181 getRestItemType: function () { 15182 return "Team"; 15183 }, 15184 15185 /** 15186 * @private 15187 * Override default to indicates that the collection supports making 15188 * requests. 15189 */ 15190 supportsRequests: true, 15191 15192 /** 15193 * @private 15194 * Override default to indicate that this object doesn't support subscriptions. 15195 */ 15196 supportsRestItemSubscriptions: false, 15197 15198 /** 15199 * @private 15200 * Retrieve the Teams. This call will re-query the server and refresh the collection. 15201 * 15202 * @returns {finesse.restservices.Teams} 15203 * This Teams object to allow cascading. 15204 */ 15205 get: function () { 15206 // set loaded to false so it will rebuild the collection after the get 15207 this._loaded = false; 15208 // reset collection 15209 this._collection = {}; 15210 // perform get 15211 this._synchronize(); 15212 return this; 15213 } 15214 15215 }); 15216 15217 window.finesse = window.finesse || {}; 15218 window.finesse.restservices = window.finesse.restservices || {}; 15219 window.finesse.restservices.Teams = Teams; 15220 15221 return Teams; 15222 }); 15223 15224 /** 15225 * JavaScript representation of the Finesse SystemInfo object 15226 * 15227 * @requires finesse.clientservices.ClientServices 15228 * @requires Class 15229 * @requires finesse.FinesseBase 15230 * @requires finesse.restservices.RestBase 15231 */ 15232 15233 /** @private */ 15234 define('restservices/SystemInfo',['restservices/RestBase'], function (RestBase) { 15235 15236 var SystemInfo = RestBase.extend(/** @lends finesse.restservices.SystemInfo.prototype */{ 15237 /** 15238 * @private 15239 * Returns whether this object supports subscriptions 15240 */ 15241 supportsSubscriptions: false, 15242 15243 doNotRefresh: true, 15244 15245 /** 15246 * @class 15247 * JavaScript representation of a SystemInfo object. 15248 * 15249 * @augments finesse.restservices.RestBase 15250 * @see finesse.restservices.SystemInfo.Statuses 15251 * @constructs 15252 */ 15253 _fakeConstuctor: function () { 15254 /* This is here to hide the real init constructor from the public docs */ 15255 }, 15256 15257 /** 15258 * @private 15259 * JavaScript representation of a SystemInfo object. Also exposes methods to operate 15260 * on the object against the server. 15261 * 15262 * @param {Object} options 15263 * An object with the following properties:<ul> 15264 * <li><b>id:</b> The id of the object being constructed</li> 15265 * <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li> 15266 * <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li> 15267 * <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li> 15268 * <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li> 15269 * <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul> 15270 * <li><b>status:</b> {Number} The HTTP status code returned</li> 15271 * <li><b>content:</b> {String} Raw string of response</li> 15272 * <li><b>object:</b> {Object} Parsed object of response</li> 15273 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 15274 * <li><b>errorType:</b> {String} Type of error that was caught</li> 15275 * <li><b>errorMessage:</b> {String} Message associated with error</li> 15276 * </ul></li> 15277 * </ul></li> 15278 * <li><b>parentObj: (optional)</b> The parent object</li></ul> 15279 **/ 15280 init: function (id, callbacks, restObj) 15281 { 15282 this._super(id, callbacks, restObj); 15283 }, 15284 15285 /** 15286 * @private 15287 * Gets the REST class for the current object - this is the SystemInfo object. 15288 */ 15289 getRestClass: function () { 15290 return SystemInfo; 15291 }, 15292 15293 /** 15294 * @private 15295 * Gets the REST type for the current object - this is a "SystemInfo". 15296 */ 15297 getRestType: function () 15298 { 15299 return "SystemInfo"; 15300 }, 15301 15302 _validate: function (obj) 15303 { 15304 return true; 15305 }, 15306 15307 /** 15308 * Returns the status of the Finesse system. 15309 * IN_SERVICE if the Finesse API reports that it is in service, 15310 * OUT_OF_SERVICE otherwise. 15311 * @returns {finesse.restservices.SystemInfo.Statuses} System Status 15312 */ 15313 getStatus: function () { 15314 this.isLoaded(); 15315 return this.getData().status; 15316 }, 15317 15318 /** 15319 * Returns the current timestamp from this SystemInfo object. 15320 * This is used to calculate time drift delta between server and client. 15321 * @returns {String} Time (GMT): yyyy-MM-dd'T'HH:mm:ss'Z' 15322 */ 15323 getCurrentTimestamp: function () { 15324 this.isLoaded(); 15325 return this.getData().currentTimestamp; 15326 }, 15327 15328 /** 15329 * Getter for the xmpp domain of the system. 15330 * @returns {String} The xmpp domain corresponding to this SystemInfo object. 15331 */ 15332 getXmppDomain: function () { 15333 this.isLoaded(); 15334 return this.getData().xmppDomain; 15335 }, 15336 15337 /** 15338 * Getter for the xmpp pubsub domain of the system. 15339 * @returns {String} The xmpp pubsub domain corresponding to this SystemInfo object. 15340 */ 15341 getXmppPubSubDomain: function () { 15342 this.isLoaded(); 15343 return this.getData().xmppPubSubDomain; 15344 }, 15345 15346 /** 15347 * Getter for the deployment type (UCCE or UCCX). 15348 * @returns {String} "UCCE" or "UCCX" 15349 */ 15350 getDeploymentType: function () { 15351 this.isLoaded(); 15352 return this.getData().deploymentType; 15353 }, 15354 15355 /** 15356 * Returns whether this is a single node deployment or not by checking for the existence of the secondary node in SystemInfo. 15357 * @returns {Boolean} True for single node deployments, false otherwise. 15358 */ 15359 isSingleNode: function () { 15360 var secondary = this.getData().secondaryNode; 15361 if (secondary && secondary.host) { 15362 return false; 15363 } 15364 return true; 15365 }, 15366 15367 /** 15368 * Checks all arguments against the primary and secondary hosts (FQDN) and returns the match. 15369 * This is useful for getting the FQDN of the current Finesse server. 15370 * @param {String} ...arguments[]... - any number of arguments to match against 15371 * @returns {String} FQDN (if properly configured) of the matched host of the primary or secondary node, or undefined if no match is found. 15372 */ 15373 getThisHost: function () { 15374 var i, 15375 primary = this.getData().primaryNode, 15376 secondary = this.getData().secondaryNode; 15377 15378 for (i = 0; (i < arguments.length); i = i + 1) { 15379 if (primary && arguments[i] === primary.host) { 15380 return primary.host; 15381 } else if (secondary && arguments[i] === secondary.host) { 15382 return secondary.host; 15383 } 15384 } 15385 }, 15386 15387 /** 15388 * Checks all arguments against the primary and secondary hosts (FQDN) and returns the other node. 15389 * This is useful for getting the FQDN of the other Finesse server, i.e. for failover purposes. 15390 * @param {String} arguments - any number of arguments to match against 15391 * @returns {String} FQDN (if properly configured) of the alternate node, defaults to primary if no match is found, undefined for single node deployments. 15392 */ 15393 getAlternateHost: function () { 15394 var i, 15395 isPrimary = false, 15396 primary = this.getData().primaryNode, 15397 secondary = this.getData().secondaryNode, 15398 xmppDomain = this.getData().xmppDomain, 15399 alternateHost; 15400 15401 if (primary && primary.host) { 15402 if (xmppDomain === primary.host) { 15403 isPrimary = true; 15404 } 15405 if (secondary && secondary.host) { 15406 if (isPrimary) { 15407 return secondary.host; 15408 } 15409 return primary.host; 15410 } 15411 } 15412 }, 15413 15414 /** 15415 * Gets the peripheral ID that Finesse is connected to. The peripheral 15416 * ID is the ID of the PG Routing Client (PIM). 15417 * 15418 * @returns {String} The peripheral Id if UCCE, or empty string otherwise. 15419 */ 15420 getPeripheralId : function () { 15421 this.isLoaded(); 15422 15423 var peripheralId = this.getData().peripheralId; 15424 if (peripheralId === null) { 15425 return ""; 15426 } else { 15427 return this.getData().peripheralId; 15428 } 15429 }, 15430 15431 /** 15432 * Gets the license. Only apply to UCCX. 15433 * 15434 * @returns {String} The license if UCCX, or empty string otherwise. 15435 */ 15436 getlicense : function () { 15437 this.isLoaded(); 15438 return this.getData().license || ""; 15439 } 15440 }); 15441 15442 SystemInfo.Statuses = /** @lends finesse.restservices.SystemInfo.Statuses.prototype */ { 15443 /** 15444 * Finesse is in service. 15445 */ 15446 IN_SERVICE: "IN_SERVICE", 15447 /** 15448 * Finesse is not in service. 15449 */ 15450 OUT_OF_SERVICE: "OUT_OF_SERVICE", 15451 /** 15452 * @class SystemInfo status values. 15453 * @constructs 15454 */ 15455 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 15456 15457 }; 15458 15459 window.finesse = window.finesse || {}; 15460 window.finesse.restservices = window.finesse.restservices || {}; 15461 window.finesse.restservices.SystemInfo = SystemInfo; 15462 15463 return SystemInfo; 15464 }); 15465 15466 /** 15467 * Provides standard way resolve message keys with substitution 15468 * 15469 * @requires finesse.container.I18n or gadgets.Prefs 15470 */ 15471 15472 // Add Utilities to the finesse.utilities namespace 15473 define('utilities/I18n',[], function () { 15474 var I18n = (function () { 15475 15476 /** 15477 * Shortcut to finesse.container.I18n.getMsg or gadgets.Prefs.getMsg 15478 * @private 15479 */ 15480 var _getMsg; 15481 15482 return { 15483 /** 15484 * Provides a message resolver for this utility singleton. 15485 * @param {Function} getMsg 15486 * A function that returns a string given a message key. 15487 * If the key is not found, this function must return 15488 * something that tests false (i.e. undefined or ""). 15489 */ 15490 setGetter : function (getMsg) { 15491 _getMsg = getMsg; 15492 }, 15493 15494 /** 15495 * Resolves the given message key, also performing substitution. 15496 * This generic utility will use a custom function to resolve the key 15497 * provided by finesse.utilities.I18n.setGetter. Otherwise, it will 15498 * discover either finesse.container.I18n.getMsg or gadgets.Prefs.getMsg 15499 * upon the first invocation and store that reference for efficiency. 15500 * 15501 * Since this will construct a new gadgets.Prefs object, it is recommended 15502 * for gadgets to explicitly provide the setter to prevent duplicate 15503 * gadgets.Prefs objects. This does not apply if your gadget does not need 15504 * access to gadgets.Prefs other than getMsg. 15505 * 15506 * @param {String} key 15507 * The key to lookup 15508 * @param {String} arguments 15509 * Arguments for substitution 15510 * @returns {String/Function} 15511 * The resolved string if successful, otherwise a function that returns 15512 * a '???' string that can also be casted into a string. 15513 */ 15514 getString : function (key) { 15515 var prefs, i, retStr, noMsg, getFailed = ""; 15516 if (!_getMsg) { 15517 if (finesse.container && finesse.container.I18n) { 15518 _getMsg = finesse.container.I18n.getMsg; 15519 } else if (gadgets) { 15520 prefs = new gadgets.Prefs(); 15521 _getMsg = prefs.getMsg; 15522 } 15523 } 15524 15525 try { 15526 retStr = _getMsg(key); 15527 } catch (e) { 15528 getFailed = "finesse.utilities.I18n.getString(): invalid _getMsg"; 15529 } 15530 15531 if (retStr) { // Lookup was successful, perform substitution (if any) 15532 for (i = 1; i < arguments.length; i += 1) { 15533 retStr = retStr.replace(new RegExp("\\{" + (i - 1) + "\\}", "g"), arguments[i]); 15534 } 15535 //in order to fix French text with single quotes in it, we need to replace \' with ' 15536 return retStr.replace(/\\'/g, "'"); 15537 } 15538 // We want a function because jQuery.html() and jQuery.text() is smart enough to invoke it. 15539 /** @private */ 15540 noMsg = function () { 15541 return "???" + key + "???" + getFailed; 15542 }; 15543 // We overload the toString() of this "function" to allow JavaScript to cast it into a string 15544 // For example, var myMsg = "something " + finesse.utilities.I18n.getMsg("unresolvable.key"); 15545 /** @private */ 15546 noMsg.toString = function () { 15547 return "???" + key + "???" + getFailed; 15548 }; 15549 return noMsg; 15550 15551 } 15552 }; 15553 }()); 15554 15555 window.finesse = window.finesse || {}; 15556 window.finesse.utilities = window.finesse.utilities || {}; 15557 window.finesse.utilities.I18n = I18n; 15558 15559 return I18n; 15560 }); 15561 15562 /** 15563 * Logging.js: provides simple logging for clients to use and overrides synchronous native methods: alert(), confirm(), and prompt(). 15564 * 15565 * On Firefox, it will hook into console for logging. On IE, it will log to the status bar. 15566 */ 15567 // Add Utilities to the finesse.utilities namespace 15568 define('utilities/Logger',[], function () { 15569 var Logger = (function () { 15570 15571 var 15572 15573 /** @private **/ 15574 debugOn, 15575 15576 /** 15577 * Pads a single digit number for display purposes (e.g. '4' shows as '04') 15578 * @param num is the number to pad to 2 digits 15579 * @returns a two digit padded string 15580 * @private 15581 */ 15582 padTwoDigits = function (num) { 15583 return (num < 10) ? '0' + num : num; 15584 }, 15585 15586 /** 15587 * Checks to see if we have a console - this allows us to support Firefox or IE. 15588 * @returns {Boolean} True for Firefox, False for IE 15589 * @private 15590 */ 15591 hasConsole = function () { 15592 var retval = false; 15593 try 15594 { 15595 if (window.console !== undefined) 15596 { 15597 retval = true; 15598 } 15599 } 15600 catch (err) 15601 { 15602 retval = false; 15603 } 15604 15605 return retval; 15606 }, 15607 15608 /** 15609 * Gets a timestamp. 15610 * @returns {String} is a timestamp in the following format: HH:MM:SS 15611 * @private 15612 */ 15613 getTimeStamp = function () { 15614 var date = new Date(), timeStr; 15615 timeStr = padTwoDigits(date.getHours()) + ":" + padTwoDigits(date.getMinutes()) + ":" + padTwoDigits(date.getSeconds()); 15616 15617 return timeStr; 15618 }; 15619 15620 return { 15621 /** 15622 * Enable debug mode. Debug mode may impact performance on the UI. 15623 * 15624 * @param {Boolean} enable 15625 * True to enable debug logging. 15626 * @private 15627 */ 15628 setDebug : function (enable) { 15629 debugOn = enable; 15630 }, 15631 15632 /** 15633 * Logs a string as DEBUG. 15634 * 15635 * @param str is the string to log. 15636 * @private 15637 */ 15638 log : function (str) { 15639 var timeStr = getTimeStamp(); 15640 15641 if (debugOn) { 15642 if (hasConsole()) 15643 { 15644 window.console.log(timeStr + ": " + "DEBUG" + " - " + str); 15645 } 15646 } 15647 }, 15648 15649 /** 15650 * Logs a string as INFO. 15651 * 15652 * @param str is the string to log. 15653 * @private 15654 */ 15655 info : function (str) { 15656 var timeStr = getTimeStamp(); 15657 15658 if (hasConsole()) 15659 { 15660 window.console.info(timeStr + ": " + "INFO" + " - " + str); 15661 } 15662 }, 15663 15664 /** 15665 * Logs a string as WARN. 15666 * 15667 * @param str is the string to log. 15668 * @private 15669 */ 15670 warn : function (str) { 15671 var timeStr = getTimeStamp(); 15672 15673 if (hasConsole()) 15674 { 15675 window.console.warn(timeStr + ": " + "WARN" + " - " + str); 15676 } 15677 }, 15678 /** 15679 * Logs a string as ERROR. 15680 * 15681 * @param str is the string to log. 15682 * @private 15683 */ 15684 error : function (str) { 15685 var timeStr = getTimeStamp(); 15686 15687 if (hasConsole()) 15688 { 15689 window.console.error(timeStr + ": " + "ERROR" + " - " + str); 15690 } 15691 } 15692 }; 15693 }()); 15694 15695 return Logger; 15696 }); 15697 15698 /** 15699 * Allows gadgets to call the log function to publish client logging messages over the hub. 15700 * 15701 * @requires OpenAjax 15702 */ 15703 /** @private */ 15704 define('cslogger/ClientLogger',[], function () { 15705 15706 var ClientLogger = ( function () { /** @lends finesse.cslogger.ClientLogger.prototype */ 15707 var _hub, _logTopic, _originId, _sessId, _host, 15708 MONTH = { 0 : "Jan", 1 : "Feb", 2 : "Mar", 3 : "Apr", 4 : "May", 5 : "Jun", 15709 6 : "Jul", 7 : "Aug", 8 : "Sep", 9 : "Oct", 10 : "Nov", 11 : "Dec"}, 15710 15711 /** 15712 * Gets timestamp drift stored in sessionStorage 15713 * @returns drift in seconds if it is set in sessionStorage otherwise returns undefined. 15714 * @private 15715 */ 15716 getTsDrift = function() { 15717 if (window.sessionStorage.getItem('clientTimestampDrift') !== null) { 15718 return parseInt(window.sessionStorage.getItem('clientTimestampDrift'), 10); 15719 } 15720 else { 15721 return undefined; 15722 } 15723 }, 15724 15725 /** 15726 * Sets timestamp drift in sessionStorage 15727 * @param delta is the timestamp drift between server.and client. 15728 * @private 15729 */ 15730 setTsDrift = function(delta) { 15731 window.sessionStorage.setItem('clientTimestampDrift', delta.toString()); 15732 }, 15733 15734 /** 15735 * Gets Finesse server timezone offset from GMT in seconds 15736 * @returns offset in seconds if it is set in sessionStorage otherwise returns undefined. 15737 * @private 15738 */ 15739 getServerOffset = function() { 15740 if (window.sessionStorage.getItem('serverTimezoneOffset') !== null) { 15741 return parseInt(window.sessionStorage.getItem('serverTimezoneOffset'), 10); 15742 } 15743 else { 15744 return undefined; 15745 } 15746 }, 15747 15748 /** 15749 * Sets server timezone offset 15750 * @param sec is the server timezone GMT offset in seconds. 15751 * @private 15752 */ 15753 setServerOffset = function(sec) { 15754 window.sessionStorage.setItem('serverTimezoneOffset', sec.toString()); 15755 }, 15756 15757 /** 15758 * Checks to see if we have a console. 15759 * @returns Whether the console object exists. 15760 * @private 15761 */ 15762 hasConsole = function () { 15763 try { 15764 if (window.console !== undefined) { 15765 return true; 15766 } 15767 } 15768 catch (err) { 15769 // ignore and return false 15770 } 15771 15772 return false; 15773 }, 15774 15775 /** 15776 * Gets a short form (6 character) session ID from sessionStorage 15777 * @private 15778 */ 15779 getSessId = function() { 15780 if (!_sessId) { 15781 //when _sessId not defined yet, initiate it 15782 if (window.sessionStorage.getItem('enableLocalLog') === 'true') { 15783 _sessId= " "+window.sessionStorage.getItem('finSessKey'); 15784 } 15785 else { 15786 _sessId=" "; 15787 } 15788 } 15789 return _sessId; 15790 }, 15791 15792 /** 15793 * Pads a single digit number for display purposes (e.g. '4' shows as '04') 15794 * @param num is the number to pad to 2 digits 15795 * @returns a two digit padded string 15796 * @private 15797 */ 15798 padTwoDigits = function (num) 15799 { 15800 return (num < 10) ? '0' + num : num; 15801 }, 15802 15803 /** 15804 * Pads a single digit number for display purposes (e.g. '4' shows as '004') 15805 * @param num is the number to pad to 3 digits 15806 * @returns a three digit padded string 15807 * @private 15808 */ 15809 padThreeDigits = function (num) 15810 { 15811 if (num < 10) 15812 { 15813 return '00'+num; 15814 } 15815 else if (num < 100) 15816 { 15817 return '0'+num; 15818 } 15819 else 15820 { 15821 return num; 15822 } 15823 }, 15824 15825 /** 15826 * Compute the "hour" 15827 * 15828 * @param s is time in seconds 15829 * @returns {String} which is the hour 15830 * @private 15831 */ 15832 ho = function (s) { 15833 return ((s/60).toString()).split(".")[0]; 15834 }, 15835 15836 /** 15837 * Gets local timezone offset string. 15838 * 15839 * @param t is the time in seconds 15840 * @param s is the separator character between hours and minutes, e.g. ':' 15841 * @returns {String} is local timezone GMT offset in the following format: [+|-]hh[|:]MM 15842 * @private 15843 */ 15844 getGmtOffString = function (min,s) { 15845 var t, sign; 15846 if (min<0) { 15847 t = -min; 15848 sign = "-"; 15849 } 15850 else { 15851 t = min; 15852 sign = "+"; 15853 } 15854 15855 if (s===':') { 15856 return sign+padTwoDigits(ho(t))+s+padTwoDigits(t%60); 15857 } 15858 else { 15859 return sign+padTwoDigits(ho(t))+padTwoDigits(t%60); 15860 } 15861 }, 15862 15863 /** 15864 * Gets short form of a month name in English 15865 * 15866 * @param monthNum is zero-based month number 15867 * @returns {String} is short form of month name in English 15868 * @private 15869 */ 15870 getMonthShortStr = function (monthNum) { 15871 var result; 15872 try { 15873 result = MONTH[monthNum]; 15874 } 15875 catch (err) { 15876 if (hasConsole()) { 15877 window.console.log("Month must be between 0 and 11"); 15878 } 15879 } 15880 return result; 15881 }, 15882 15883 /** 15884 * Gets a timestamp. 15885 * @param aDate is a javascript Date object 15886 * @returns {String} is a timestamp in the following format: yyyy-mm-ddTHH:MM:ss.SSS [+|-]HH:MM 15887 * @private 15888 */ 15889 getDateTimeStamp = function (aDate) 15890 { 15891 var date, off, timeStr; 15892 if (aDate === null) { 15893 date = new Date(); 15894 } 15895 else { 15896 date = aDate; 15897 } 15898 off = -1*date.getTimezoneOffset(); 15899 timeStr = date.getFullYear().toString() + "-" + 15900 padTwoDigits(date.getMonth()+1) + "-" + 15901 padTwoDigits (date.getDate()) + "T"+ 15902 padTwoDigits(date.getHours()) + ":" + 15903 padTwoDigits(date.getMinutes()) + ":" + 15904 padTwoDigits(date.getSeconds())+"." + 15905 padThreeDigits(date.getMilliseconds()) + " "+ 15906 getGmtOffString(off, ':'); 15907 15908 return timeStr; 15909 }, 15910 15911 /** 15912 * Gets drift-adjusted timestamp. 15913 * @param aTimestamp is a timestamp in milliseconds 15914 * @param drift is a timestamp drift in milliseconds 15915 * @param serverOffset is a timezone GMT offset in minutes 15916 * @returns {String} is a timestamp in the Finesse server log format, e.g. Jan 07 2104 HH:MM:ss.SSS -0500 15917 * @private 15918 */ 15919 getDriftedDateTimeStamp = function (aTimestamp, drift, serverOffset) 15920 { 15921 var date, timeStr, localOffset; 15922 if (aTimestamp === null) { 15923 return "--- -- ---- --:--:--.--- -----"; 15924 } 15925 else if (drift === undefined || serverOffset === undefined) { 15926 if (hasConsole()) { 15927 window.console.log("drift or serverOffset must be a number"); 15928 } 15929 return "--- -- ---- --:--:--.--- -----"; 15930 } 15931 else { 15932 //need to get a zone diff in minutes 15933 localOffset = (new Date()).getTimezoneOffset(); 15934 date = new Date(aTimestamp+drift+(localOffset+serverOffset)*60000); 15935 timeStr = getMonthShortStr(date.getMonth()) + " "+ 15936 padTwoDigits (date.getDate())+ " "+ 15937 date.getFullYear().toString() + " "+ 15938 padTwoDigits(date.getHours()) + ":" + 15939 padTwoDigits(date.getMinutes()) + ":" + 15940 padTwoDigits(date.getSeconds())+"." + 15941 padThreeDigits(date.getMilliseconds())+" "+ 15942 getGmtOffString(serverOffset, ''); 15943 return timeStr; 15944 } 15945 }, 15946 15947 /** 15948 * Logs a message to a hidden textarea element on the page 15949 * 15950 * @param msg is the string to log. 15951 * @private 15952 */ 15953 writeToLogOutput = function (msg) { 15954 var logOutput = document.getElementById("finesseLogOutput"); 15955 15956 if (logOutput === null) 15957 { 15958 logOutput = document.createElement("textarea"); 15959 logOutput.id = "finesseLogOutput"; 15960 logOutput.style.display = "none"; 15961 document.body.appendChild(logOutput); 15962 } 15963 15964 if (logOutput.value === "") 15965 { 15966 logOutput.value = msg; 15967 } 15968 else 15969 { 15970 logOutput.value = logOutput.value + "\n" + msg; 15971 } 15972 }, 15973 15974 /* 15975 * Logs a message to console 15976 * @param str is the string to log. * @private 15977 */ 15978 logToConsole = function (str) 15979 { 15980 var now, msg, timeStr, driftedTimeStr, sessKey=getSessId(); 15981 now = new Date(); 15982 timeStr = getDateTimeStamp(now); 15983 if (getTsDrift() !== undefined) { 15984 driftedTimeStr = getDriftedDateTimeStamp(now.getTime(), getTsDrift(), getServerOffset()); 15985 } 15986 else { 15987 driftedTimeStr = getDriftedDateTimeStamp(null, 0, 0); 15988 } 15989 msg = timeStr + ":"+sessKey+": "+ _host + ": "+driftedTimeStr+ ": " + str; 15990 // Log to console 15991 if (hasConsole()) { 15992 window.console.log(msg); 15993 } 15994 15995 //Uncomment to print logs to hidden textarea. 15996 //writeToLogOutput(msg); 15997 15998 return msg; 15999 }; 16000 return { 16001 16002 /** 16003 * Publishes a Log Message over the hub. 16004 * 16005 * @param {String} message 16006 * The string to log. 16007 * @example 16008 * _clientLogger.log("This is some important message for MyGadget"); 16009 * 16010 */ 16011 log : function (message) { 16012 if(_hub) { 16013 _hub.publish(_logTopic, logToConsole(_originId + message)); 16014 } 16015 }, 16016 16017 /** 16018 * @class 16019 * Allows gadgets to call the log function to publish client logging messages over the hub. 16020 * 16021 * @constructs 16022 */ 16023 _fakeConstuctor: function () { 16024 /* This is here so we can document init() as a method rather than as a constructor. */ 16025 }, 16026 16027 /** 16028 * Initiates the client logger with a hub a gadgetId and gadget's config object. 16029 * @param {Object} hub 16030 * The hub to communicate with. 16031 * @param {String} gadgetId 16032 * A unique string to identify which gadget is doing the logging. 16033 * @param {finesse.gadget.Config} config 16034 * The config object used to get host name for that thirdparty gadget 16035 * @example 16036 * var _clientLogger = finesse.cslogger.ClientLogger; 16037 * _clientLogger.init(gadgets.Hub, "MyGadgetId", config); 16038 * 16039 */ 16040 init: function (hub, gadgetId, config) { 16041 _hub = hub; 16042 _logTopic = "finesse.clientLogging." + gadgetId; 16043 _originId = gadgetId + " : "; 16044 if ((config === undefined) || (config === "undefined")) 16045 { 16046 _host = ((finesse.container && finesse.container.Config && finesse.container.Config.host)?finesse.container.Config.host : "?.?.?.?"); 16047 } 16048 else 16049 { 16050 _host = ((config && config.host)?config.host : "?.?.?.?"); 16051 } 16052 } 16053 }; 16054 }()); 16055 16056 window.finesse = window.finesse || {}; 16057 window.finesse.cslogger = window.finesse.cslogger || {}; 16058 window.finesse.cslogger.ClientLogger = ClientLogger; 16059 16060 finesse = finesse || {}; 16061 /** @namespace Supports writing messages to a central log. */ 16062 finesse.cslogger = finesse.cslogger || {}; 16063 16064 return ClientLogger; 16065 }); 16066 16067 /* using variables before they are defined. 16068 */ 16069 /*global navigator,unescape,sessionStorage,localStorage,_initSessionList,_initSessionListComplete */ 16070 16071 /** 16072 * Allows each gadget to communicate with the server to send logs. 16073 */ 16074 16075 /** 16076 * @class 16077 * @private 16078 * Allows each product to initialize its method of storage 16079 */ 16080 define('cslogger/FinesseLogger',["clientservices/ClientServices", "utilities/Utilities"], function (ClientServices, Utilities) { 16081 16082 var FinesseLogger = (function () { 16083 16084 var 16085 16086 /** 16087 * Array use to collect ongoing logs in memory 16088 * @private 16089 */ 16090 _logArray = [], 16091 16092 /** 16093 * The final data string sent to the server, =_logArray.join 16094 * @private 16095 */ 16096 _logStr = "", 16097 16098 /** 16099 * Keep track of size of log 16100 * @private 16101 */ 16102 _logSize = 0, 16103 16104 /** 16105 * Flag to keep track show/hide of send log link 16106 * @private 16107 */ 16108 _sendLogShown = false, 16109 16110 /** 16111 * Flag to keep track if local log initialized 16112 * @private 16113 */ 16114 _loggingInitialized = false, 16115 16116 16117 /** 16118 * local log size limit 16119 * @private 16120 */ 16121 _maxLocalStorageSize = 5000000, 16122 16123 /** 16124 * half local log size limit 16125 * @private 16126 */ 16127 _halfMaxLocalStorageSize = 0.5*_maxLocalStorageSize, 16128 16129 16130 /** 16131 * threshold for purge 16132 * @private 16133 */ 16134 _purgeStartPercent = 0.75, 16135 16136 /** 16137 * log item prefix 16138 * @private 16139 */ 16140 _linePrefix = null, 16141 16142 /** 16143 * locallog session 16144 * @private 16145 */ 16146 _session = null, 16147 16148 /** 16149 * Flag to keep track show/hide of send log link 16150 * @private 16151 */ 16152 _sessionKey = null, 16153 /** 16154 * Log session metadata 16155 * @private 16156 */ 16157 _logInfo = {}, 16158 16159 /** 16160 * Flag to find sessions 16161 * @private 16162 */ 16163 _findSessionsObj = null, 16164 16165 /** 16166 * Wrap up console.log esp. for IE9 16167 * @private 16168 */ 16169 _myConsoleLog = function (str) { 16170 if (window.console !== undefined) { 16171 window.console.log(str); 16172 } 16173 }, 16174 /** 16175 * Initialize the Local Logging 16176 * @private 16177 */ 16178 _initLogging = function () { 16179 if (_loggingInitialized) { 16180 return; 16181 } 16182 //Build a new store 16183 _session = sessionStorage.getItem("finSessKey"); 16184 //if the _session is null or empty, skip the init 16185 if (!_session) { 16186 return; 16187 } 16188 _sessionKey = "Fi"+_session; 16189 _linePrefix = _sessionKey + "_"; 16190 _logInfo = {}; 16191 _logInfo.name = _session; 16192 _logInfo.size = 0; 16193 _logInfo.head = 0; 16194 _logInfo.tail = 0; 16195 _logInfo.startTime = new Date().getTime(); 16196 _loggingInitialized = true; 16197 _initSessionList(); 16198 }, 16199 16200 /** 16201 * get total data size 16202 * 16203 * @return {Integer} which is the amount of data stored in local storage. 16204 * @private 16205 */ 16206 _getTotalData = function () 16207 { 16208 var sessName, sessLogInfoStr,sessLogInfoObj, sessionsInfoObj, totalData = 0, 16209 sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo"); 16210 if (!sessionsInfoStr) { 16211 return 0; 16212 } 16213 sessionsInfoObj = JSON.parse(sessionsInfoStr); 16214 16215 for (sessName in sessionsInfoObj.sessions) 16216 { 16217 if (sessionsInfoObj.sessions.hasOwnProperty(sessName)) { 16218 sessLogInfoStr = localStorage.getItem("Fi" + sessName); 16219 if (!sessLogInfoStr) { 16220 _myConsoleLog("_getTotalData failed to get log info for "+sessName); 16221 } 16222 else { 16223 sessLogInfoObj = JSON.parse(sessLogInfoStr); 16224 totalData = totalData + sessLogInfoObj.size; 16225 } 16226 } 16227 } 16228 16229 return totalData; 16230 }, 16231 16232 /** 16233 * Remove lines from tail up until store size decreases to half of max size limit. 16234 * 16235 * @private 16236 */ 16237 _purgeCurrentSession = function() { 16238 var curStoreSize, purgedSize=0, line, tailKey, secLogInfoStr, logInfoStr, theLogInfo; 16239 curStoreSize = _getTotalData(); 16240 if (curStoreSize < _halfMaxLocalStorageSize) { 16241 return; 16242 } 16243 logInfoStr = localStorage.getItem(_sessionKey); 16244 if (!logInfoStr) { 16245 return; 16246 } 16247 theLogInfo = JSON.parse(logInfoStr); 16248 //_myConsoleLog("Starting _purgeCurrentSession() - currentStoreSize=" + curStoreSize); 16249 while(curStoreSize > _halfMaxLocalStorageSize) { 16250 try { 16251 tailKey = _sessionKey+"_"+theLogInfo.tail; 16252 line = localStorage.getItem(tailKey); 16253 if (line) { 16254 purgedSize = purgedSize +line.length; 16255 localStorage.removeItem(tailKey); 16256 curStoreSize = curStoreSize - line.length; 16257 theLogInfo.size = theLogInfo.size - line.length; 16258 } 16259 } 16260 catch (err) { 16261 _myConsoleLog("purgeCurrentSession encountered err="+err); 16262 } 16263 if (theLogInfo.tail < theLogInfo.head) { 16264 theLogInfo.tail = theLogInfo.tail + 1; 16265 } 16266 else { 16267 break; 16268 } 16269 } 16270 //purge stops here, we need to update session's meta data in storage 16271 secLogInfoStr = localStorage.getItem(_sessionKey); 16272 if (!secLogInfoStr) { 16273 //somebody cleared the localStorage 16274 return; 16275 } 16276 16277 //_myConsoleLog("In _purgeCurrentSession() - after purging current session, currentStoreSize=" + curStoreSize); 16278 //_myConsoleLog("In _purgeCurrentSession() - after purging purgedSize=" + purgedSize); 16279 //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.size=" + theLogInfo.size); 16280 //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.tail=" + theLogInfo.tail); 16281 localStorage.setItem(_sessionKey, JSON.stringify(theLogInfo)); 16282 _myConsoleLog("Done _purgeCurrentSession() - currentStoreSize=" + curStoreSize); 16283 }, 16284 16285 /** 16286 * Purge a session 16287 * 16288 * @param sessionName is the name of the session 16289 * @return {Integer} which is the current amount of data purged 16290 * @private 16291 */ 16292 _purgeSession = function (sessionName) { 16293 var theLogInfo, logInfoStr, sessionsInfoStr, sessionsInfoObj; 16294 //Get the session logInfo 16295 logInfoStr = localStorage.getItem("Fi" + sessionName); 16296 if (!logInfoStr) { 16297 _myConsoleLog("_purgeSession failed to get logInfo for "+sessionName); 16298 return 0; 16299 } 16300 theLogInfo = JSON.parse(logInfoStr); 16301 16302 //Note: This assumes that we don't crash in the middle of purging 16303 //=> if we do then it should get deleted next time 16304 //Purge tail->head 16305 while (theLogInfo.tail <= theLogInfo.head) 16306 { 16307 try { 16308 localStorage.removeItem("Fi" + sessionName + "_" + theLogInfo.tail); 16309 theLogInfo.tail = theLogInfo.tail + 1; 16310 } 16311 catch (err) { 16312 _myConsoleLog("In _purgeSession err="+err); 16313 break; 16314 } 16315 } 16316 16317 //Remove the entire session 16318 localStorage.removeItem("Fi" + sessionName); 16319 16320 //Update FinesseSessionsInfo 16321 sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo"); 16322 if (!sessionsInfoStr) { 16323 _myConsoleLog("_purgeSession could not get sessions Info, it was cleared?"); 16324 return 0; 16325 } 16326 sessionsInfoObj = JSON.parse(sessionsInfoStr); 16327 if (sessionsInfoObj.sessions !== null) 16328 { 16329 delete sessionsInfoObj.sessions[sessionName]; 16330 16331 sessionsInfoObj.total = sessionsInfoObj.total - 1; 16332 sessionsInfoObj.lastWrittenBy = _session; 16333 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(sessionsInfoObj)); 16334 } 16335 16336 return theLogInfo.size; 16337 }, 16338 16339 /** 16340 * purge old sessions 16341 * 16342 * @param storeSize 16343 * @return {Boolean} whether purging reaches its target 16344 * @private 16345 */ 16346 _purgeOldSessions = function (storeSize) { 16347 var sessionsInfoStr, purgedSize = 0, sessName, sessions, curStoreSize, activeSession, sessionsInfoObj; 16348 sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo"); 16349 if (!sessionsInfoStr) { 16350 _myConsoleLog("Could not get FinesseSessionsInfo"); 16351 return true; 16352 } 16353 sessionsInfoObj = JSON.parse(sessionsInfoStr); 16354 curStoreSize = _getTotalData(); 16355 16356 activeSession = _session; 16357 sessions = sessionsInfoObj.sessions; 16358 for (sessName in sessions) { 16359 if (sessions.hasOwnProperty(sessName)) { 16360 if (sessName !== activeSession) { 16361 purgedSize = purgedSize + _purgeSession(sessName); 16362 if ((curStoreSize-purgedSize) < _halfMaxLocalStorageSize) { 16363 return true; 16364 } 16365 } 16366 } 16367 } 16368 //purge is not done, so return false 16369 return false; 16370 }, 16371 16372 /** 16373 * handle insert error 16374 * 16375 * @param error 16376 * @private 16377 */ 16378 _insertLineHandleError = function (error) { 16379 _myConsoleLog(error); 16380 }, 16381 16382 /** 16383 * check storage data size and if need purge 16384 * @private 16385 */ 16386 _checkSizeAndPurge = function () { 16387 var purgeIsDone=false, totalSize = _getTotalData(); 16388 if (totalSize > 0.75*_maxLocalStorageSize) { 16389 _myConsoleLog("in _checkSizeAndPurge, totalSize ("+totalSize+") exceeds limit"); 16390 purgeIsDone = _purgeOldSessions(totalSize); 16391 if (purgeIsDone) { 16392 _myConsoleLog("in _checkSizeAndPurge after purging old session, purge is done"); 16393 } 16394 else { 16395 //after all old sessions purged, still need purge 16396 totalSize = _getTotalData(); 16397 if (totalSize > 0.75*_maxLocalStorageSize) { 16398 _myConsoleLog("in _checkSizeAndPurge after purging old session,still needs purging, now storeSize ("+totalSize+")"); 16399 _purgeCurrentSession(); 16400 _myConsoleLog("in _checkSizeAndPurge done purging current session."); 16401 } 16402 } 16403 } 16404 }, 16405 16406 /** 16407 * check if the session is already in meta data 16408 * 16409 * @param metaData 16410 * @param sessionName 16411 * @return {Boolean} true if session has metaData (false otherwise) 16412 * @private 16413 */ 16414 _sessionsInfoContains = function (metaData, sessionName) { 16415 if (metaData && metaData.sessions && metaData.sessions.hasOwnProperty(sessionName)) { 16416 return true; 16417 } 16418 return false; 16419 }, 16420 16421 16422 /** 16423 * setup sessions in local storage 16424 * 16425 * @param logInfo 16426 * @private 16427 */ 16428 _getAndSetNumberOfSessions = function (logInfo) { 16429 var numOfSessionsPass1, numOfSessionsPass2, l; 16430 numOfSessionsPass1 = localStorage.getItem("FinesseSessionsInfo"); 16431 if (numOfSessionsPass1 === null) { 16432 //Init first time 16433 numOfSessionsPass1 = {}; 16434 numOfSessionsPass1.total = 1; 16435 numOfSessionsPass1.sessions = {}; 16436 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime; 16437 numOfSessionsPass1.lastWrittenBy = logInfo.name; 16438 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1)); 16439 } 16440 else { 16441 numOfSessionsPass1 = JSON.parse(numOfSessionsPass1); 16442 //check if the session is already in the FinesseSessionSInfo 16443 if (_sessionsInfoContains(numOfSessionsPass1, logInfo.name)) { 16444 return; 16445 } 16446 //Save numOfSessionsPass1 16447 numOfSessionsPass1.total = parseInt(numOfSessionsPass1.total, 10) + 1; 16448 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime; 16449 numOfSessionsPass1.lastWrittenBy = logInfo.name; 16450 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1)); 16451 numOfSessionsPass2 = localStorage.getItem("FinesseSessionsInfo"); 16452 if (!numOfSessionsPass2) { 16453 _myConsoleLog("Could not get FinesseSessionsInfo"); 16454 return; 16455 } 16456 numOfSessionsPass2 = JSON.parse(numOfSessionsPass2); 16457 //in future we need to confirm the numOfSessionsPass2 is the same as numOfSessionsPass1 16458 ////if (numOfSessionsPass1.lastWrittenBy !== numOfSessionsPass2.lastWrittenBy) { 16459 //// _myConsoleLog("Rebuild sessions"); 16460 //// _sessionTimerId = setTimeout(_initSessionList, 10000); 16461 ////} 16462 ////else { 16463 //// _sessionTimerId = null; 16464 ////callback(numOfSessionsPass2.sessions); 16465 ////} 16466 } 16467 if (!localStorage.getItem(_sessionKey)) { 16468 localStorage.setItem(_sessionKey, JSON.stringify(_logInfo)); 16469 } 16470 }, 16471 16472 16473 /** 16474 * init session list 16475 * @private 16476 */ 16477 _initSessionList = function () { 16478 _getAndSetNumberOfSessions(_logInfo); 16479 }, 16480 16481 /** 16482 * do the real store of log line 16483 * 16484 * @param line 16485 * @private 16486 */ 16487 _persistLine = function (line) { 16488 var key, logInfoStr; 16489 logInfoStr = localStorage.getItem(_sessionKey); 16490 if (logInfoStr === null) { 16491 return; 16492 } 16493 _logInfo = JSON.parse(logInfoStr); 16494 _logInfo.head = _logInfo.head + 1; 16495 key = _linePrefix + _logInfo.head; 16496 localStorage.setItem(key, line); 16497 //Save the size 16498 _logInfo.size = _logInfo.size + line.length; 16499 if (_logInfo.tail === 0) { 16500 _logInfo.tail = _logInfo.head; 16501 } 16502 16503 localStorage.setItem(_sessionKey, JSON.stringify(_logInfo)); 16504 _checkSizeAndPurge(); 16505 }, 16506 16507 /** 16508 * Insert a line into the localStorage. 16509 * 16510 * @param line line to be inserted 16511 * @private 16512 */ 16513 _insertLine = function (line) { 16514 //_myConsoleLog("_insertLine: [" + line + "]"); 16515 //Write the next line to localStorage 16516 try { 16517 //Persist the line 16518 _persistLine(line); 16519 } 16520 catch (err) { 16521 _myConsoleLog("error in _insertLine(), err="+err); 16522 //_insertLineHandleError(err); 16523 } 16524 }, 16525 16526 16527 /** 16528 * Clear the local storage 16529 * @private 16530 */ 16531 _clearLocalStorage = function() { 16532 localStorage.clear(); 16533 16534 }, 16535 16536 /** 16537 * Collect logs when onCollect called 16538 * 16539 * @param data 16540 * @private 16541 */ 16542 _collectMethod = function(data) { 16543 //Size of log should not exceed 1.5MB 16544 var info, maxLength = 1572864; 16545 16546 //add size buffer equal to the size of info to be added when publish 16547 info = Utilities.getSanitizedUserAgentString() + " "; 16548 info = escape(info); 16549 16550 //If log was empty previously, fade in buttons 16551 if (!_sendLogShown) { 16552 //call the fadeInSendLog() in Footer 16553 finesse.modules.Footer.sendLogAppear(); 16554 _sendLogShown = true; 16555 _logSize = info.length; 16556 } 16557 16558 //if local storage logging is enabled, then insert the log into local storage 16559 if (window.sessionStorage.getItem('enableLocalLog')==='true') { 16560 if (data) { 16561 if (data.length>0 && data.substring(0,1) === '\n') { 16562 _insertLine(data.substring(1)); 16563 } 16564 else { 16565 _insertLine(data); 16566 } 16567 } 16568 } 16569 16570 //escape all data to get accurate size (shindig will escape when it builds request) 16571 //escape 6 special chars for XML: &<>"'\n 16572 data = data.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/>/g, ">").replace(/</g, "<").replace(/\n/g, " "); 16573 data = escape(data+"\n"); 16574 16575 if (data.length < maxLength){ 16576 //make room for new data if log is exceeding max length 16577 while (_logSize + data.length > maxLength) { 16578 _logSize -= (_logArray.shift()).length; 16579 } 16580 } 16581 16582 //Else push the log into memory, increment the log size 16583 _logArray.push(data); 16584 16585 //inc the size accordingly 16586 _logSize+=data.length; 16587 16588 }; 16589 16590 return { 16591 16592 /** 16593 * @private 16594 * Initiate FinesseLogger. 16595 */ 16596 init: function () { 16597 ClientServices.subscribe("finesse.clientLogging.*", _collectMethod); 16598 _initLogging(); 16599 }, 16600 16601 /** 16602 * @private 16603 * Clear all items stored in localStorage. 16604 */ 16605 clear : function () { 16606 _clearLocalStorage(); 16607 }, 16608 16609 /** 16610 * @private 16611 * Initialize the local storage logging. 16612 */ 16613 initLocalLog: function () { 16614 _initLogging(); 16615 }, 16616 16617 /** 16618 * @private 16619 * Inserts a line into the localStorage. 16620 * @param line to insert 16621 */ 16622 localLog : function (line) { 16623 _insertLine(line); 16624 }, 16625 16626 /** 16627 * @ignore 16628 * Publish logs to server and clear the memory 16629 * 16630 * @param userObj 16631 * @param options 16632 * @param callBack 16633 */ 16634 publish: function(userObj, options, callBack) { 16635 // Avoid null references. 16636 options = options || {}; 16637 callBack = callBack || {}; 16638 16639 if (callBack.sending === "function") { 16640 callBack.sending(); 16641 } 16642 16643 //logs the basic version and machine info and escaped new line 16644 _logStr = Utilities.getSanitizedUserAgentString() + " "; 16645 16646 //join the logs to correct string format 16647 _logStr += unescape(_logArray.join("")); 16648 16649 //turning log string to JSON obj 16650 var logObj = { 16651 ClientLog: { 16652 logData : _logStr //_logStr 16653 } 16654 }, 16655 tmpOnAdd = (options.onAdd && typeof options.onAdd === "function")? options.onAdd : function(){}; 16656 /** @private */ 16657 options.onAdd = function(){ 16658 tmpOnAdd(); 16659 _logArray.length = 0; _logSize =0; 16660 _sendLogShown = false; 16661 }; 16662 //adding onLoad to the callbacks, this is the subscribe success case for the first time user subscribe to the client log node 16663 /** @private */ 16664 options.onLoad = function (clientLogObj) { 16665 clientLogObj.sendLogs(logObj,{ 16666 error: callBack.error 16667 }); 16668 }; 16669 16670 userObj.getClientLog(options); 16671 } 16672 }; 16673 }()); 16674 16675 window.finesse = window.finesse || {}; 16676 window.finesse.cslogger = window.finesse.cslogger || {}; 16677 /** @private */ 16678 window.finesse.cslogger.FinesseLogger = FinesseLogger; 16679 16680 return FinesseLogger; 16681 }); 16682 16683 /** 16684 * Contains a list of topics used for containerservices pubsub. 16685 * 16686 */ 16687 16688 /** 16689 * @class 16690 * Contains a list of topics with some utility functions. 16691 */ 16692 /** @private */ 16693 define('containerservices/Topics',[], function () { 16694 16695 var Topics = (function () { 16696 16697 /** 16698 * The namespace prepended to all Finesse topics. 16699 */ 16700 this.namespace = "finesse.containerservices"; 16701 16702 /** 16703 * @private 16704 * Gets the full topic name with the ContainerServices namespace prepended. 16705 * @param {String} topic 16706 * The topic category. 16707 * @returns {String} 16708 * The full topic name with prepended namespace. 16709 */ 16710 var _getNSTopic = function (topic) { 16711 return this.namespace + "." + topic; 16712 }; 16713 16714 16715 16716 /** @scope finesse.containerservices.Topics */ 16717 return { 16718 /** 16719 * @private 16720 * request channel. */ 16721 REQUESTS: _getNSTopic("requests"), 16722 16723 /** 16724 * @private 16725 * reload gadget channel. */ 16726 RELOAD_GADGET: _getNSTopic("reloadGadget"), 16727 16728 /** 16729 * @private 16730 * Convert a Finesse REST URI to a OpenAjax compatible topic name. 16731 */ 16732 getTopic: function (restUri) { 16733 //The topic should not start with '/' else it will get replaced with 16734 //'.' which is invalid. 16735 //Thus, remove '/' if it is at the beginning of the string 16736 if (restUri.indexOf('/') === 0) { 16737 restUri = restUri.substr(1); 16738 } 16739 16740 //Replace every instance of "/" with ".". This is done to follow the 16741 //OpenAjaxHub topic name convention. 16742 return restUri.replace(/\//g, "."); 16743 } 16744 }; 16745 }()); 16746 16747 window.finesse = window.finesse || {}; 16748 window.finesse.containerservices = window.finesse.containerservices || {}; 16749 window.finesse.containerservices.Topics = Topics; 16750 16751 /** @namespace JavaScript class objects and methods to handle gadget container services.*/ 16752 finesse.containerservices = finesse.containerservices || {}; 16753 16754 return Topics; 16755 }); 16756 16757 /** The following comment is to prevent jslint errors about 16758 * using variables before they are defined. 16759 */ 16760 /*global finesse*/ 16761 16762 /** 16763 * Per containerservices request, publish to the OpenAjax gadget pubsub infrastructure. 16764 * 16765 * @requires OpenAjax, finesse.containerservices.Topics 16766 */ 16767 16768 /** @private */ 16769 define('containerservices/MasterPublisher',[ 16770 "utilities/Utilities", 16771 "containerservices/Topics" 16772 ], 16773 function (Utilities, Topics) { 16774 16775 var MasterPublisher = function () { 16776 16777 var 16778 16779 /** 16780 * Reference to the gadget pubsub Hub instance. 16781 * @private 16782 */ 16783 _hub = gadgets.Hub, 16784 16785 /** 16786 * Reference to the Topics class. 16787 * @private 16788 */ 16789 _topics = Topics, 16790 16791 /** 16792 * Reference to conversion utilities class. 16793 * @private 16794 */ 16795 _utils = Utilities, 16796 16797 /** 16798 * References to ClientServices logger methods 16799 * @private 16800 */ 16801 _logger = { 16802 log: finesse.clientservices.ClientServices.log 16803 }, 16804 16805 /** 16806 * The types of possible request types supported when listening to the 16807 * requests channel. Each request type could result in different operations. 16808 * @private 16809 */ 16810 _REQTYPES = { 16811 ACTIVETAB: "ActiveTabReq", 16812 SET_ACTIVETAB: "SetActiveTabReq", 16813 RELOAD_GADGET: "ReloadGadgetReq" 16814 }, 16815 16816 /** 16817 * Handles client requests made to the request topic. The type of the 16818 * request is described in the "type" property within the data payload. Each 16819 * type can result in a different operation. 16820 * @param {String} topic 16821 * The topic which data was published to. 16822 * @param {Object} data 16823 * The data containing requests information published by clients. 16824 * @param {String} data.type 16825 * The type of the request. Supported: "ActiveTabReq", "SetActiveTabReq", "ReloadGadgetReq" 16826 * @param {Object} data.data 16827 * May contain data relevant for the particular requests. 16828 * @param {String} [data.invokeID] 16829 * The ID used to identify the request with the response. The invoke ID 16830 * will be included in the data in the publish to the topic. It is the 16831 * responsibility of the client to correlate the published data to the 16832 * request made by using the invoke ID. 16833 * @private 16834 */ 16835 _clientRequestHandler = function (topic, data) { 16836 16837 //Ensure a valid data object with "type" and "data" properties. 16838 if (typeof data === "object" && 16839 typeof data.type === "string" && 16840 typeof data.data === "object") { 16841 switch (data.type) { 16842 case _REQTYPES.ACTIVETAB: 16843 _hub.publish("finesse.containerservices.activeTab", finesse.container.Tabs.getActiveTab()); 16844 break; 16845 case _REQTYPES.SET_ACTIVETAB: 16846 if (typeof data.data.id === "string") { 16847 _logger.log("Handling request to activate tab: " + data.data.id); 16848 if (!finesse.container.Tabs.activateTab(data.data.id)) { 16849 _logger.log("No tab found with id: " + data.data.id); 16850 } 16851 } 16852 break; 16853 case _REQTYPES.RELOAD_GADGET: 16854 _hub.publish("finesse.containerservices.reloadGadget", data.data); 16855 break; 16856 default: 16857 break; 16858 } 16859 } 16860 }; 16861 16862 (function () { 16863 16864 //Listen to a request channel to respond to any requests made by other 16865 //clients because the Master may have access to useful information. 16866 _hub.subscribe(_topics.REQUESTS, _clientRequestHandler); 16867 }()); 16868 16869 //BEGIN TEST CODE// 16870 /** 16871 * Test code added to expose private functions that are used by unit test 16872 * framework. This section of code is removed during the build process 16873 * before packaging production code. The [begin|end]TestSection are used 16874 * by the build to identify the section to strip. 16875 * @ignore 16876 */ 16877 this.beginTestSection = 0; 16878 16879 /** 16880 * @ignore 16881 */ 16882 this.getTestObject = function () { 16883 //Load mock dependencies. 16884 var _mock = new MockControl(); 16885 _hub = _mock.createMock(gadgets.Hub); 16886 16887 return { 16888 //Expose mock dependencies 16889 mock: _mock, 16890 hub: _hub, 16891 16892 //Expose internal private functions 16893 reqtypes: _REQTYPES, 16894 16895 clientRequestHandler: _clientRequestHandler 16896 16897 }; 16898 }; 16899 16900 16901 /** 16902 * @ignore 16903 */ 16904 this.endTestSection = 0; 16905 //END TEST CODE// 16906 }; 16907 16908 window.finesse = window.finesse || {}; 16909 window.finesse.containerservices = window.finesse.containerservices || {}; 16910 window.finesse.containerservices.MasterPublisher = MasterPublisher; 16911 16912 return MasterPublisher; 16913 }); 16914 16915 /** 16916 * JavaScript representation of the Finesse WorkflowActionEvent object. 16917 * 16918 * @requires finesse.FinesseBase 16919 */ 16920 16921 /** The following comment is to prevent jslint errors about 16922 * using variables before they are defined. 16923 */ 16924 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */ 16925 /** @private */ 16926 define('containerservices/WorkflowActionEvent', ["FinesseBase"], function (FinesseBase) { 16927 var WorkflowActionEvent = FinesseBase.extend(/** @lends finesse.containerservices.WorkflowActionEvent.prototype */{ 16928 /** 16929 * Reference to the WorkflowActionEvent name 16930 * This will be set by setWorkflowActionEvent 16931 * @private 16932 */ 16933 _name: null, 16934 16935 /** 16936 * Reference to the WorkflowActionEvent type 16937 * This will be set by setWorkflowActionEvent 16938 * @private 16939 */ 16940 _type: null, 16941 16942 /** 16943 * Reference to the WorkflowActionEvent handledBy value 16944 * This will be set by setWorkflowActionEvent 16945 * @private 16946 */ 16947 _handledBy: null, 16948 16949 /** 16950 * Reference to the WorkflowActionEvent params array 16951 * This will be set by setWorkflowActionEvent 16952 * @private 16953 */ 16954 _params: [], 16955 16956 /** 16957 * Reference to the WorkflowActionEvent actionVariables array 16958 * This will be set by setWorkflowActionEvent 16959 * @private 16960 */ 16961 _actionVariables: [], 16962 16963 /** 16964 * @class 16965 * JavaScript representation of a WorkflowActionEvent object. 16966 * The WorkflowActionEvent object is delivered as the payload of 16967 * a WorkflowAction callback. This can be subscribed to by using 16968 * {@link finesse.containerservices.ContainerServices#addHandler} with a 16969 * topic of {@link finesse.containerservices.ContainerServices.Topics#WORKFLOW_ACTION_EVENT}. 16970 * Gadgets should key on events with a handleBy value of "OTHER". 16971 * 16972 * @constructs 16973 **/ 16974 init: function () { 16975 this._super(); 16976 }, 16977 16978 /** 16979 * Validate that the passed in object is a WorkflowActionEvent object 16980 * and sets the variables if it is 16981 * @param maybeWorkflowActionEvent A possible WorkflowActionEvent object to be evaluated and set if 16982 * it validates successfully. 16983 * @returns {Boolean} Whether it is valid or not. 16984 * @private 16985 */ 16986 setWorkflowActionEvent: function(maybeWorkflowActionEvent) { 16987 var returnValue; 16988 16989 if (maybeWorkflowActionEvent.hasOwnProperty("name") === true && 16990 maybeWorkflowActionEvent.hasOwnProperty("type") === true && 16991 maybeWorkflowActionEvent.hasOwnProperty("handledBy") === true && 16992 maybeWorkflowActionEvent.hasOwnProperty("params") === true && 16993 maybeWorkflowActionEvent.hasOwnProperty("actionVariables") === true) { 16994 this._name = maybeWorkflowActionEvent.name; 16995 this._type = maybeWorkflowActionEvent.type; 16996 this._handledBy = maybeWorkflowActionEvent.handledBy; 16997 this._params = maybeWorkflowActionEvent.params; 16998 this._actionVariables = maybeWorkflowActionEvent.actionVariables; 16999 returnValue = true; 17000 } else { 17001 returnValue = false; 17002 } 17003 17004 return returnValue; 17005 }, 17006 17007 /** 17008 * Getter for the WorkflowActionEvent name. 17009 * @returns {String} The name of the WorkflowAction. 17010 */ 17011 getName: function () { 17012 // escape nulls to empty string 17013 return this._name || ""; 17014 }, 17015 17016 /** 17017 * Getter for the WorkflowActionEvent type. 17018 * @returns {String} The type of the WorkflowAction (BROWSER_POP, HTTP_REQUEST). 17019 */ 17020 getType: function () { 17021 // escape nulls to empty string 17022 return this._type || ""; 17023 }, 17024 17025 /** 17026 * Getter for the WorkflowActionEvent handledBy value. Gadgets should look for 17027 * events with a handleBy of "OTHER". 17028 * @see finesse.containerservices.WorkflowActionEvent.HandledBy 17029 * @returns {String} The handledBy value of the WorkflowAction that is a value of {@link finesse.containerservices.WorkflowActionEvent.HandledBy}. 17030 */ 17031 getHandledBy: function () { 17032 // escape nulls to empty string 17033 return this._handledBy || ""; 17034 }, 17035 17036 17037 /** 17038 * Getter for the WorkflowActionEvent Params map. 17039 * @returns {Object} key = param name, value = Object{name, value, expandedValue} 17040 * BROWSER_POP<ul> 17041 * <li>windowName : Name of window to pop into, or blank to always open new window. 17042 * <li>path : URL to open.</ul> 17043 * HTTP_REQUEST<ul> 17044 * <li>method : "PUT" or "POST". 17045 * <li>location : "FINESSE" or "OTHER". 17046 * <li>contentType : MIME type of request body, if applicable, e.g. "text/plain". 17047 * <li>path : Request URL. 17048 * <li>body : Request content for POST requests.</ul> 17049 */ 17050 getParams: function () { 17051 var map = {}, 17052 params = this._params, 17053 i, 17054 param; 17055 17056 if (params === null || params.length === 0) { 17057 return map; 17058 } 17059 17060 for (i = 0; i < params.length; i += 1) { 17061 param = params[i]; 17062 // escape nulls to empty string 17063 param.name = param.name || ""; 17064 param.value = param.value || ""; 17065 param.expandedValue = param.expandedValue || ""; 17066 map[param.name] = param; 17067 } 17068 17069 return map; 17070 }, 17071 17072 /** 17073 * Getter for the WorkflowActionEvent ActionVariables map 17074 * @returns {Object} key = action variable name, value = Object{name, type, node, testValue, actualValue} 17075 */ 17076 getActionVariables: function() { 17077 var map = {}, 17078 actionVariables = this._actionVariables, 17079 i, 17080 actionVariable; 17081 17082 if (actionVariables === null || actionVariables.length === 0) { 17083 return map; 17084 } 17085 17086 for (i = 0; i < actionVariables.length; i += 1) { 17087 actionVariable = actionVariables[i]; 17088 // escape nulls to empty string 17089 actionVariable.name = actionVariable.name || ""; 17090 actionVariable.type = actionVariable.type || ""; 17091 actionVariable.node = actionVariable.node || ""; 17092 actionVariable.testValue = actionVariable.testValue || ""; 17093 actionVariable.actualValue = actionVariable.actualValue || ""; 17094 map[actionVariable.name] = actionVariable; 17095 } 17096 17097 return map; 17098 } 17099 }); 17100 17101 17102 WorkflowActionEvent.HandledBy = /** @lends finesse.containerservices.WorkflowActionEvent.HandledBy.prototype */ { 17103 /** 17104 * This specifies that Finesse will handle this WorkflowActionEvent. A 3rd Party can do additional processing 17105 * with the action, but first and foremost Finesse will handle this WorkflowAction. 17106 */ 17107 FINESSE: "FINESSE", 17108 17109 /** 17110 * This specifies that a 3rd Party will handle this WorkflowActionEvent. Finesse's Workflow Engine Executor will 17111 * ignore this action and expects Gadget Developers to take action. 17112 */ 17113 OTHER: "OTHER", 17114 17115 /** 17116 * @class This is the set of possible HandledBy values used for WorkflowActionEvent from ContainerServices. This 17117 * is provided from the {@link finesse.containerservices.WorkflowActionEvent#getHandledBy} method. 17118 * @constructs 17119 */ 17120 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 17121 }; 17122 17123 window.finesse = window.finesse || {}; 17124 window.finesse.containerservices = window.finesse.containerservices || {}; 17125 window.finesse.containerservices.WorkflowActionEvent = WorkflowActionEvent; 17126 17127 return WorkflowActionEvent; 17128 }); 17129 17130 /** 17131 * JavaScript representation of the Finesse TimerTickEvent 17132 * 17133 * @requires finesse.FinesseBase 17134 */ 17135 17136 /** The following comment is to prevent jslint errors about 17137 * using variables before they are defined. 17138 */ 17139 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */ 17140 /** @private */ 17141 define('containerservices/TimerTickEvent',[ 17142 "FinesseBase" 17143 ], 17144 function (FinesseBase) { 17145 var TimerTickEvent = FinesseBase.extend(/** @lends finesse.containerservices.TimerTickEvent.prototype */{ 17146 /** 17147 * date the TimerTickEvent was queued 17148 * @private 17149 */ 17150 _dateQueued: null, 17151 17152 /** 17153 * the frequency of the timer tick (in miiliseconds) 17154 * @private 17155 */ 17156 _tickFrequency: 1000, 17157 17158 /** 17159 * @class 17160 * JavaScript representation of a TimerTickEvent object. 17161 * The TimerTickEvent object is delivered as the payload of 17162 * a TimerTickEvent callback. This can be subscribed to by using 17163 * {@link finesse.containerservices.ContainerServices#addHandler} with a 17164 * topic of {@link finesse.containerservices.ContainerServices.Topics#TIMER_TICK_EVENT}. 17165 * 17166 * @constructs 17167 **/ 17168 init: function (tickFrequency, dateQueued) { 17169 this._super(); 17170 17171 this._tickFrequency = tickFrequency; 17172 this._dateQueued = dateQueued; 17173 }, 17174 17175 /** 17176 * Get the "tickFrequency" field 17177 * @param {int} which is the "TickFrequency" field 17178 * @private 17179 */ 17180 getTickFrequency: function () { 17181 return this._tickFrequency; 17182 }, 17183 17184 /** 17185 * Getter for the TimerTickEvent "DateQueued" field. 17186 * @returns {Date} which is a Date object when the TimerTickEvent was queued 17187 */ 17188 getDateQueued: function () { 17189 return this._dateQueued; 17190 } 17191 17192 }); 17193 17194 window.finesse = window.finesse || {}; 17195 window.finesse.containerservices = window.finesse.containerservices || {}; 17196 window.finesse.containerservices.TimerTickEvent = TimerTickEvent; 17197 17198 return TimerTickEvent; 17199 }); 17200 17201 /** 17202 * JavaScript representation of the Finesse GadgetViewChangedEvent object. 17203 * 17204 * @requires finesse.FinesseBase 17205 */ 17206 17207 /** The following comment is to prevent jslint errors about 17208 * using variables before they are defined. 17209 */ 17210 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */ 17211 /** @private */ 17212 define('containerservices/GadgetViewChangedEvent',[ 17213 "FinesseBase" 17214 ], 17215 function (FinesseBase) { 17216 var GadgetViewChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.GadgetViewChangedEvent.prototype */{ 17217 /** 17218 * Reference to the gadget id 17219 * @private 17220 */ 17221 _gadgetId: null, 17222 17223 /** 17224 * Reference to the tab id 17225 * @private 17226 */ 17227 _tabId: null, 17228 17229 /** 17230 * Reference to the maxAvailableHeight 17231 * @private 17232 */ 17233 _maxAvailableHeight: null, 17234 17235 /** 17236 * Reference to the view 17237 * E.g. 'default' or 'canvas' 17238 * @private 17239 */ 17240 _view: null, 17241 17242 /** 17243 * @class 17244 * JavaScript representation of a GadgetViewChangedEvent object. 17245 * The GadgetViewChangedEvent object is delivered as the payload of 17246 * a GadgetViewChangedEvent callback. This can be subscribed to by using 17247 * {@link finesse.containerservices.ContainerServices#addHandler} with a 17248 * topic of {@link finesse.containerservices.ContainerServices.Topics#GADGET_VIEW_CHANGED_EVENT}. 17249 * 17250 * @constructs 17251 **/ 17252 init: function (gadgetId, tabId, maxAvailableHeight, view) { 17253 this._super(); 17254 17255 this._gadgetId = gadgetId; 17256 this._tabId = tabId; 17257 this._maxAvailableHeight = maxAvailableHeight; 17258 this._view = view; 17259 }, 17260 17261 /** 17262 * Getter for the gadget id. 17263 * @returns {String} The identifier for the gadget changing view. 17264 */ 17265 getGadgetId: function () { 17266 // escape nulls to empty string 17267 return this._gadgetId || ""; 17268 }, 17269 17270 /** 17271 * Getter for the maximum available height. 17272 * @returns {String} The maximum available height for the gadget's view. 17273 */ 17274 getMaxAvailableHeight: function () { 17275 // escape nulls to empty string 17276 return this._maxAvailableHeight || ""; 17277 }, 17278 17279 /** 17280 * Getter for the tab id. 17281 * @returns {String} The identifier for the tab where the gadget changing view resides. 17282 */ 17283 getTabId: function () { 17284 // escape nulls to empty string 17285 return this._tabId || ""; 17286 }, 17287 17288 /** 17289 * Getter for the view. 17290 * @returns {String} The view type the gadget is changing to. 17291 */ 17292 getView: function () { 17293 // escape nulls to empty string 17294 return this._view || ""; 17295 } 17296 }); 17297 17298 window.finesse = window.finesse || {}; 17299 window.finesse.containerservices = window.finesse.containerservices || {}; 17300 window.finesse.containerservices.GadgetViewChangedEvent = GadgetViewChangedEvent; 17301 17302 return GadgetViewChangedEvent; 17303 }); 17304 17305 /** 17306 * JavaScript representation of the Finesse MaxAvailableHeightChangedEvent object. 17307 * 17308 * @requires finesse.FinesseBase 17309 */ 17310 17311 /** The following comment is to prevent jslint errors about 17312 * using variables before they are defined. 17313 */ 17314 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */ 17315 /** @private */ 17316 define('containerservices/MaxAvailableHeightChangedEvent',[ 17317 "FinesseBase" 17318 ], 17319 function (FinesseBase) { 17320 var MaxAvailableHeightChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.MaxAvailableHeightChangedEvent.prototype */{ 17321 17322 /** 17323 * Reference to the maxAvailableHeight 17324 * @private 17325 */ 17326 _maxAvailableHeight: null, 17327 17328 /** 17329 * @class 17330 * JavaScript representation of a MaxAvailableHeightChangedEvent object. 17331 * The MaxAvailableHeightChangedEvent object is delivered as the payload of 17332 * a MaxAvailableHeightChangedEvent callback. This can be subscribed to by using 17333 * {@link finesse.containerservices.ContainerServices#addHandler} with a 17334 * topic of {@link finesse.containerservices.ContainerServices.Topics#MAX_AVAILABLE_HEIGHT_CHANGED_EVENT}. 17335 * 17336 * @constructs 17337 **/ 17338 init: function (maxAvailableHeight) { 17339 this._super(); 17340 17341 this._maxAvailableHeight = maxAvailableHeight; 17342 }, 17343 17344 /** 17345 * Getter for the maximum available height. 17346 * @returns {String} The maximum available height for a gadget in canvas view 17347 */ 17348 getMaxAvailableHeight: function () { 17349 // escape nulls to empty string 17350 return this._maxAvailableHeight || ""; 17351 } 17352 }); 17353 17354 window.finesse = window.finesse || {}; 17355 window.finesse.containerservices = window.finesse.containerservices || {}; 17356 window.finesse.containerservices.MaxAvailableHeightChangedEvent = MaxAvailableHeightChangedEvent; 17357 17358 return MaxAvailableHeightChangedEvent; 17359 }); 17360 17361 /** 17362 * Exposes a set of API wrappers that will hide the dirty work of 17363 * constructing Finesse API requests and consuming Finesse events. 17364 * 17365 * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities 17366 */ 17367 17368 /** The following comment is to prevent jslint errors about using variables before they are defined. */ 17369 /*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 */ 17370 /*jslint nomen: true, unparam: true, sloppy: true, white: true */ 17371 /** @private */ 17372 define('containerservices/ContainerServices',[ 17373 "utilities/Utilities", 17374 "restservices/Notifier", 17375 "containerservices/Topics", 17376 "containerservices/MasterPublisher", 17377 "containerservices/WorkflowActionEvent", 17378 "containerservices/TimerTickEvent", 17379 "containerservices/GadgetViewChangedEvent", 17380 "containerservices/MaxAvailableHeightChangedEvent" 17381 ], 17382 function (Utilities, Notifier, Topics, MasterPublisher, WorkflowActionEvent) { 17383 17384 var ContainerServices = ( function () { /** @lends finesse.containerservices.ContainerServices.prototype */ 17385 17386 var 17387 17388 /** 17389 * Shortcut reference to the Utilities singleton 17390 * This will be set by init() 17391 * @private 17392 */ 17393 _util, 17394 17395 /** 17396 * Shortcut reference to the gadget pubsub Hub instance. 17397 * This will be set by init() 17398 * @private 17399 */ 17400 _hub, 17401 17402 /** 17403 * Boolean whether this instance is master or not 17404 * @private 17405 */ 17406 _master = false, 17407 17408 /** 17409 * Whether the Client Services have been initiated yet. 17410 * @private 17411 */ 17412 _inited = false, 17413 17414 /** 17415 * References to ClientServices logger methods 17416 * @private 17417 */ 17418 _logger = { 17419 log: finesse.clientservices.ClientServices.log 17420 }, 17421 17422 /** 17423 * Stores the list of subscription IDs for all subscriptions so that it 17424 * could be retrieve for unsubscriptions. 17425 * @private 17426 */ 17427 _subscriptionID = {}, 17428 17429 /** 17430 * Reference to the gadget's parent container 17431 * @private 17432 */ 17433 _container, 17434 17435 /** 17436 * Reference to the MasterPublisher 17437 * @private 17438 */ 17439 _publisher, 17440 17441 /** 17442 * Object that will contain the Notifiers 17443 * @private 17444 */ 17445 _notifiers = {}, 17446 17447 /** 17448 * Reference to the tabId that is associated with the gadget 17449 * @private 17450 */ 17451 _myTab = null, 17452 17453 /** 17454 * Reference to the visibility of current gadget 17455 * @private 17456 */ 17457 _visible = false, 17458 17459 /** 17460 * Shortcut reference to the Topics class. 17461 * This will be set by init() 17462 * @private 17463 */ 17464 _topics, 17465 17466 /** 17467 * Associates a topic name with the private handler function. 17468 * Adding a new topic requires that you add this association here 17469 * in to keep addHandler generic. 17470 * @param {String} topic : Specifies the callback to retrieve 17471 * @return {Function} The callback function associated with the topic param. 17472 * @private 17473 */ 17474 _topicCallback = function (topic) { 17475 var callback, notifier; 17476 switch (topic) 17477 { 17478 case finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB: 17479 callback = _tabTracker; 17480 break; 17481 case finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT: 17482 callback = _workflowActionEventTracker; 17483 break; 17484 case finesse.containerservices.ContainerServices.Topics.RELOAD_GADGET_EVENT: 17485 callback = _masterReloader; 17486 break; 17487 case finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT: 17488 callback = _gadgetViewChanged; 17489 break; 17490 case finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT: 17491 callback = _maxAvailableHeightChanged; 17492 break; 17493 default: 17494 callback = function (param) { 17495 var data = null; 17496 17497 notifier = _getNotifierReference(topic); 17498 17499 if (arguments.length === 1) { 17500 data = param; 17501 } else { 17502 data = arguments; 17503 } 17504 notifier.notifyListeners(data); 17505 }; 17506 } 17507 return callback; 17508 }, 17509 17510 /** 17511 * Ensure that ClientServices have been inited. 17512 * @private 17513 */ 17514 _isInited = function () { 17515 if (!_inited) { 17516 throw new Error("ContainerServices needs to be inited."); 17517 } 17518 }, 17519 17520 /** 17521 * Retrieves a Notifier reference to a particular topic, and creates one if it doesn't exist. 17522 * @param {String} topic : Specifies the notifier to retrieve 17523 * @return {Notifier} The notifier object. 17524 * @private 17525 */ 17526 _getNotifierReference = function (topic) { 17527 if (!_notifiers.hasOwnProperty(topic)) 17528 { 17529 _notifiers[topic] = new Notifier(); 17530 } 17531 17532 return _notifiers[topic]; 17533 }, 17534 17535 /** 17536 * Utility function to make a subscription to a particular topic. Only one 17537 * callback function is registered to a particular topic at any time. 17538 * @param {String} topic 17539 * The full topic name. The topic name should follow the OpenAjax 17540 * convention using dot notation (ex: finesse.api.User.1000). 17541 * @param {Function} callback 17542 * The function that should be invoked with the data when an event 17543 * is delivered to the specific topic. 17544 * @returns {Boolean} 17545 * True if the subscription was made successfully and the callback was 17546 * been registered. False if the subscription already exist, the 17547 * callback was not overwritten. 17548 * @private 17549 */ 17550 _subscribe = function (topic, callback) { 17551 _isInited(); 17552 17553 //Ensure that the same subscription isn't made twice. 17554 if (!_subscriptionID[topic]) { 17555 //Store the subscription ID using the topic name as the key. 17556 _subscriptionID[topic] = _hub.subscribe(topic, 17557 //Invoke the callback just with the data object. 17558 function (topic, data) { 17559 callback(data); 17560 }); 17561 return true; 17562 } 17563 return false; 17564 }, 17565 17566 /** 17567 * Unsubscribe from a particular topic. 17568 * @param {String} topic : The full topic name. 17569 * @private 17570 */ 17571 _unsubscribe = function (topic) { 17572 _isInited(); 17573 17574 //Unsubscribe from the topic using the subscription ID recorded when 17575 //the subscription was made, then delete the ID from data structure. 17576 _hub.unsubscribe(_subscriptionID[topic]); 17577 delete _subscriptionID[topic]; 17578 }, 17579 17580 /** 17581 * Get my tab id. 17582 * @returns {String} tabid : The tabid of this container/gadget. 17583 * @private 17584 */ 17585 _getMyTab = function () { 17586 if (_myTab === null) 17587 { 17588 try { 17589 _myTab = $(frameElement).closest("div.tab-panel").attr("id").replace("panel_", ""); 17590 } catch (err) { 17591 _logger.log("Error accessing current tab: " + err.message); 17592 _myTab = null; 17593 } 17594 } 17595 return _myTab; 17596 }, 17597 17598 /** 17599 * Callback function that is called when an activeTab message is posted to the Hub. 17600 * Notifies listener functions if this tab is the one that was just made active. 17601 * @param {String} tabId : The tabId which was just made visible. 17602 * @private 17603 */ 17604 _tabTracker = function(tabId) { 17605 if (tabId === _getMyTab()) { 17606 if(!_visible) { 17607 _visible = true; 17608 _notifiers[finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB].notifyListeners(this); 17609 } 17610 } else { 17611 _visible = false; 17612 } 17613 }, 17614 17615 /** 17616 * Make a request to set a particular tab active. This 17617 * method should be called after {@link finesse.containerservices.ContainerServices#addHandler} 17618 * to ensure the gadget gets properly initialized. 17619 * @param {String} tabId 17620 * The tabId (not the label text) of the tab to make active. If the id is invalid, no action will occur. 17621 * @private 17622 */ 17623 _activateTab = function ( tabId ) { 17624 _logger.log("Sending request to activate tab: " + tabId); 17625 if(_hub){ 17626 var data = { 17627 type: "SetActiveTabReq", 17628 data: { id: tabId }, 17629 invokeID: (new Date()).getTime() 17630 }; 17631 _hub.publish(_topics.REQUESTS, data); 17632 } else { 17633 throw new Error("Hub is not defined."); 17634 } 17635 17636 }, 17637 17638 /** 17639 * Callback function that is called when a gadget view changed message is posted to the Hub. 17640 * @private 17641 */ 17642 _gadgetViewChanged = function (data) { 17643 if (data) { 17644 var gadgetViewChangedEvent = new finesse.containerservices.GadgetViewChangedEvent( 17645 data.gadgetId, 17646 data.tabId, 17647 data.maxAvailableHeight, 17648 data.view); 17649 17650 _notifiers[finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT].notifyListeners(gadgetViewChangedEvent); 17651 } 17652 }, 17653 17654 /** 17655 * Callback function that is called when a max available height changed message is posted to the Hub. 17656 * @private 17657 */ 17658 _maxAvailableHeightChanged = function (data) { 17659 if (data) { 17660 var maxAvailableHeightChangedEvent = new finesse.containerservices.MaxAvailableHeightChangedEvent( 17661 data.maxAvailableHeight); 17662 17663 _notifiers[finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT].notifyListeners(maxAvailableHeightChangedEvent); 17664 } 17665 }, 17666 17667 /** 17668 * Callback function that is called when a workflowActionEvent message is posted to the Hub. 17669 * Notifies listener functions if the posted object can be converted to a proper WorkflowActionEvent object. 17670 * @param {String} workflowActionEvent : The workflowActionEvent that was posted to the Hub 17671 * @private 17672 */ 17673 _workflowActionEventTracker = function(workflowActionEvent) { 17674 var vWorkflowActionEvent = new finesse.containerservices.WorkflowActionEvent(); 17675 17676 if (vWorkflowActionEvent.setWorkflowActionEvent(workflowActionEvent)) { 17677 _notifiers[finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT].notifyListeners(vWorkflowActionEvent); 17678 } 17679 // else 17680 // { 17681 //?console.log("Error in ContainerServices : _workflowActionEventTracker - could not map published HUB object to WorkflowActionEvent"); 17682 // } 17683 17684 }, 17685 17686 /** 17687 * Callback function that is called when a reloadGadget event message is posted to the Hub. 17688 * 17689 * Grabs the id of the gadget we want to reload from the data and reload it! 17690 * 17691 * @param {String} topic 17692 * which topic the event came on (unused) 17693 * @param {Object} data 17694 * the data published with the event 17695 * @private 17696 */ 17697 _masterReloader = function (topic, data) { 17698 var gadgetId = data.gadgetId; 17699 if (gadgetId) { 17700 _container.reloadGadget(gadgetId); 17701 } 17702 }, 17703 17704 /** 17705 * Pulls the gadget id from the url parameters 17706 * @return {String} id of the gadget 17707 * @private 17708 */ 17709 _findMyGadgetId = function () { 17710 if (gadgets && gadgets.util && gadgets.util.getUrlParameters()) { 17711 return gadgets.util.getUrlParameters().mid; 17712 } 17713 }; 17714 17715 return { 17716 /** 17717 * @class 17718 * This class provides container-level services for gadget developers, exposing container events by 17719 * calling a set of exposed functions. Gadgets can utilize the container dialogs and 17720 * event handling (add/remove). 17721 * @example 17722 * containerServices = finesse.containerservices.ContainerServices.init(); 17723 * containerServices.addHandler( 17724 * finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, 17725 * function() { 17726 * clientLogs.log("Gadget is now visible"); // log to Finesse logger 17727 * // automatically adjust the height of the gadget to show the html 17728 * gadgets.window.adjustHeight(); 17729 * }); 17730 * containerServices.makeActiveTabReq(); 17731 * 17732 * @constructs 17733 */ 17734 _fakeConstuctor: function () { 17735 /* This is here so we can document init() as a method rather than as a constructor. */ 17736 }, 17737 17738 /** 17739 * Initialize ContainerServices for use in gadget. 17740 * @param {Boolean} [master=false] Do not use this parameter from your gadget. 17741 * @returns ContainerServices instance. 17742 */ 17743 init: function (master) { 17744 if (!_inited) { 17745 _inited = true; 17746 // Set shortcuts 17747 _util = Utilities; 17748 17749 //init the hub only when it's available 17750 if(gadgets.Hub) { 17751 _hub = gadgets.Hub; 17752 } 17753 17754 if(Topics) { 17755 _topics = Topics; 17756 } 17757 17758 if (master) { 17759 _master = true; 17760 _container = finesse.container.Container; 17761 _publisher = new MasterPublisher(); 17762 17763 // subscribe for reloading gadget events 17764 // we only want the master ContainerServices handling these events 17765 _hub.subscribe(_topics.RELOAD_GADGET, _topicCallback(_topics.RELOAD_GADGET)); 17766 } else { 17767 _container = parent.finesse.container.Container; 17768 } 17769 } 17770 17771 this.makeActiveTabReq(); 17772 17773 //Return the CS object for object chaining. 17774 return this; 17775 }, 17776 17777 /** 17778 * Shows the jQuery UI Dialog with the specified parameters. The following are the 17779 * default parameters: <ul> 17780 * <li> Title of "Cisco Finesse".</li> 17781 * <li>Message of "A generic error has occured".</li> 17782 * <li>The only button, "Ok", closes the dialog.</li> 17783 * <li>Modal (blocks other dialogs).</li> 17784 * <li>Not draggable.</li> 17785 * <li>Fixed size.</li></ul> 17786 * @param {Object} options 17787 * An object containing additional options for the dialog. 17788 * @param {String/Boolean} options.title 17789 * Title to use. undefined defaults to "Cisco Finesse". false to hide 17790 * @param {Function} options.close 17791 * A function to invoke when the dialog is closed. 17792 * @param {String} options.message 17793 * The message to display in the dialog. 17794 * Defaults to "A generic error has occurred." 17795 * @param {Boolean} options.isBlocking 17796 * Flag indicating whether this dialog will block other dialogs from being shown (Modal). 17797 * @returns {jQuery} JQuery wrapped object of the dialog DOM element. 17798 * @see finesse.containerservices.ContainerServices#hideDialog 17799 */ 17800 showDialog: function(options) { 17801 if ((_container.showDialog !== undefined) && (_container.showDialog !== this.showDialog)) { 17802 return _container.showDialog(options); 17803 } 17804 }, 17805 17806 /** 17807 * Hides the jQuery UI Dialog. 17808 * @returns {jQuery} jQuery wrapped object of the dialog DOM element 17809 * @see finesse.containerservices.ContainerServices#showDialog 17810 */ 17811 hideDialog: function() { 17812 if ((_container.hideDialog !== undefined) && (_container.hideDialog !== this.hideDialog)) { 17813 return _container.hideDialog(); 17814 } 17815 }, 17816 17817 /** 17818 * Reloads the current gadget. 17819 * For use from within a gadget only. 17820 */ 17821 reloadMyGadget: function () { 17822 var topic, gadgetId, data; 17823 17824 if (!_master) { 17825 // first unsubscribe this gadget from all topics on the hub 17826 for (topic in _notifiers) { 17827 if (_notifiers.hasOwnProperty(topic)) { 17828 _unsubscribe(topic); 17829 delete _notifiers[topic]; 17830 } 17831 } 17832 17833 // send an asynch request to the hub to tell the master container 17834 // services that we want to refresh this gadget 17835 gadgetId = _findMyGadgetId(); 17836 data = { 17837 type: "ReloadGadgetReq", 17838 data: {gadgetId: gadgetId}, 17839 invokeID: (new Date()).getTime() 17840 }; 17841 _hub.publish(_topics.REQUESTS, data); 17842 } 17843 }, 17844 17845 /** 17846 * Updates the url for this gadget and then reload it. 17847 * 17848 * This allows the gadget to be reloaded from a different location 17849 * than what is uploaded to the current server. For example, this 17850 * would be useful for 3rd party gadgets to implement their own failover 17851 * mechanisms. 17852 * 17853 * For use from within a gadget only. 17854 * 17855 * @param {String} url 17856 * url from which to reload gadget 17857 */ 17858 reloadMyGadgetFromUrl: function (url) { 17859 if (!_master) { 17860 var gadgetId = _findMyGadgetId(); 17861 17862 // update the url in the container 17863 _container.modifyGadgetUrl(gadgetId, url); 17864 17865 // reload it 17866 this.reloadMyGadget(); 17867 } 17868 }, 17869 17870 /** 17871 * Adds a handler for one of the supported topics provided by ContainerServices. The callbacks provided 17872 * will be invoked when that topic is notified. 17873 * @param {String} topic 17874 * The Hub topic to which we are listening. 17875 * @param {Function} callback 17876 * The callback function to invoke. 17877 * @see finesse.containerservices.ContainerServices.Topics 17878 * @see finesse.containerservices.ContainerServices#removeHandler 17879 */ 17880 addHandler: function (topic, callback) { 17881 _isInited(); 17882 var notifier = null; 17883 17884 try { 17885 // For backwards compatibility... 17886 if (topic === "tabVisible") { 17887 if (window.console && typeof window.console.log === "function") { 17888 window.console.log("WARNING - Using tabVisible as topic. This is deprecated. Use finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB now!"); 17889 } 17890 17891 topic = finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB; 17892 } 17893 17894 // Add the callback to the notifier. 17895 _util.validateHandler(callback); 17896 17897 notifier = _getNotifierReference(topic); 17898 17899 notifier.addListener(callback); 17900 17901 // Subscribe to the topic. _subscribe ensures that a topic is only subscribed to once, 17902 // so attempt to subscribe each time a handler is added. This ensures that a topic is subscribed 17903 // to only when necessary. 17904 _subscribe(topic, _topicCallback(topic)); 17905 17906 } catch (err) { 17907 throw new Error("addHandler(): " + err); 17908 } 17909 }, 17910 17911 /** 17912 * Removes a previously-added handler for one of the supported topics. 17913 * @param {String} topic 17914 * The Hub topic from which we are removing the callback. 17915 * @param {Function} callback 17916 * The name of the callback function to remove. 17917 * @see finesse.containerservices.ContainerServices.Topics 17918 * @see finesse.containerservices.ContainerServices#addHandler 17919 */ 17920 removeHandler: function(topic, callback) { 17921 var notifier = null; 17922 17923 try { 17924 _util.validateHandler(callback); 17925 17926 notifier = _getNotifierReference(topic); 17927 17928 notifier.removeListener(callback); 17929 } catch (err) { 17930 throw new Error("removeHandler(): " + err); 17931 } 17932 }, 17933 17934 /** 17935 * Returns the visibility of current gadget. Note that this 17936 * will not be set until after the initialization of the gadget. 17937 * @return {Boolean} The visibility of current gadget. 17938 */ 17939 tabVisible: function(){ 17940 return _visible; 17941 }, 17942 17943 /** 17944 * Make a request to check the current tab. The 17945 * activeTab event will be invoked if on the active tab. This 17946 * method should be called after {@link finesse.containerservices.ContainerServices#addHandler} 17947 * to ensure the gadget gets properly initialized. 17948 */ 17949 makeActiveTabReq : function () { 17950 if(_hub){ 17951 var data = { 17952 type: "ActiveTabReq", 17953 data: {}, 17954 invokeID: (new Date()).getTime() 17955 }; 17956 _hub.publish(_topics.REQUESTS, data); 17957 } else { 17958 throw new Error("Hub is not defined."); 17959 } 17960 17961 }, 17962 17963 /** 17964 * Make a request to set a particular tab active. This 17965 * method should be called after {@link finesse.containerservices.ContainerServices#addHandler} 17966 * to ensure the gadget gets properly initialized. 17967 * @param {String} tabId 17968 * The tabId (not the label text) of the tab to make active. If the id is invalid, no action will occur. 17969 */ 17970 activateTab : function (tabId) { 17971 _activateTab(tabId); 17972 }, 17973 17974 /** 17975 * Make a request to set this container's tab active. This 17976 * method should be called after {@link finesse.containerservices.ContainerServices#addHandler} 17977 * to ensure the gadget gets properly initialized. 17978 */ 17979 activateMyTab : function () { 17980 _activateTab( _getMyTab() ); 17981 }, 17982 17983 /** 17984 * Get the tabId of my container/gadget. 17985 * @returns {String} tabid : The tabid of this container/gadget. 17986 */ 17987 getMyTabId : function () { 17988 return _getMyTab(); 17989 }, 17990 17991 /** 17992 * Gets the id of the gadget. 17993 * @returns {number} the id of the gadget 17994 */ 17995 getMyGadgetId : function () { 17996 return _findMyGadgetId(); 17997 }, 17998 17999 //BEGIN TEST CODE// 18000 /** 18001 * Test code added to expose private functions that are used by unit test 18002 * framework. This section of code is removed during the build process 18003 * before packaging production code. The [begin|end]TestSection are used 18004 * by the build to identify the section to strip. 18005 * @ignore 18006 */ 18007 beginTestSection : 0, 18008 18009 /** 18010 * @ignore 18011 */ 18012 getTestObject: function () { 18013 //Load mock dependencies. 18014 var _mock = new MockControl(); 18015 _util = _mock.createMock(Utilities); 18016 _hub = _mock.createMock(gadgets.Hub); 18017 _inited = true; 18018 return { 18019 //Expose mock dependencies 18020 mock: _mock, 18021 hub: _hub, 18022 util: _util, 18023 addHandler: this.addHandler, 18024 removeHandler: this.removeHandler 18025 }; 18026 }, 18027 18028 /** 18029 * @ignore 18030 */ 18031 endTestSection: 0 18032 //END TEST CODE// 18033 }; 18034 }()); 18035 18036 ContainerServices.Topics = /** @lends finesse.containerservices.ContainerServices.Topics.prototype */ { 18037 /** 18038 * Topic for subscribing to be notified when the active tab changes. 18039 * The provided callback will be invoked when the tab that the gadget 18040 * that subscribes with this becomes active. To ensure code is called 18041 * when the gadget is already on the active tab use the 18042 * {@link finesse.containerservices.ContainerServices#makeActiveTabReq} 18043 * method. 18044 */ 18045 ACTIVE_TAB: "finesse.containerservices.activeTab", 18046 18047 /** 18048 * Topic for WorkflowAction events traffic. 18049 * The provided callback will be invoked when a WorkflowAction needs 18050 * to be handled. The callback will be passed a {@link finesse.containerservices.WorkflowActionEvent} 18051 * that can be used to interrogate the WorkflowAction and determine to use or not. 18052 */ 18053 WORKFLOW_ACTION_EVENT: "finesse.containerservices.workflowActionEvent", 18054 18055 /** 18056 * Topic for Timer Tick event. 18057 * The provided callback will be invoked when this event is fired. 18058 * The callback will be passed a {@link finesse.containerservices.TimerTickEvent}. 18059 */ 18060 TIMER_TICK_EVENT : "finesse.containerservices.timerTickEvent", 18061 18062 /** 18063 * Topic for Reload Gadget events traffic. 18064 * Only the master ContainerServices instance will handle this event. 18065 */ 18066 RELOAD_GADGET_EVENT: "finesse.containerservices.reloadGadget", 18067 18068 /** 18069 * Topic for listening to gadget view changed events. 18070 * The provided callback will be invoked when a gadget changes view. 18071 * The callback will be passed a {@link finesse.containerservices.GadgetViewChangedEvent}. 18072 */ 18073 GADGET_VIEW_CHANGED_EVENT: "finesse.containerservices.gadgetViewChangedEvent", 18074 18075 /** 18076 * Topic for listening to max available height changed events. 18077 * The provided callback will be invoked when the maximum height available to a maximized gadget changes. 18078 * This event is only meant for maximized gadgets and will not be published unless a maximized gadget exists. 18079 * The callback will be passed a {@link finesse.containerservices.MaxAvailableHeightChangedEvent}. 18080 */ 18081 MAX_AVAILABLE_HEIGHT_CHANGED_EVENT: "finesse.containerservices.maxAvailableHeightChangedEvent", 18082 18083 /** 18084 * @class This is the set of Topics used for subscribing for events from ContainerServices. 18085 * Use {@link finesse.containerservices.ContainerServices#addHandler} to subscribe to the topic. 18086 * 18087 * @constructs 18088 */ 18089 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 18090 }; 18091 18092 window.finesse = window.finesse || {}; 18093 window.finesse.containerservices = window.finesse.containerservices || {}; 18094 window.finesse.containerservices.ContainerServices = ContainerServices; 18095 18096 return ContainerServices; 18097 }); 18098 18099 /** 18100 * This "interface" is just a way to easily jsdoc the Object callback handlers. 18101 * 18102 * @requires finesse.clientservices.ClientServices 18103 * @requires Class 18104 */ 18105 /** @private */ 18106 define('interfaces/RestObjectHandlers',[ 18107 "FinesseBase", 18108 "utilities/Utilities", 18109 "restservices/Notifier", 18110 "clientservices/ClientServices", 18111 "clientservices/Topics" 18112 ], 18113 function () { 18114 18115 var RestObjectHandlers = ( function () { /** @lends finesse.interfaces.RestObjectHandlers.prototype */ 18116 18117 return { 18118 18119 /** 18120 * @class 18121 * This "interface" defines REST Object callback handlers, passed as an argument to 18122 * Object getter methods in cases where the Object is going to be created. 18123 * 18124 * @param {Object} [handlers] 18125 * An object containing callback handlers for instantiation and runtime 18126 * Callback to invoke upon successful instantiation, passes in REST object. 18127 * @param {Function} [handlers.onLoad(this)] 18128 * Callback to invoke upon loading the data for the first time. 18129 * @param {Function} [handlers.onChange(this)] 18130 * Callback to invoke upon successful update object (PUT) 18131 * @param {Function} [handlers.onAdd(this)] 18132 * Callback to invoke upon successful update to add object (POST) 18133 * @param {Function} [handlers.onDelete(this)] 18134 * Callback to invoke upon successful update to delete object (DELETE) 18135 * @param {Function} [handlers.onError(rsp)] 18136 * Callback to invoke on update error (refresh or event) 18137 * as passed by finesse.restservices.RestBase.restRequest()<br> 18138 * {<br> 18139 * status: {Number} The HTTP status code returned<br> 18140 * content: {String} Raw string of response<br> 18141 * object: {Object} Parsed object of response<br> 18142 * error: {Object} Wrapped exception that was caught<br> 18143 * error.errorType: {String} Type of error that was caught<br> 18144 * error.errorMessage: {String} Message associated with error<br> 18145 * }<br> 18146 * <br> 18147 * Note that RestCollections have two additional callback handlers:<br> 18148 * <br> 18149 * @param {Function} [handlers.onCollectionAdd(this)]: when an object is added to this collection 18150 * @param {Function} [handlers.onCollectionDelete(this)]: when an object is removed from this collection 18151 18152 * @constructs 18153 */ 18154 _fakeConstuctor: function () { 18155 /* This is here to enable jsdoc to document this as a class. */ 18156 } 18157 }; 18158 }()); 18159 18160 window.finesse = window.finesse || {}; 18161 window.finesse.interfaces = window.finesse.interfaces || {}; 18162 window.finesse.interfaces.RestObjectHandlers = RestObjectHandlers; 18163 18164 return RestObjectHandlers; 18165 18166 }); 18167 18168 18169 /** 18170 * This "interface" is just a way to easily jsdoc the REST request handlers. 18171 * 18172 * @requires finesse.clientservices.ClientServices 18173 * @requires Class 18174 */ 18175 /** @private */ 18176 define('interfaces/RequestHandlers',[ 18177 "FinesseBase", 18178 "utilities/Utilities", 18179 "restservices/Notifier", 18180 "clientservices/ClientServices", 18181 "clientservices/Topics" 18182 ], 18183 function () { 18184 18185 var RequestHandlers = ( function () { /** @lends finesse.interfaces.RequestHandlers.prototype */ 18186 18187 return { 18188 18189 /** 18190 * @class 18191 * This "interface" defines REST Object callback handlers, passed as an argument to 18192 * Object getter methods in cases where the Object is going to be created. 18193 * 18194 * @param {Object} handlers 18195 * An object containing the following (optional) handlers for the request:<ul> 18196 * <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following 18197 * response object as its only parameter:<ul> 18198 * <li><b>status:</b> {Number} The HTTP status code returned</li> 18199 * <li><b>content:</b> {String} Raw string of response</li> 18200 * <li><b>object:</b> {Object} Parsed object of response</li></ul> 18201 * <li><b>error(rsp):</b> An error callback function for an unsuccessful request to be invoked with the 18202 * error response object as its only parameter:<ul> 18203 * <li><b>status:</b> {Number} The HTTP status code returned</li> 18204 * <li><b>content:</b> {String} Raw string of response</li> 18205 * <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li> 18206 * <li><b>error:</b> {Object} Wrapped exception that was caught:<ul> 18207 * <li><b>errorType:</b> {String} Type of error that was caught</li> 18208 * <li><b>errorMessage:</b> {String} Message associated with error</li> 18209 * </ul></li> 18210 * </ul> 18211 18212 * @constructs 18213 */ 18214 _fakeConstuctor: function () { 18215 /* This is here to enable jsdoc to document this as a class. */ 18216 } 18217 }; 18218 }()); 18219 18220 window.finesse = window.finesse || {}; 18221 window.finesse.interfaces = window.finesse.interfaces || {}; 18222 window.finesse.interfaces.RequestHandlers = RequestHandlers; 18223 18224 finesse = finesse || {}; 18225 /** @namespace These interfaces are just a convenience for documenting common parameter structures. */ 18226 finesse.interfaces = finesse.interfaces || {}; 18227 18228 return RequestHandlers; 18229 18230 }); 18231 18232 18233 18234 define('gadget/Config',[ 18235 "utilities/Utilities" 18236 ], function (Utilities) { 18237 var Config = (function () { /** @lends finesse.gadget.Config.prototype */ 18238 18239 if (gadgets && gadgets.Prefs) { 18240 18241 var _prefs = new gadgets.Prefs(); 18242 18243 return { 18244 /** 18245 * The base64 encoded "id:password" string used for authentication. 18246 */ 18247 authorization: Utilities.getUserAuthString(), 18248 18249 /** 18250 * The country code of the client (derived from locale). 18251 */ 18252 country: _prefs.getString("country"), 18253 18254 /** 18255 * The language code of the client (derived from locale). 18256 */ 18257 language: _prefs.getString("language"), 18258 18259 /** 18260 * The locale of the client. 18261 */ 18262 locale: _prefs.getString("locale"), 18263 18264 /** 18265 * The Finesse server IP/host as reachable from the browser. 18266 */ 18267 host: _prefs.getString("host"), 18268 18269 /** 18270 * The Finesse server host's port reachable from the browser. 18271 */ 18272 hostPort: _prefs.getString("hostPort"), 18273 18274 /** 18275 * The extension of the user. 18276 */ 18277 extension: _prefs.getString("extension"), 18278 18279 /** 18280 * One of the work modes found in {@link finesse.restservices.User.WorkMode}, or something false (undefined) for a normal login. 18281 */ 18282 mobileAgentMode: _prefs.getString("mobileAgentMode"), 18283 18284 /** 18285 * The dial number to use for mobile agent, or something false (undefined) for a normal login. 18286 */ 18287 mobileAgentDialNumber: _prefs.getString("mobileAgentDialNumber"), 18288 18289 /** 18290 * The domain of the XMPP server. 18291 */ 18292 xmppDomain: _prefs.getString("xmppDomain"), 18293 18294 /** 18295 * The pub sub domain where the pub sub service is running. 18296 */ 18297 pubsubDomain: _prefs.getString("pubsubDomain"), 18298 18299 /** 18300 * The Finesse API IP/host as reachable from the gadget container. 18301 */ 18302 restHost: _prefs.getString("restHost"), 18303 18304 /** 18305 * The type of HTTP protocol (http or https). 18306 */ 18307 scheme: _prefs.getString("scheme"), 18308 18309 /** 18310 * The localhost fully qualified domain name. 18311 */ 18312 localhostFQDN: _prefs.getString("localhostFQDN"), 18313 18314 /** 18315 * The localhost port. 18316 */ 18317 localhostPort: _prefs.getString("localhostPort"), 18318 18319 /** 18320 * The id of the team the user belongs to. 18321 */ 18322 teamId: _prefs.getString("teamId"), 18323 18324 /** 18325 * The name of the team the user belongs to. 18326 */ 18327 teamName: _prefs.getString("teamName"), 18328 18329 /** 18330 * The drift time between the client and the server in milliseconds. 18331 */ 18332 clientDriftInMillis: _prefs.getInt("clientDriftInMillis"), 18333 18334 /** 18335 * The client compatibility mode configuration (true if it is or false otherwise). 18336 */ 18337 compatibilityMode: _prefs.getString("compatibilityMode"), 18338 18339 /** 18340 * The peripheral Id that Finesse is connected to. 18341 */ 18342 peripheralId: _prefs.getString("peripheralId"), 18343 18344 /** 18345 * @class 18346 * The Config object for gadgets within the Finesse desktop container which 18347 * contains configuration data provided by the container page. 18348 * @constructs 18349 */ 18350 _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly 18351 18352 }; 18353 } else { 18354 return {}; 18355 } 18356 }()); 18357 18358 /** Assign to container and gadget namespace to have config available in both */ 18359 window.finesse = window.finesse || {}; 18360 window.finesse.container = window.finesse.container || {}; 18361 window.finesse.container.Config = window.finesse.container.Config || Config; 18362 18363 window.finesse.gadget = window.finesse.gadget || {}; 18364 window.finesse.gadget.Config = Config; 18365 18366 return Config; 18367 }); 18368 define('finesse',[ 18369 'restservices/Users', 18370 'restservices/Teams', 18371 'restservices/SystemInfo', 18372 'utilities/I18n', 18373 'utilities/Logger', 18374 'utilities/SaxParser', 18375 'cslogger/ClientLogger', 18376 'cslogger/FinesseLogger', 18377 'containerservices/ContainerServices', 18378 'interfaces/RestObjectHandlers', 18379 'interfaces/RequestHandlers', 18380 'gadget/Config' 18381 ], 18382 function () { 18383 return window.finesse; 18384 }); 18385 18386 require(["finesse"]); 18387 return require('finesse'); })); 18388 18389 // Prevent other JS files from wiping out window.finesse from the namespace 18390 var finesse = window.finesse;