1 /**
  2  * Cisco Finesse - JavaScript Library
  3  * Version 12.5.1)
  4  * Cisco Systems, Inc.
  5  * http://www.cisco.com/
  6  *
  7  * Portions created or assigned to Cisco Systems, Inc. are
  8  * Copyright (c) 2020 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      var 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('utilities/../../thirdparty/util/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('utilities/../../thirdparty/util/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     "./SaxParser",
1242     "../../thirdparty/util/iso8601",
1243     "../../thirdparty/util/Math.uuid"
1244 ],
1245 function (Converter, SaxParser) {
1246     var Utilities = /** @lends finesse.utilities.Utilities */ {
1247     	/**
1248     	 * @private
1249     	 */	
1250     	CONSTANTS: {
1251 			CACHED_REST_DATA: 'CACHED_REST_DATA',
1252 			RELOAD_OTHER_SIDE: 'RELOAD_OTHER_SIDE',
1253 			LOG_COLL_DATA: 'LOG_COLL_DATA',
1254 			CHANGED_LAYOUT_STRUCTURE: 'CHANGED_LAYOUT_STRUCTURE',
1255 			PREVIOUS_LAYOUT_HASH_ID: 'PREVIOUS_LAYOUT_HASH_ID',
1256 			PREVIOUS_CD_LAYOUT: 'PREVIOUS_CD_LAYOUT',
1257 			MESSAGES_DATA: 'MESSAGES_DATA',
1258 			USER_ID: 'USER_ID'
1259     	},
1260         IDB_ACTIONS: {
1261             SAVE_OR_UPDATE: 'saveOrUpdate',
1262             FETCH: 'fetch',
1263             DELETE: 'delete',
1264             CLEAR_ALL: 'clear'
1265         },
1266         IDB_TOPICS: {
1267             REQUEST: 'finesse.idb.request',
1268             RESPONSE: 'finesse.idb.response'
1269         },
1270         /**
1271          * @class
1272          * Utilities is collection of utility methods.
1273          * 
1274          * @augments finesse.restservices.RestBase
1275          * @see finesse.restservices.Contacts
1276          * @constructs
1277          */
1278         _fakeConstuctor: function () {
1279             /* This is here for jsdocs. */
1280         },
1281             
1282         /**
1283          * @private
1284          * Retrieves the specified item from window.localStorage
1285          * @param {String} key
1286          *     The key of the item to retrieve
1287          * @returns {String}
1288          *     The string with the value of the retrieved item; returns
1289          *     what the browser would return if not found (typically null or undefined)
1290          *     Returns false if window.localStorage feature is not even found.
1291          */
1292         getDOMStoreItem: function (key) {
1293             var store = window.localStorage;
1294             if (store) {
1295                 return store.getItem(key);
1296             }
1297         },
1298 
1299         /**
1300          * @private
1301          * Sets an item into window.localStorage
1302          * @param {String} key
1303          *     The key for the item to set
1304          * @param {String} value
1305          *     The value to set
1306          * @returns {Boolean}
1307          *     True if successful, false if window.localStorage is
1308          *     not even found.
1309          */
1310         setDOMStoreItem: function (key, value) {
1311             var store = window.localStorage;
1312             if (store) {
1313                 store.setItem(key, value);
1314                 return true;
1315             }
1316             return false;
1317         },
1318 
1319         /**
1320          * @private
1321          * Removes a particular item from window.localStorage
1322          * @param {String} key
1323          *     The key of the item to remove
1324          * @returns {Boolean}
1325          *     True if successful, false if not
1326          *     Returns false if window.localStorage feature is not even found.
1327          */
1328         removeDOMStoreItem: function (key) {
1329             var store = window.localStorage;
1330             if (store) {
1331                 store.removeItem(key);
1332                 return true;
1333             }
1334             return false;
1335         },
1336 
1337         /**
1338          * @private
1339          * Dumps all the contents of window.localStorage
1340          * @returns {Boolean}
1341          *     True if successful, false if not.
1342          *     Returns false if window.localStorage feature is not even found.
1343          */
1344         clearDOMStore: function () {
1345             var store = window.localStorage;
1346             if (store) {
1347                 store.clear();
1348                 return true;
1349             }
1350             return false;
1351         },
1352         
1353         /**
1354          * @private
1355          * Creates a message listener for window.postMessage messages.
1356          * @param {Function} callback
1357          *     The callback that will be invoked with the message. The callback
1358          *     is responsible for any security checks.
1359          * @param {String} [origin]
1360          *     The origin to check against for security. Allows all messages
1361          *     if no origin is provided.
1362          * @returns {Function}
1363          *     The callback function used to register with the message listener.
1364          *     This is different than the one provided as a parameter because
1365          *     the function is overloaded with origin checks.
1366          * @throws {Error} If the callback provided is not a function.
1367          */
1368         receiveMessage: function (callback, origin) {
1369             if (typeof callback !== "function") {
1370                 throw new Error("Callback is not a function.");
1371             }
1372 
1373             //Create a function closure to perform origin check.
1374             /** @private */
1375             var cb = function (e) {
1376                 // If an origin check is requested (provided), we'll only invoke the callback if it passes
1377                 if (typeof origin !== "string" || (typeof origin === "string" && typeof e.origin === "string" && e.origin.toLowerCase() === origin.toLowerCase())) {
1378                     callback(e);
1379                 }
1380             };
1381 
1382             if (window.addEventListener) { //Firefox, Opera, Chrome, Safari
1383                 window.addEventListener("message", cb, false);
1384             } else { //Internet Explorer
1385                 window.attachEvent("onmessage", cb);
1386             }
1387 
1388             //Return callback used to register with listener so that invoker
1389             //could use it to remove.
1390             return cb;
1391         },
1392 
1393         /**
1394          * Funtion to return if the browser is IE or Edge
1395          */
1396         isIEorEdge: function () {
1397             var isIE = navigator.userAgent.indexOf('Trident') !== -1 || navigator.userAgent.indexOf('MSIE ') > 0;
1398             var isEDGE = navigator.appName === 'Netscape' && navigator.appVersion.indexOf('Edge') > -1;
1399             return (isIE || isEDGE);
1400         },
1401 
1402         /**
1403          * @private
1404          * Sends a message to a target frame using window.postMessage.
1405          * @param {Function} message
1406          *     Message to be sent to target frame.
1407          * @param {Object} [target="parent"]
1408          *     An object reference to the target frame. Default us the parent.
1409          * @param {String} [targetOrigin="*"]
1410          *     The URL of the frame this frame is sending the message to.
1411          */
1412         sendMessage: function (message, target, targetOrigin) {
1413             //Default to any target URL if none is specified.
1414             targetOrigin = targetOrigin || "*";
1415 
1416             //Default to parent target if none is specified.
1417             target = target || parent;
1418 
1419             //Ensure postMessage is supported by browser before invoking.
1420             if (window.postMessage) {
1421                 target.postMessage(message, targetOrigin);
1422             }
1423         },
1424 
1425         /**
1426          * Returns the passed in handler, if it is a function.
1427          * @param {Function} handler
1428          *     The handler to validate
1429          * @returns {Function}
1430          *     The provided handler if it is valid
1431          * @throws Error
1432          *     If the handler provided is invalid
1433          */
1434         validateHandler: function (handler) {
1435             if (handler === undefined || typeof handler === "function") {
1436                 return handler;
1437             } else {
1438                 throw new Error("handler must be a function");
1439             }
1440         },
1441 
1442         /**
1443          * @private
1444          * Tries to get extract the AWS error code from a
1445          * finesse.clientservices.ClientServices parsed error response object.
1446          * @param {Object} rsp
1447          *     The handler to validate
1448          * @returns {String}
1449          *     The error code, HTTP status code, or undefined
1450          */
1451         getErrCode: function (rsp) {
1452             try { // Best effort to get the error code
1453                 return rsp.object.ApiErrors.ApiError.ErrorType;
1454             } catch (e) { // Second best effort to get the HTTP Status code
1455                 if (rsp && rsp.status) {
1456                     return "HTTP " + rsp.status;
1457                 } else if (rsp && rsp.isUnsent) { // If request was aborted/cancelled/timedout
1458                     return "Request could not be completed";
1459                 }
1460             } // Otherwise, don't return anything (undefined)
1461         },
1462 
1463         /**
1464          * @private
1465          * Tries to get extract the AWS error data from a
1466          * finesse.clientservices.ClientServices parsed error response object.
1467          * @param {Object} rsp
1468          *     The handler to validate
1469          * @returns {String}
1470          *     The error data, HTTP status code, or undefined
1471          */
1472         getErrData: function (rsp) {
1473             try { // Best effort to get the error data
1474                 return rsp.object.ApiErrors.ApiError.ErrorData;
1475             } catch (e) { // Second best effort to get the HTTP Status code
1476                 if (rsp && rsp.status) {
1477                     return "HTTP " + rsp.status;
1478                 } else if (rsp && rsp.isUnsent) { // If request was aborted/cancelled/timedout
1479                     return "Request could not be completed";
1480                 }
1481             } // Otherwise, don't return anything (undefined)
1482         },
1483         
1484         /**
1485          * @private
1486          * Tries to get extract the AWS error message from a
1487          * finesse.clientservices.ClientServices parsed error response object.
1488          * @param {Object} rsp
1489          *     The handler to validate
1490          * @returns {String}
1491          *     The error message, HTTP status code, or undefined
1492          */
1493         getErrMessage: function (rsp) {
1494             try { // Best effort to get the error message
1495                 return rsp.object.ApiErrors.ApiError.ErrorMessage;
1496             } catch (e) { // Second best effort to get the HTTP Status code
1497                 if (rsp && rsp.status) {
1498                     return "HTTP " + rsp.status;
1499                 } else if (rsp && rsp.isUnsent) { // If request was aborted/cancelled/timedout
1500                     return "Request could not be completed";
1501                 }
1502             } // Otherwise, don't return anything (undefined)
1503         },
1504 
1505         /**
1506          * @private
1507          * Tries to get extract the AWS peripheral error code from a
1508          * finesse.clientservices.ClientServices parsed error response object.
1509          * @param {Object} rsp
1510          *     The handler to validate
1511          * @returns {String}
1512          *     The error message, HTTP status code, or undefined
1513          */
1514         getPeripheralErrorCode: function(rsp) {
1515             try {
1516                 return rsp.object.ApiErrors.ApiError.PeripheralErrorCode
1517             } catch (e) { // Second best effort to get the HTTP Status code
1518                 if (rsp && rsp.status) {
1519                     return "HTTP " + rsp.status;
1520                 } else if (rsp && rsp.isUnsent) { // If request was aborted/cancelled/timedout
1521                     return "Request could not be completed";
1522                 }
1523             } 
1524         },
1525         
1526         /**
1527          * @private
1528          * Tries to get extract the AWS overrideable boolean from a
1529          * finesse.clientservices.ClientServices parsed error response object.
1530          * @param {Object} rsp
1531          *     The handler to validate
1532          * @returns {String}
1533          *     The overrideable boolean, HTTP status code, or undefined
1534          */
1535         getErrOverrideable: function (rsp) {
1536             try { // Best effort to get the override boolean
1537                 return rsp.object.ApiErrors.ApiError.Overrideable;
1538             } catch (e) { // Second best effort to get the HTTP Status code
1539                 if (rsp && rsp.status) {
1540                     return "HTTP " + rsp.status;
1541                 }
1542             } // Otherwise, don't return anything (undefined)
1543         },
1544 
1545         /**
1546          * Trims leading and trailing whitespace from a string.
1547          * @param {String} str
1548          *     The string to trim
1549          * @returns {String}
1550          *     The trimmed string
1551          */
1552         trim: function (str) {
1553             return str.replace(/^\s*/, "").replace(/\s*$/, "");
1554         },
1555 
1556         /**
1557          * Utility method for getting the current time in milliseconds.
1558          * @returns {String}
1559          *     The current time in milliseconds
1560          */
1561         currentTimeMillis : function () {
1562             return (new Date()).getTime();
1563         },
1564 
1565        /**
1566         * Gets the current drift (between client and server)
1567         *
1568         * @returns {integer} which is the current drift (last calculated; 0 if we have not calculated yet)
1569         */
1570        getCurrentDrift : function () {
1571             var drift;
1572             
1573             //Get the current client drift from localStorage
1574             drift = window.sessionStorage.getItem("clientTimestampDrift");
1575             if (drift) {
1576                  drift = parseInt(drift, 10);
1577                  if (isNaN(drift)) {
1578                       drift = 0; 
1579                  }
1580             }
1581           return drift;
1582         },
1583 
1584        /**
1585         * Converts the specified clientTime to server time by adjusting by the current drift.
1586         *
1587         * @param clientTime is the time in milliseconds
1588         * @returns {int} serverTime in milliseconds
1589         */
1590         convertToServerTimeMillis : function(clientTime) {
1591             var drift = this.getCurrentDrift();
1592             return (clientTime + drift);
1593         },
1594 
1595         /**
1596          * Utility method for getting the current time,
1597          * adjusted by the calculated "drift" to closely
1598          * approximate the server time.  This is used
1599          * when calculating durations based on a server
1600          * timestamp, which otherwise can produce unexpected
1601          * results if the times on client and server are
1602          * off.
1603          * 
1604          * @returns {String}
1605          *     The current server time in milliseconds
1606          */
1607         currentServerTimeMillis : function () {
1608             var drift = this.getCurrentDrift();
1609             return (new Date()).getTime() + drift;
1610         },
1611 
1612         /**
1613          * Given a specified timeInMs, this method will builds a string which displays minutes and seconds. 
1614          *
1615          * @param timeInMs is the time in milliseconds
1616          * @returns {String} which corresponds to minutes and seconds (e.g. 11:23)
1617          */
1618         buildTimeString : function (timeInMs) {
1619            var min, sec, timeStr = "00:00";
1620           
1621            if (timeInMs && timeInMs !== "-1") {
1622               // calculate minutes, and seconds
1623               min = this.pad(Math.floor(timeInMs / 60000));
1624               sec = this.pad(Math.floor((timeInMs % 60000) / 1000));
1625               
1626               // construct MM:SS time string
1627               timeStr =  min + ":" + sec;
1628            }
1629            return timeStr;  
1630         },
1631         
1632         /**
1633          * Given a specified timeInMs, this method will builds a string which displays minutes and seconds (and optionally hours)
1634          *
1635          * @param timeInMs is the time in milliseconds
1636          * @returns {String} which corresponds to hours, minutes and seconds (e.g. 01:11:23 or 11:23)
1637          */
1638         buildTimeStringWithOptionalHours: function (timeInMs) {
1639            var hour, min, sec, timeStr = "00:00", optionalHour = "", timeInSecs;
1640           
1641            if (timeInMs && timeInMs !== "-1") {
1642               timeInSecs = timeInMs / 1000;
1643               
1644               // calculate {hours}, minutes, and seconds
1645               hour = this.pad(Math.floor(timeInSecs / 3600));
1646               min = this.pad(Math.floor((timeInSecs % 3600) / 60));
1647               sec = this.pad(Math.floor((timeInSecs % 3600) % 60));   
1648               
1649               //Optionally add the hour if we have hours
1650               if (hour > 0) {
1651                 optionalHour = hour + ":";
1652               }
1653               
1654               // construct MM:SS time string (or optionally HH:MM:SS)
1655               timeStr = optionalHour + min + ":" + sec; 
1656            }
1657            return timeStr;
1658         },
1659         
1660         
1661         /**
1662          * @private
1663          * Builds a string which specifies the amount of time user has been in this state (e.g. 11:23).
1664          *
1665          * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1666          * @param stateStartTimeInMs is integer argument which specifies time call entered current state
1667          * @returns {String} which is the elapsed time (MINUTES:SECONDS) 
1668          *
1669          */
1670         buildElapsedTimeString : function (adjustedServerTimeInMs, stateStartTimeInMs) {
1671            var result, delta;
1672            
1673            result = "--:--";
1674            if (stateStartTimeInMs !== 0) {
1675              delta = adjustedServerTimeInMs - stateStartTimeInMs;
1676              
1677              if (delta > 0) {
1678                result = this.buildTimeString(delta);
1679              }
1680           }
1681           return result;
1682        },
1683        
1684         /**
1685          * @private
1686          * 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).
1687          *
1688          * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1689          * @param startTimeInMs is integer argument which specifies the start time
1690          * @returns {String} which is the elapsed time (MINUTES:SECONDS) or (HOURS:MINUTES:SECONDS)
1691          *
1692          */
1693         buildElapsedTimeStringWithOptionalHours : function (adjustedServerTimeInMs, stateStartTimeInMs) {
1694            var result, delta;
1695            
1696            result = "--:--";
1697            if (stateStartTimeInMs !== 0) {
1698              delta = adjustedServerTimeInMs - stateStartTimeInMs;
1699              
1700              if (delta > 0) {
1701                result = this.buildTimeStringWithOptionalHours(delta);
1702              }
1703           }
1704           return result;
1705        },
1706        
1707        
1708        /**
1709         * Builds a string which displays the total call time in minutes and seconds.
1710         *
1711         * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1712         * @param callStartTimeInMs is integer argument which specifies time the call started
1713         * @returns {String} which is the elapsed time [MINUTES:SECONDS]
1714         */
1715        buildTotalTimeString : function (adjustedServerTimeInMs, callStartTimeInMs) {
1716           return this.buildElapsedTimeString(adjustedServerTimeInMs, callStartTimeInMs);
1717        },
1718        
1719        /**
1720         * @private
1721         * Builds a string which displays the hold time in minutes and seconds.
1722         *
1723         * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1724         * @param holdStartTimeInMs is integer argument which specifies time the hold started
1725         * @returns {String} which is the elapsed time [MINUTES:SECONDS] 
1726         */
1727        buildHoldTimeString : function (adjustedServerTimeInMs, holdStartTimeInMs) {
1728           return this.buildElapsedTimeString(adjustedServerTimeInMs, holdStartTimeInMs);
1729       },
1730       
1731       /**
1732        * @private
1733        * Builds a string which displays the elapsed time the call has been in wrap up.
1734        *
1735        * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1736        * @param wrapupStartTimeInMs is integer argument which specifies time call entered wrapup state
1737        * @returns {String} which is the elapsed wrapup time
1738        *
1739        */
1740       buildWrapupTimeString : function (adjustedServerTimeInMs, wrapupStartTimeInMs) {
1741          return this.buildElapsedTimeString(adjustedServerTimeInMs, wrapupStartTimeInMs);
1742       },
1743       
1744       /**
1745        * Extracts a time from the timeStr.  Note: The timeStr could be empty.  In this case, the time returned will be 0.
1746        * @param timeStr is a time string in ISO8601 format (note: could be empty)
1747        * @returns {long} is the time 
1748        */
1749       extractTime : function (timeStr) {
1750          var result = 0, theDate;
1751          if (timeStr === "") {
1752            result = 0;
1753          } else if (timeStr === null) {
1754            result = 0;
1755          } else {
1756            theDate = this.parseDateStringISO8601(timeStr);
1757            result = theDate.getTime();
1758          }
1759          return result;
1760       },
1761       
1762       /**
1763        * @private
1764        * Generates an RFC1422v4-compliant UUID using pesudorandom numbers.
1765        * @returns {String}
1766        *     An RFC1422v4-compliant UUID using pesudorandom numbers.
1767        **/        
1768         generateUUID: function () {
1769             return Math.uuidCompact();
1770         },
1771 
1772         /** @private */
1773         xml2json: finesse.Converter.xml2json,
1774         
1775         
1776         /**
1777          * @private
1778          * Utility method to get the JSON parser either from gadgets.json
1779          * or from window.JSON (which will be initialized by CUIC if 
1780          * browser doesn't support
1781          */
1782         getJSONParser: function() {
1783             var _container = window.gadgets || {},
1784                 parser = _container.json || window.JSON;
1785             return parser;
1786         },
1787 
1788        /**
1789         * @private
1790         * Utility method to convert a javascript object to XML.
1791         * @param {Object} object
1792         *   The object to convert to XML.
1793         * @param {Boolean} escapeFlag
1794         *   If escapeFlag evaluates to true:
1795         *       - XML escaping is done on the element values.
1796         *       - Attributes, #cdata, and #text is not supported.
1797         *       - The XML is unformatted (no whitespace between elements).
1798         *   If escapeFlag evaluates to false:
1799         *       - Element values are written 'as is' (no escaping).
1800         *       - Attributes, #cdata, and #text is supported.
1801         *       - The XML is formatted.
1802         * @returns The XML string.
1803         */
1804         json2xml: function (object, escapeFlag) {
1805             var xml;
1806             if (escapeFlag) {
1807                 xml = this._json2xmlWithEscape(object);
1808             }
1809             else {
1810                 xml = finesse.Converter.json2xml(object, '\t');
1811             }
1812             return xml;
1813         },
1814 
1815         /**
1816          * @private
1817          * Utility method to convert XML string into javascript object.
1818          */
1819         xml2JsObj : function (event) {
1820             var parser = this.getJSONParser();
1821             return parser.parse(finesse.Converter.xml2json(jQuery.parseXML(event), ""));
1822         },
1823 
1824        /**
1825         * @private
1826         * Utility method to convert an XML string to a javascript object.
1827         * @desc This function calls to the SAX parser and responds to callbacks
1828         *     received from the parser. Entity translation is not handled here.
1829         * @param {String} xml
1830         *   The XML to parse.
1831         * @returns The javascript object.
1832         */
1833         xml2js: function (xml) {
1834             var STATES = {
1835                     INVALID: 0,
1836                     NEW_NODE: 1,
1837                     ATTRIBUTE_NODE: 2,
1838                     TEXT_NODE: 3,
1839                     END_NODE: 4
1840                 },
1841                 state = STATES.INVALID,
1842                 rootObj = {},
1843                 newObj,
1844                 objStack = [rootObj],
1845                 nodeName = "",
1846 
1847                 /**
1848                 * @private
1849                 * Adds a property to the current top JSO.
1850                 * @desc This is also where we make considerations for arrays.
1851                 * @param {String} name
1852                 *   The name of the property to add.
1853                 * @param (String) value
1854                 *     The value of the property to add.
1855                 */
1856                 addProperty = function (name, value) {
1857                     var current = objStack[objStack.length - 1];
1858                     if(current.hasOwnProperty(name) && current[name] instanceof Array){
1859                         current[name].push(value);
1860                     }else if(current.hasOwnProperty(name)){
1861                         current[name] = [current[name], value];
1862                     }else{
1863                         current[name] = value;
1864                     }
1865                 },
1866 
1867                 /**
1868                 * @private
1869                 * The callback passed to the SAX parser which processes events from
1870                 * the SAX parser in order to construct the resulting JSO.
1871                 * @param (String) type
1872                 *     The type of event received.
1873                 * @param (String) data
1874                 *     The data received from the SAX parser. The contents of this
1875                 *     parameter vary based on the type of event.
1876                 */
1877                 xmlFound = function (type, data) {
1878                     switch (type) {
1879                     case "StartElement":
1880                         // Because different node types have different expectations
1881                         // of parenting, we don't push another JSO until we know
1882                         // what content we're getting
1883 
1884                         // If we're already in the new node state, we're running
1885                         // into a child node. There won't be any text here, so
1886                         // create another JSO
1887                         if(state === STATES.NEW_NODE){
1888                             newObj = {};
1889                             addProperty(nodeName, newObj);
1890                             objStack.push(newObj);
1891                         }
1892                         state = STATES.NEW_NODE;
1893                         nodeName = data;
1894                         break;
1895                     case "EndElement":
1896                         // If we're in the new node state, we've found no content
1897                         // set the tag property to null
1898                         if(state === STATES.NEW_NODE){
1899                             addProperty(nodeName, null);
1900                         }else if(state === STATES.END_NODE){
1901                             objStack.pop();
1902                         }
1903                         state = STATES.END_NODE;
1904                         break;
1905                     case "Attribute":
1906                         // If were in the new node state, no JSO has yet been created
1907                         // for this node, create one
1908                         if(state === STATES.NEW_NODE){
1909                             newObj = {};
1910                             addProperty(nodeName, newObj);
1911                             objStack.push(newObj);
1912                         }
1913                         // Attributes are differentiated from child elements by a
1914                         // preceding "@" in the property name
1915                         addProperty("@" + data.name, data.value);
1916                         state = STATES.ATTRIBUTE_NODE;
1917                         break;
1918                     case "Text":
1919                         // In order to maintain backwards compatibility, when no
1920                         // attributes are assigned to a tag, its text contents are
1921                         // assigned directly to the tag property instead of a JSO.
1922 
1923                         // If we're in the attribute node state, then the JSO for
1924                         // this tag was already created when the attribute was
1925                         // assigned, differentiate this property from a child
1926                         // element by naming it "#text"
1927                         if(state === STATES.ATTRIBUTE_NODE){
1928                             addProperty("#text", data);
1929                         }else{
1930                             addProperty(nodeName, data);
1931                         }
1932                         state = STATES.TEXT_NODE;
1933                         break;
1934                     }
1935                 };
1936             SaxParser.parse(xml, xmlFound);
1937             return rootObj;
1938         },
1939 
1940        /**
1941         * @private
1942         * Traverses a plain-old-javascript-object recursively and outputs its XML representation.
1943         * @param {Object} obj
1944         *     The javascript object to be converted into XML.
1945         * @returns {String} The XML representation of the object.
1946         */
1947         js2xml: function (obj) {
1948             var xml = "", i, elem;
1949 
1950             if (obj !== null) {
1951                 if (obj.constructor === Object) {
1952                     for (elem in obj) {
1953                         if (obj[elem] === null || typeof(obj[elem]) === 'undefined') {
1954                             xml += '<' + elem + '/>';
1955                         } else if (obj[elem].constructor === Array) {
1956                             for (i = 0; i < obj[elem].length; i++) {
1957                                 xml += '<' + elem + '>' + this.js2xml(obj[elem][i]) + '</' + elem + '>';
1958                             }
1959                         } else if (elem[0] !== '@') {
1960                             if (this.js2xmlObjIsEmpty(obj[elem])) {
1961                                 xml += '<' + elem + this.js2xmlAtt(obj[elem]) + '/>';
1962                             } else if (elem === "#text") {
1963                                 xml += obj[elem];
1964                             } else {
1965                                 xml += '<' + elem + this.js2xmlAtt(obj[elem]) + '>' + this.js2xml(obj[elem]) + '</' + elem + '>';
1966                             }
1967                         }
1968                     }
1969                 } else {
1970                     xml = obj;
1971                 }
1972             }
1973 
1974             return xml;
1975         },
1976 
1977        /**
1978         * @private
1979         * Utility method called exclusively by js2xml() to find xml attributes.
1980         * @desc Traverses children one layer deep of a javascript object to "look ahead"
1981         * for properties flagged as such (with '@').
1982         * @param {Object} obj
1983         *   The obj to traverse.
1984         * @returns {String} Any attributes formatted for xml, if any.
1985         */
1986         js2xmlAtt: function (obj) {
1987             var elem;
1988 
1989             if (obj !== null) {
1990                 if (obj.constructor === Object) {
1991                     for (elem in obj) {
1992                         if (obj[elem] !== null && typeof(obj[elem]) !== "undefined" && obj[elem].constructor !== Array) {
1993                             if (elem[0] === '@'){
1994                                 return ' ' + elem.substring(1) + '="' + obj[elem] + '"';
1995                             }
1996                         }
1997                     }
1998                 }
1999             }
2000 
2001             return '';
2002         },
2003 
2004        /**
2005         * @private
2006         * Utility method called exclusively by js2xml() to determine if
2007         * a node has any children, with special logic for ignoring attributes.
2008         * @desc Attempts to traverse the elements in the object while ignoring attributes.
2009         * @param {Object} obj
2010         *   The obj to traverse.
2011         * @returns {Boolean} whether or not the JS object is "empty"
2012         */
2013         js2xmlObjIsEmpty: function (obj) {
2014             var elem;
2015 
2016             if (obj !== null) {
2017                 if (obj.constructor === Object) {
2018                     for (elem in obj) {
2019                         if (obj[elem] !== null) {
2020                             if (obj[elem].constructor === Array){
2021                                 return false;
2022                             }
2023 
2024                             if (elem[0] !== '@'){
2025                                 return false;
2026                             }
2027                         } else {
2028                             return false;
2029                         }
2030                     }
2031                 } else {
2032                     return false;
2033                 }
2034             }
2035 
2036             return true;
2037         },
2038 
2039         /**
2040          * Encodes the given string into base64.
2041          *<br>
2042          * <b>NOTE:</b> {input} is assumed to be UTF-8; only the first
2043          * 8 bits of each input element are significant.
2044          *
2045          * @param {String} input
2046          *     The string to convert to base64.
2047          * @returns {String}
2048          *     The converted string.
2049          */
2050         b64Encode: function (input) {
2051             var output = "", idx, data,
2052                 table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
2053 
2054             for (idx = 0; idx < input.length; idx += 3) {
2055                 data =  input.charCodeAt(idx) << 16 |
2056                             input.charCodeAt(idx + 1) << 8 |
2057                             input.charCodeAt(idx + 2);
2058 
2059                 //assume the first 12 bits are valid
2060                 output +=   table.charAt((data >>> 18) & 0x003f) +
2061                             table.charAt((data >>> 12) & 0x003f);
2062                 output +=   ((idx + 1) < input.length) ?
2063                             table.charAt((data >>> 6) & 0x003f) :
2064                             "=";
2065                 output +=   ((idx + 2) < input.length) ?
2066                             table.charAt(data & 0x003f) :
2067                             "=";
2068             }
2069 
2070             return output;
2071         },
2072 
2073         /**
2074          * Decodes the given base64 string.
2075          * <br>
2076          * <b>NOTE:</b> output is assumed to be UTF-8; only the first
2077          * 8 bits of each output element are significant.
2078          *
2079          * @param {String} input
2080          *     The base64 encoded string
2081          * @returns {String}
2082          *     Decoded string
2083          */
2084         b64Decode: function (input) {
2085             var output = "", idx, h, data,
2086                 table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
2087 
2088             for (idx = 0; idx < input.length; idx += 4) {
2089                 h = [
2090                     table.indexOf(input.charAt(idx)),
2091                     table.indexOf(input.charAt(idx + 1)),
2092                     table.indexOf(input.charAt(idx + 2)),
2093                     table.indexOf(input.charAt(idx + 3))
2094                 ];
2095 
2096                 data = (h[0] << 18) | (h[1] << 12) | (h[2] << 6) | h[3];
2097                 if (input.charAt(idx + 2) === '=') {
2098                     data = String.fromCharCode(
2099                         (data >>> 16) & 0x00ff
2100                     );
2101                 } else if (input.charAt(idx + 3) === '=') {
2102                     data = String.fromCharCode(
2103                         (data >>> 16) & 0x00ff,
2104                         (data >>> 8) & 0x00ff
2105                     );
2106                 } else {
2107                     data = String.fromCharCode(
2108                         (data >>> 16) & 0x00ff,
2109                         (data >>> 8) & 0x00ff,
2110                         data & 0x00ff
2111                     );
2112                 }
2113                 output += data;
2114             }
2115 
2116             return output;
2117         },
2118 
2119         /**
2120          * @private
2121          * Extracts the username and the password from the Base64 encoded string.
2122          * @params {String}
2123          *     A base64 encoded string containing credentials that (when decoded)
2124          *     are colon delimited.
2125          * @returns {Object}
2126          *     An object with the following structure:
2127          *     {id:string, password:string}
2128          */
2129         getCredentials: function (authorization) {
2130             var credObj = {},
2131                 credStr = this.b64Decode(authorization),
2132                 colonIndx = credStr.indexOf(":");
2133 
2134             //Check to ensure that string is colon delimited.
2135             if (colonIndx === -1) {
2136                 throw new Error("String is not colon delimited.");
2137             }
2138 
2139             //Extract ID and password.
2140             credObj.id = credStr.substring(0, colonIndx);
2141             credObj.password = credStr.substring(colonIndx + 1);
2142             return credObj;
2143         },
2144 
2145         /**
2146          * Takes a string and removes any spaces within the string.
2147          * @param {String} string
2148          *     The string to remove spaces from
2149          * @returns {String}
2150          *     The string without spaces
2151          */
2152         removeSpaces: function (string) {
2153             return string.split(' ').join('');
2154         },
2155 
2156         /**
2157          * Escapes spaces as encoded " " characters so they can
2158          * be safely rendered by jQuery.text(string) in all browsers.
2159          *
2160          * (Although IE behaves as expected, Firefox collapses spaces if this function is not used.)
2161          *
2162          * @param text
2163          *    The string whose spaces should be escaped
2164          *
2165          * @returns
2166          *    The string with spaces escaped
2167          */
2168         escapeSpaces: function (string) {
2169             return string.replace(/\s/g, '\u00a0');
2170         },
2171 
2172         /**
2173          * Adds a span styled to line break at word edges around the string passed in.
2174          * @param str String to be wrapped in word-breaking style.
2175          * @private
2176          */
2177         addWordWrapping : function (str) {
2178             return '<span style="word-wrap: break-word;">' + str + '</span>';
2179         },
2180 
2181         /**
2182          * Takes an Object and determines whether it is an Array or not.
2183          * @param {Object} obj
2184          *     The Object in question
2185          * @returns {Boolean}
2186          *     true if the object is an Array, else false.
2187          */
2188         isArray: function (obj) {
2189             return obj.constructor.toString().indexOf("Array") !== -1;
2190         },
2191 
2192         /**
2193          * @private
2194          * Takes a data object and returns an array extracted
2195          * @param {Object} data
2196          *     JSON payload
2197          *
2198          * @returns {array}
2199          *     extracted array
2200          */
2201         getArray: function (data) {
2202             if (this.isArray(data)) {
2203                 //Return if already an array.
2204                 return data;
2205             } else {
2206                 //Create an array, iterate through object, and push to array. This
2207                 //should only occur with one object, and therefore one obj in array.
2208                 var arr = [];
2209                 arr.push(data);
2210                 return arr;
2211             }
2212         },
2213 
2214         /**
2215          * @private
2216          * Extracts the ID for an entity given the Finesse REST URI. The ID is
2217          * assumed to be the last element in the URI (after the last "/").
2218          * @param {String} uri
2219          *     The Finesse REST URI to extract the ID from.
2220          * @returns {String}
2221          *     The ID extracted from the REST URI.
2222          */
2223         getId: function (uri) {
2224             if (!uri) {
2225                 return "";
2226             }
2227             var strLoc = uri.lastIndexOf("/");
2228             return uri.slice(strLoc + 1);
2229         },
2230 
2231         /**
2232          * Compares two objects for equality.
2233          * @param {Object} obj1 
2234          *      First of two objects to compare.
2235          * @param {Object} obj2
2236          *      Second of two objects to compare.
2237          */
2238         getEquals: function (objA, objB) {
2239             var key;
2240 
2241             for (key in objA) {
2242                 if (objA.hasOwnProperty(key)) {
2243                     if (!objA[key]) {
2244                         objA[key] = "";
2245                     }
2246 
2247                     if (typeof objB[key] === 'undefined') {
2248                         return false;
2249                     }
2250                     if (typeof objB[key] === 'object') {
2251                         if (!objB[key].equals(objA[key])) {
2252                             return false;
2253                         }
2254                     }
2255                     if (objB[key] !== objA[key]) {
2256                         return false;
2257                     }
2258                 }
2259             }
2260             return true;
2261         },
2262 
2263         /**
2264          * @private
2265          * Regular expressions used in translating HTML and XML entities
2266          */
2267         ampRegEx : new RegExp('&', 'gi'),
2268         ampEntityRefRegEx : new RegExp('&', 'gi'),
2269         ltRegEx : new RegExp('<', 'gi'),
2270         ltEntityRefRegEx : new RegExp('<', 'gi'),
2271         gtRegEx : new RegExp('>', 'gi'),
2272         gtEntityRefRegEx : new RegExp('>', 'gi'),
2273         xmlSpecialCharRegEx: new RegExp('[&<>"\']', 'g'),
2274         entityRefRegEx: new RegExp('&[^;]+(?:;|$)', 'g'),
2275 
2276         /**
2277          * Translates between special characters and HTML entities
2278          *
2279          * @param text
2280          *     The text to translate
2281          *
2282          * @param makeEntityRefs
2283          *    If true, encode special characters as HTML entities; if
2284          *    false, decode HTML entities back to special characters
2285          *
2286          * @private
2287          */
2288         translateHTMLEntities: function (text, makeEntityRefs) {
2289             if (typeof(text) !== "undefined" && text !== null && text !== "") {
2290                 if (makeEntityRefs) {
2291                     text = text.replace(this.ampRegEx, '&');
2292                     text = text.replace(this.ltRegEx, '<');
2293                     text = text.replace(this.gtRegEx, '>');
2294                 } else {
2295                     text = text.replace(this.gtEntityRefRegEx, '>');
2296                     text = text.replace(this.ltEntityRefRegEx, '<');
2297                     text = text.replace(this.ampEntityRefRegEx, '&');
2298                 }
2299             }
2300 
2301             return text;
2302         },
2303 
2304         /**
2305          * Translates between special characters and XML entities
2306          *
2307          * @param text
2308          *     The text to translate
2309          *
2310          * @param makeEntityRefs
2311          *    If true, encode special characters as XML entities; if
2312          *    false, decode XML entities back to special characters
2313          *
2314          * @private
2315          */
2316         translateXMLEntities: function (text, makeEntityRefs) {
2317             /** @private */
2318             var escape = function (character) {
2319                 switch (character) {
2320                     case "&":
2321                         return "&";
2322                     case "<":
2323                         return "<";
2324                     case ">":
2325                         return ">";
2326                     case "'":
2327                         return "'";
2328                     case "\"":
2329                         return """;
2330                     default:
2331                         return character;
2332                 }
2333             },
2334             /** @private */
2335             unescape = function (entity) {
2336                 switch (entity) {
2337                     case "&":
2338                         return "&";
2339                     case "<":
2340                         return "<";
2341                     case ">":
2342                         return ">";
2343                     case "'":
2344                         return "'";
2345                     case """:
2346                         return "\"";
2347                     default:
2348                         if (entity.charAt(1) === "#" && entity.charAt(entity.length - 1) === ";") {
2349                             if (entity.charAt(2) === "x") {
2350                                 return String.fromCharCode(parseInt(entity.slice(3, -1), 16));
2351                             } else {
2352                                 return String.fromCharCode(parseInt(entity.slice(2, -1), 10));
2353                             }
2354                         } else {
2355                             throw new Error("Invalid XML entity: " + entity);
2356                         }
2357                 }
2358             };
2359 
2360             if (typeof(text) !== "undefined" && text !== null && text !== "") {
2361                 if (makeEntityRefs) {
2362                     text = text.replace(this.xmlSpecialCharRegEx, escape);
2363                 } else {
2364                     text = text.replace(this.entityRefRegEx, unescape);
2365                 }
2366             }
2367 
2368             return text;
2369         },
2370 
2371         /**
2372          * @private
2373          * Utility method to pad the number with a leading 0 for single digits
2374          * @param (Number) num
2375          *     the number to pad
2376          */
2377         pad : function (num) {
2378             if (num < 10) {
2379                 return "0" + num;
2380             }
2381 
2382             return String(num);
2383         },
2384         
2385         /**
2386          * @private
2387          * Pad with zeros based on a padWidth.
2388          *
2389          * @param num
2390          * @param padWidth
2391          * @returns {String} with padded zeros (based on padWidth)
2392          */
2393         padWithWidth : function (num, padWidth) {
2394             var value, index, result;
2395             
2396             result = "";
2397             for(index=padWidth;index>1;index--)
2398             {
2399                 value = Math.pow(10, index-1);
2400                 
2401                 if (num < value) {
2402                    result = result + "0";
2403                 }
2404             }
2405             result = result + num;
2406             
2407             return String(result);
2408         },
2409         
2410         /**
2411          * Converts a date to an ISO date string.
2412          *
2413          * @param aDate
2414          * @returns {String} in ISO date format
2415          *
2416          * Note: Some browsers don't support this method (e.g. IE8).
2417          */
2418         convertDateToISODateString : function (aDate) {
2419              var result;
2420              
2421              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";
2422              return result;
2423         },
2424         
2425        /**
2426         * Get the date in ISO date format. 
2427         * 
2428         * @param aDate is the date
2429         * @returns {String} date in ISO format
2430         *
2431         * Note: see convertDateToISODateString() above.
2432         */
2433         dateToISOString : function (aDate) {
2434              var result;
2435              
2436              try {
2437                 result = aDate.toISOString();
2438              } catch (e) {
2439                 result = this.convertDateToISODateString(aDate);
2440              }
2441              return result;
2442         },
2443         
2444         /**
2445          * Parse string (which is formated as ISO8601 date) into Javascript Date object.
2446          *
2447          * @param s ISO8601 string
2448          * @return {Date}
2449          * Note: Some browsers don't support Date constructor which take ISO8601 date (e.g. IE 8).
2450          */
2451         parseDateStringISO8601 : function (s) {
2452              var i, re = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:.(\d+))?(Z|[+\-]\d{2})(?::(\d{2}))?/,
2453              d = s.match(re);
2454              if( !d ) {
2455                 return null;
2456              }
2457              for( i in d ) {
2458                 d[i] = ~~d[i];
2459              }
2460              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);
2461         },
2462         
2463         /**
2464          * Utility method to render a timestamp value (in seconds) into HH:MM:SS format.
2465          * @param {Number} time
2466          *     The timestamp in ms to render
2467          * @returns {String}
2468          * Time string in HH:MM:SS format.
2469          */
2470         getDisplayTime : function (time) {
2471             var hour, min, sec, timeStr = "00:00:00";
2472 
2473             if (time && time !== "-1") {
2474                 // calculate hours, minutes, and seconds
2475                 hour = this.pad(Math.floor(time / 3600));
2476                 min = this.pad(Math.floor((time % 3600) / 60));
2477                 sec = this.pad(Math.floor((time % 3600) % 60));
2478                 // construct HH:MM:SS time string
2479                 timeStr = hour + ":" + min + ":" + sec;
2480             }
2481 
2482             return timeStr;
2483         },
2484 
2485         /**
2486          * Checks if the string is null. If it is, return empty string; else return
2487          * the string itself.
2488          * 
2489          * @param  {String} str 
2490          * The string to check
2491          * @return {String}     
2492          * Empty string or string itself
2493          */
2494         convertNullToEmptyString : function (str) {
2495             return str || "";
2496         },
2497 
2498         /**
2499          * Utility method to render a timestamp string (of format
2500          * YYYY-MM-DDTHH:MM:SSZ) into a duration of HH:MM:SS format.
2501          * 
2502          * @param {String} timestamp
2503          *           The timestamp to render
2504          * @param {Date} [now]
2505          *            Optional argument to provide the time from which to
2506          *            calculate the duration instead of using the current time
2507          * @returns {String}
2508          * Duration string in HH:MM:SS format.
2509          */
2510         convertTsToDuration : function (timestamp, now) {
2511             return this.convertTsToDurationWithFormat(timestamp, false, now); 
2512         },
2513         
2514         /**
2515          * Utility method to render a timestamp string (of format
2516          * YYYY-MM-DDTHH:MM:SSZ) into a duration of HH:MM:SS format,
2517          * with optional -1 for null or negative times.
2518          * 
2519          * @param {String} timestamp
2520          *             The timestamp to render
2521          * @param {Boolean} forFormat
2522          *            If True, if duration is null or negative, return -1 so that the duration can be formated
2523          *            as needed in the Gadget. 
2524          * @param {Date} [now]
2525          *             Optional argument to provide the time from which to
2526          *            calculate the duration instead of using the current time
2527          * @returns {String}
2528          * Duration string in HH:MM:SS format.
2529          */
2530         convertTsToDurationWithFormat : function (timestamp, forFormat, now) {
2531             var startTimeInMs, nowInMs, durationInSec = "-1";
2532             
2533             // Calculate duration
2534             if (timestamp && typeof timestamp === "string") {
2535                 // first check it '--' for a msg in grid
2536                 if (timestamp === '--' || timestamp ==="" || timestamp === "-1") {
2537                     return "-1";
2538                 }
2539                 // else try convert string into a time
2540                 startTimeInMs = Date.parse(timestamp);
2541                 if (!isNaN(startTimeInMs)) {
2542                     if (!now || !(now instanceof Date)) {
2543                         nowInMs = this.currentServerTimeMillis();
2544                     } else {
2545                         nowInMs = this.convertToServerTimeMillis(now.getTime());
2546                     }
2547                     durationInSec = Math.floor((nowInMs - startTimeInMs) / 1000);
2548                     // Since currentServerTime is not exact (lag between sending and receiving
2549                     // messages will differ slightly), treat a slightly negative (less than 1 sec) 
2550                     // value as 0, to avoid "--" showing up when a state first changes.
2551                     if (durationInSec === -1) {
2552                         durationInSec = 0;
2553                     }
2554                     
2555                     if (durationInSec < 0) {
2556                         if (forFormat) {
2557                             return "-1";
2558                         } else {
2559                             return this.getDisplayTime("-1");
2560                         }
2561                     }
2562                 }
2563             }else {
2564                 if(forFormat){
2565                     return "-1";
2566                 }
2567             }
2568             return this.getDisplayTime(durationInSec);
2569          },
2570          
2571          /**
2572           * @private
2573           * Takes the time in seconds and duration in % and return the duration in milliseconds.
2574           *
2575           * @param time in seconds
2576           * @param duration in %
2577           */
2578          
2579          getRefreshTime :function(expiryTime , duration){
2580           var durationInMs = Math.floor((expiryTime * duration * 1000) / 100);
2581             return durationInMs;
2582          },
2583          
2584         /**
2585          * Takes a string (typically from window.location) and finds the value which corresponds to a name. For
2586          * example: http://www.company.com/?param1=value1¶m2=value2
2587          *
2588          * @param str is the string to search
2589          * @param name is the name to search for
2590          */
2591         getParameterByName : function(str, name) {
2592             var regex, results;
2593             name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
2594             regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
2595             results = regex.exec(str);
2596             return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
2597         }, 
2598         
2599         /**
2600          *
2601          * Returns the base64 encoded user authorization String.
2602          * @returns {String} the Authorization String
2603          * 
2604          */
2605         getUserAuthString: function () {
2606             var authString = window.sessionStorage.getItem('userFinesseAuth');
2607             return authString;
2608         },
2609         
2610         /**
2611          * Return the user access token as JSON Object.
2612          * @returns {Object} the access token JSON object
2613          * 
2614          */
2615         getAuthTokenObj: function(){
2616            var authTokenString = window.sessionStorage.getItem('ssoTokenObject');
2617            return this.getJSONParser().parse(authTokenString);
2618         },
2619         
2620         /**
2621          * Returns the user access token as String.
2622          * @returns {String} the access token
2623          * 
2624          */
2625 
2626         getToken: function () {
2627             var tokenString = window.sessionStorage.getItem('ssoTokenObject'), tokenObj;
2628 					if (tokenString && typeof tokenString === "string") {
2629 						tokenObj = this.getJSONParser().parse(tokenString);
2630 						if (tokenObj.token) {
2631 							return tokenObj.token;
2632 						} else {
2633 							throw new Error(
2634 									"Unable to retrieve token : Invalid token Object in browser session");
2635 						}
2636 					} 
2637         },
2638         
2639         /**
2640 		 * The authorization header based on SSO or non SSO deployment.
2641 		 *          Can be "Bearer " or "Basic "
2642 		 * @returns {String} The authorization header string.
2643 		 */
2644 		 getAuthHeaderString : function(configObj) {
2645 					var authHeader;
2646 					if (configObj.systemAuthMode === this.getAuthModes().SSO) {
2647 						authHeader = "Bearer " + configObj.authToken;
2648 					} else if (configObj.systemAuthMode === this.getAuthModes().NONSSO) {
2649 						// CSCvs56405- In case of SPOG, supervisor credentials can be used to retrieve the Workflows and ReasonCodes. 
2650 						// Since supervisor can log in with SSO. But with context to cfadmin, configObj.systemAuthMode will always be nonsso
2651 						// Hence 'Basic' will be appended to actual SSO token.
2652 						// Below check is to avoid to append 'Basic' if its already a token
2653 						if(configObj.authorization && configObj.authorization.indexOf("Bearer") > -1) {
2654 							authHeader = configObj.authorization;
2655 						} else {
2656 							authHeader = "Basic " + configObj.authorization;
2657 						}
2658 					} else {
2659 						throw new Error("Unknown auth mode "+configObj.systemAuthMode);
2660 					}
2661 					return authHeader;
2662 				},
2663 		
2664 		/**
2665 		 * Can be used as a constant for auth modes
2666 		 *          Can be "SSO" , "NON_SSO" or "HYBRID"
2667 		 * @returns {String} The authorization header string.
2668 		 */		
2669 		getAuthModes : function(){
2670 		     return {
2671                     SSO: "SSO",
2672 		            NONSSO: "NON_SSO",
2673 		            HYBRID: "HYBRID"
2674 		     };
2675 		},
2676 		
2677 		/**
2678 		 * Encodes the node name
2679 		 */
2680 		encodeNodeName : function(node){
2681 			if (node === null){
2682 			    return null;
2683 			}
2684 			var originalChars, encodedChars,encodedNode, i;
2685 			originalChars = ["?", "@", "&","'"];
2686 			encodedChars = ["?3F", "?40", "?26","?27"];
2687 			encodedNode = node;
2688 			
2689 			if(encodedNode.indexOf(originalChars[0]) !== -1){
2690 			   encodedNode = encodedNode.replace(/\?/g, encodedChars[0]);
2691 			}
2692 			for (i = 1; i < originalChars.length; i++){
2693 			    if(encodedNode.indexOf(originalChars[i]) !== -1){
2694 			        encodedNode = encodedNode.replace(new RegExp(originalChars[i], "g"), encodedChars[i]);
2695 			    }
2696 			}
2697 			return encodedNode;
2698 		},
2699 		
2700 		/**
2701 		 * @private Utility method to convert milliseconds into minutes.
2702 		 * @param {String} Time in milliseconds
2703 		 * @returns {String} Time in minutes
2704 		 */
2705 		convertMilliSecondsToMinutes : function(millisec){
2706 			if(!millisec || isNaN(millisec)){
2707 				throw new Error("passed argument is not a number");
2708 			}else{
2709 				var minutes = Math.floor(millisec / (1000 * 60));
2710 	            return minutes;
2711 			}
2712 		},
2713 
2714         
2715         /**
2716 		 * @private Adds a new cookie to the page with a default domain.
2717 		 * @param {String}
2718 		 *            key the key to assign a value to
2719 		 * @param {String}
2720 		 *            value the value to assign to the key
2721 		 * @param {Number}
2722 		 *            days number of days (from current) until the cookie should
2723 		 *            expire
2724 		 * @param {String}
2725 		 *            domain the domain for the cookie   
2726 		 */
2727         addCookie : function (key, value, days, domain) {
2728             var date, expires = "",
2729                 cookie = key + "=" + escape(value);
2730             if (typeof days === "number") {
2731                 date = new Date();
2732                 date.setTime(date.getTime() + (days * 24 * 3600 * 1000));
2733                 cookie += "; expires=" + date.toGMTString();
2734             }
2735             
2736             if (domain) {
2737             	cookie += "; domain=" + domain;
2738             }
2739             
2740             document.cookie = cookie + "; path=/";
2741         },
2742 
2743         /**
2744          * @private
2745          * Get the value of a cookie given a key.
2746          * @param {String} key
2747          *      a key to lookup
2748          * @returns {String}
2749          *      the value mapped to a key, null if key doesn't exist
2750          */
2751         getCookie : function (key) {
2752             var i, pairs, pair;
2753             if (document.cookie) {
2754                 pairs = document.cookie.split(";");
2755                 for (i = 0; i < pairs.length; i += 1) {
2756                     pair = this.trim(pairs[i]).split("=");
2757                     if (pair[0] === key) {
2758                         return unescape(pair[1]);
2759                     }
2760                 }
2761             }
2762             return null;
2763         },
2764         
2765         /**
2766          * @private
2767          * serach cookies which starts with given string
2768          */
2769         getCookieEntriesThatStartsWith : function (key) {
2770        	 var i, pairs, pair, entries = [];
2771             if (document.cookie) {
2772                 pairs = document.cookie.split(";");
2773                 for (i = 0; i < pairs.length; i += 1) {
2774                     pair = this.trim(pairs[i]).split("=");
2775                     if (pair[0].startsWith(key)) {
2776                    	 entries.push({key: pair[0], value: unescape(pair[1])});
2777                     }
2778                 }
2779             }
2780             return entries;
2781        },
2782 
2783         /**
2784          * @private
2785          * Deletes the cookie mapped to specified key.
2786          * @param {String} key
2787          *      the key to delete
2788          */
2789         deleteCookie : function (key, domain) {
2790             this.addCookie(key, "", -1, domain);
2791         },
2792 
2793         /**
2794          * @private
2795          * Case insensitive sort for use with arrays or Dojox stores
2796          * @param {String} a
2797          *      first value
2798          * @param {String} b
2799          *      second value
2800          */
2801         caseInsensitiveSort: function (a, b) {
2802             var ret = 0, emptyString = "";
2803             a = a + emptyString;
2804             b = b + emptyString;
2805             a = a.toLowerCase();
2806             b = b.toLowerCase();
2807             if (a > b) {
2808                 ret = 1;
2809             }
2810             if (a < b) { 
2811                 ret = -1;
2812             }
2813             return ret;
2814         },
2815 
2816         /**
2817          * @private
2818         * Calls the specified function to render the dojo wijit for a gadget  when the gadget first becomes visible.
2819         *
2820         * The displayWjitFunc function will be called once and only once when the div for our wijit 
2821         * becomes visible for the first time.  This is necessary because some dojo wijits such as the grid
2822         * throw exceptions and do not render properly if they are created in a display:none div.
2823         * If our gadget is visisble the function will be called immediately.
2824         * If our gadget is not yet visisble, then it sets a timer and waits for it to become visible.
2825         * NOTE:  The timer may seem inefficent, originally I tried connecting to the tab onclick handler, but
2826         * there is a problem with dojo.connnect to an iframe's parent node in Internet Explorer. 
2827         * In Firefox the click handler works OK, but it happens before the node is actually visisble, so you
2828         * end up waiting for the node to become visisble anyway.
2829         * @displayWjitFunc:  A function to be called once our gadget has become visisble for th first time.
2830         */  
2831         onGadgetFirstVisible: function (displayWjitFunc) {
2832             var i, q, frameId, gadgetNbr, gadgetTitleId, panelId, panelNode, link, iterval, once = false, active = false, tabId = "#finesse-tab-selector";
2833             try {
2834                 frameId = dojo.attr(window.frameElement, "id"); // Figure out what gadget number we are by looking at our frameset
2835                 gadgetNbr = frameId.match(/\d+$/)[0];  // Strip the number off the end of the frame Id, that's our gadget number
2836                 gadgetTitleId = "#finesse_gadget_" + gadgetNbr + "_title";  // Create a a gadget title id from the number
2837                 
2838                 // Loop through all of the tab panels to find one that has our gadget id
2839                 dojo.query('.tab-panel', window.parent.document).some(function (node, index, arr) {
2840                     q = dojo.query(gadgetTitleId, node);  // Look in this panel for our gadget id
2841                     if (q.length > 0) {  // You found it
2842                         panelNode = node;
2843                         panelId = dojo.attr(panelNode, "id");  // Get panel id  e.g. panel_Workgroups
2844                         active = dojo.hasClass(panelNode, "active");
2845                         tabId = "#tab_" + panelId.slice(6);  // Turn it into a tab id e.g.tab_Workgroups
2846                         return;
2847                     }
2848                 });
2849                 // If panel is already active - execute the function - we're done
2850                 if (active) {
2851                     //?console.log(frameId + " is visible display it");
2852                     setTimeout(displayWjitFunc);
2853                 } 
2854                 // If its not visible - wait for the active class to show up.
2855                 else {
2856                     //?console.log(frameId  + " (" + tabId + ") is NOT active wait for it");
2857                     iterval = setInterval(dojo.hitch(this, function () {
2858                         if (dojo.hasClass(panelNode, "active")) {
2859                             //?console.log(frameId  + " (" + tabId + ") is visible display it");
2860                             clearInterval(iterval);
2861                             setTimeout(displayWjitFunc);
2862                         } 
2863                     }), 250);
2864                 }
2865             } catch (err) {
2866                 //?console.log("Could not figure out what tab " + frameId + " is in: " + err);
2867             }
2868         },
2869 
2870         /**
2871          * @private
2872          * Downloads the specified url using a hidden iframe. In order to cause the browser to download rather than render
2873          * in the hidden iframe, the server code must append the header "Content-Disposition" with a value of 
2874          * "attachment; filename=\"<WhateverFileNameYouWant>\"".
2875          */
2876         downloadFile : function (url) {
2877             var iframe = document.getElementById("download_iframe");
2878 
2879             if (!iframe)
2880             {
2881                 iframe = document.createElement("iframe");
2882                 $(document.body).append(iframe);
2883                 $(iframe).css("display", "none");
2884             }
2885 
2886             iframe.src = url;
2887         },
2888 
2889         /**
2890          * @private
2891          * bitMask has functions for testing whether bit flags specified by integers are set in the supplied value
2892          */
2893         bitMask: {
2894             /** @private */
2895             isSet: function (value, mask) {
2896                 return (value & mask) === mask;
2897             },
2898             /**
2899              * Returns true if all flags in the intArray are set on the specified value
2900              * @private 
2901              */
2902             all: function (value, intArray) {
2903                 var i = intArray.length;
2904                 if (typeof(i) === "undefined")
2905                 {
2906                     intArray = [intArray];
2907                     i = 1;
2908                 }
2909                 while ((i = i - 1) !== -1)
2910                 {
2911                     if (!this.isSet(value, intArray[i]))
2912                     {
2913                         return false;
2914                     }
2915                 }
2916                 return true;
2917             },
2918             /**
2919              * @private
2920              * Returns true if any flags in the intArray are set on the specified value
2921              */
2922             any: function (value, intArray) {
2923                 var i = intArray.length;
2924                 if (typeof(i) === "undefined")
2925                 {
2926                     intArray = [intArray];
2927                     i = 1;
2928                 }
2929                 while ((i = i - 1) !== -1)
2930                 {
2931                     if (this.isSet(value, intArray[i]))
2932                     {
2933                         return true;
2934                     }
2935                 }
2936                 return false;
2937             }
2938         },
2939 
2940         /** @private */
2941         renderDojoGridOffScreen: function (grid) {
2942             var offscreenDiv = $("<div style='position: absolute; left: -5001px; width: 5000px;'></div>")[0];
2943             $(document.body).append(offscreenDiv);
2944             grid.placeAt(offscreenDiv);
2945             grid.startup();
2946             document.body.removeChild(offscreenDiv);
2947             return grid;
2948         },
2949 
2950         /** @private */
2951         initializeSearchInput: function(searchInput, changeCallback, callbackDelay, callbackScope, placeholderText) {
2952             var timerId = null,
2953                 theControl = typeof(searchInput) === "string" ? $("#" + searchInput) : $(searchInput),
2954                 theInputControl = theControl.find("input"),
2955                 theClearButton = theControl.find("a"),
2956                 inputControlWidthWithClear = 204,
2957                 inputControlWidthNoClear = 230,
2958                 sPreviousInput = theInputControl.val(),
2959                 /** @private **/
2960                 toggleClearButton = function(){
2961                     if (theInputControl.val() === "") {
2962                         theClearButton.hide();
2963                         theControl.removeClass("input-append");
2964                         theInputControl.width(inputControlWidthNoClear);
2965                     } else {
2966                         theInputControl.width(inputControlWidthWithClear);
2967                         theClearButton.show();
2968                         theControl.addClass("input-append");
2969                     }
2970                 };
2971 
2972             // set placeholder text
2973             theInputControl.attr('placeholder', placeholderText);
2974 
2975             theInputControl.unbind('keyup').bind('keyup', function() {
2976                 if (sPreviousInput !== theInputControl.val()) {
2977                     window.clearTimeout(timerId);
2978                     sPreviousInput = theInputControl.val();
2979                     timerId = window.setTimeout(function() {
2980                         changeCallback.call((callbackScope || window), theInputControl.val());
2981                         theInputControl[0].focus();
2982                     }, callbackDelay);
2983                 }
2984 
2985                 toggleClearButton();
2986             });
2987 
2988             theClearButton.bind('click', function() {
2989                 theInputControl.val('');
2990                 changeCallback.call((callbackScope || window), '');
2991 
2992                 toggleClearButton();
2993                 theInputControl[0].focus(); // jquery and dojo on the same page break jquery's focus() method
2994             });
2995 
2996             theInputControl.val("");
2997             toggleClearButton();
2998         },
2999 
3000         DataTables: {
3001             /** @private */
3002             createDataTable: function (options, dataTableOptions) {
3003                 var grid,
3004                     table = $('<table cellpadding="0" cellspacing="0" border="0" class="finesse"><thead><tr></tr></thead></table>'),
3005                     headerRow = table.find("tr"),
3006                     defaultOptions = {
3007                         "aaData": [],
3008                         "bPaginate": false,
3009                         "bLengthChange": false,
3010                         "bFilter": false,
3011                         "bInfo": false,
3012                         "sScrollY": "176",
3013                         "oLanguage": {
3014                             "sEmptyTable": "",
3015                             "sZeroRecords": ""
3016                         }
3017                     },
3018                     gridOptions = $.extend({}, defaultOptions, dataTableOptions),
3019                     columnDefs = [],
3020                     columnFormatter;
3021 
3022                 // Create a header cell for each column, and set up the datatable definition for the column
3023                 $(options.columns).each(function (index, column) {
3024                     headerRow.append($("<th></th>"));
3025                     columnDefs[index] = {
3026                         "mData": column.propertyName,
3027                         "sTitle": column.columnHeader,
3028                         "sWidth": column.width,
3029                         "aTargets": [index],
3030                         "bSortable": column.sortable,
3031                         "bVisible": column.visible,
3032                         "mRender": column.render
3033                     };
3034                     if (typeof(column.renderFunction) === "function")
3035                     {
3036                         /** @ignore **/
3037                         columnDefs[index].mRender = /** @ignore **/ function (value, type, dataObject) { 
3038                             var returnValue;
3039 
3040                             //Apply column render logic to value before applying extra render function
3041                             if (typeof(column.render) === "function")
3042                             {
3043                                 value = column.render.call(value, value, value);
3044                             }
3045 
3046                             if (typeof(type) === "string")
3047                             {
3048                                 switch (type)
3049                                 {
3050                                 case "undefined":
3051                                 case "sort":
3052                                     returnValue = value;
3053                                     break;
3054                                 case "set":
3055                                     throw new Error("Unsupported set data in Finesse Grid");
3056                                 case "filter":
3057                                 case "display":
3058                                 case "type":
3059                                     returnValue = column.renderFunction.call(dataObject, value, dataObject);
3060                                     break;
3061                                 default:
3062                                     break;
3063                                 }
3064                             }
3065                             else
3066                             {
3067                                 throw new Error("type param not specified in Finesse DataTable mData");
3068                             }
3069 
3070                             return  returnValue;
3071                         };
3072                     }
3073                 });
3074                 gridOptions.aoColumnDefs = columnDefs;
3075 
3076                 // Set the height
3077                 if (typeof(options.bodyHeightPixels) !== "undefined" && options.bodyHeightPixels !== null)
3078                 {
3079                     gridOptions.sScrollY = options.bodyHeightPixels + "px";
3080                 }
3081 
3082                 // Place it into the DOM
3083                 if (typeof(options.container) !== "undefined" && options.container !== null)
3084                 {
3085                     $(options.container).append(table);
3086                 }
3087 
3088                 // Create the DataTable
3089                 table.dataTable(gridOptions);
3090 
3091                 return table;
3092             }
3093         },
3094         
3095         /**
3096          * @private
3097          * Sets a dojo button to the specified disable state, removing it from
3098          * the tab order if disabling, and restoring it to the tab order if enabling.
3099          * @param {Object} dojoButton Reference to the dijit.form.Button object. This is not the DOM element.
3100          * @param {bool} disabled
3101          */
3102         setDojoButtonDisabledAttribute: function (dojoButton, disabled) {
3103             var labelNode,
3104                 tabIndex;
3105 
3106             dojoButton.set("disabled", disabled);
3107 
3108             // Remove the tabindex attribute on disabled buttons, store it, 
3109             // and replace it when it becomes enabled again
3110             labelNode = $("#" + dojoButton.id + "_label");
3111             if (disabled)
3112             {
3113                 labelNode.data("finesse:dojoButton:tabIndex", labelNode.attr("tabindex"));
3114                 labelNode.removeAttr("tabindex");
3115             }
3116             else
3117             {
3118                 tabIndex = labelNode.data("finesse:dojoButton:tabIndex");
3119                 if (typeof(tabIndex) === "string")
3120                 {
3121                     labelNode.attr("tabindex", Number(tabIndex));
3122                 }
3123             }
3124         },
3125 
3126         /**
3127          * @private
3128          * Use this utility to disable the tab stop for a Dojo Firebug iframe within a gadget.
3129          *
3130          * Dojo sometimes adds a hidden iframe for enabling a firebug lite console in older
3131          * browsers. Unfortunately, this adds an additional tab stop that impacts accessibility.
3132          */
3133         disableTabStopForDojoFirebugIframe: function () {
3134             var iframe = $("iframe[src*='loadFirebugConsole']");
3135 
3136             if ((iframe.length) && (iframe.attr("tabIndex") !== "-1")) {
3137                 iframe.attr('tabIndex', '-1'); 
3138             }
3139         },
3140 
3141         /**
3142          * @private
3143          * Measures the given text using the supplied fontFamily and fontSize
3144          * @param  {string} text       text to measure
3145          * @param  {string} fontFamily
3146          * @param  {string} fontSize
3147          * @return {number} pixel width
3148          */
3149         measureText: function (text, fontFamily, fontSize) {
3150             var width,
3151                 element = $("<div></div>").text(text).css({
3152                     "fontSize": fontSize,
3153                     "fontFamily": fontFamily
3154                 }).addClass("offscreen").appendTo(document.body);
3155 
3156             width = element.width();
3157             element.remove();
3158 
3159             return width;
3160         },
3161 
3162         /**
3163          * Adjusts the gadget height. Shindig's gadgets.window.adjustHeight fails when
3164          * needing to resize down in IE. This gets around that by calculating the height
3165          * manually and passing it in.
3166          * @return {undefined}
3167          */
3168         "adjustGadgetHeight": function () {
3169             var bScrollHeight = $("body").height() + 20;
3170             gadgets.window.adjustHeight(bScrollHeight);
3171         },
3172 
3173         /**
3174         * Private helper method for converting a javascript object to xml, where the values of the elements are
3175         * appropriately escaped for XML.
3176         * This is a simple implementation that does not implement cdata or attributes. It is also 'unformatted' in that
3177         * there is no whitespace between elements.
3178         * @param object The javascript object to convert to XML.
3179         * @returns The XML string.
3180         * @private
3181         */
3182         _json2xmlWithEscape: function(object) {
3183             var that = this,
3184                 xml = "",
3185                 m,
3186                 /** @private **/
3187                 toXmlHelper = function(value, name) {
3188                 var xml = "",
3189                     i,
3190                     m;
3191                 if (value instanceof Array) {
3192                     for (i = 0; i < value.length; ++i) {
3193                         xml += toXmlHelper(value[i], name);
3194                     }
3195                 }
3196                 else if (typeof value === "object") {
3197                     xml += "<" + name + ">";
3198                     for (m in value) {
3199                         if (value.hasOwnProperty(m)) {
3200                            xml += toXmlHelper(value[m], m);
3201                         }
3202                     }
3203                     xml += "</" + name + ">";
3204                 }
3205                 else {
3206                     // is a leaf node
3207                     xml += "<" + name + ">" + that.translateHTMLEntities(value.toString(), true) +
3208                         "</" + name + ">";
3209                 }
3210                 return xml;
3211             };
3212             for (m in object) {
3213                 if (object.hasOwnProperty(m)) {
3214                     xml += toXmlHelper(object[m], m);
3215                 }
3216             }
3217             return xml;
3218         },
3219 
3220         /**
3221          * Private method for returning a sanitized version of the user agent string.
3222          * @returns the user agent string, but sanitized!
3223          * @private
3224          */
3225         getSanitizedUserAgentString: function () {
3226             return this.translateXMLEntities(navigator.userAgent, true);
3227         },
3228 
3229         /**
3230          * Use JQuery's implementation of Promises (Deferred) to execute code when 
3231          * multiple async processes have finished. An example use:
3232          *
3233          * var asyncProcess1 = $.Deferred(),
3234          *     asyncProcess2 = $.Deferred();
3235          *     
3236          * finesse.utilities.Utilities.whenAllDone(asyncProcess1, asyncProcess2) // WHEN both asyncProcess1 and asyncProcess2 are resolved or rejected ...
3237          *     .then(
3238          *         // First function passed to then() is called when all async processes are complete, regardless of errors
3239          *         function () {
3240          *             console.log("all processes completed");
3241          *         },
3242          *         // Second function passed to then() is called if any async processed threw an exception
3243          *         function (failures) { // Array of failure messages
3244          *             console.log("Number of failed async processes: " + failures.length);
3245          *         });
3246          *
3247          * Found at:
3248          * http://stackoverflow.com/a/15094263/1244030
3249          *
3250          * Pass in any number of $.Deferred instances.
3251          * @returns {Object}
3252          */
3253         whenAllDone: function () {
3254             var deferreds = [],
3255                 result = $.Deferred();
3256 
3257             $.each(arguments, function(i, current) {
3258                 var currentDeferred = $.Deferred();
3259                 current.then(function() {
3260                     currentDeferred.resolve(false, arguments);
3261                 }, function() {
3262                     currentDeferred.resolve(true, arguments);
3263                 });
3264                 deferreds.push(currentDeferred);
3265             });
3266 
3267             $.when.apply($, deferreds).then(function() {
3268                 var failures = [],
3269                     successes = [];
3270 
3271                 $.each(arguments, function(i, args) {
3272                     // If we resolved with `true` as the first parameter
3273                     // we have a failure, a success otherwise
3274                     var target = args[0] ? failures : successes,
3275                         data = args[1];
3276                     // Push either all arguments or the only one
3277                     target.push(data.length === 1 ? data[0] : args);
3278                 });
3279 
3280                 if(failures.length) {
3281                     return result.reject.apply(result, failures);
3282                 }
3283 
3284                 return result.resolve.apply(result, successes);
3285             });
3286 
3287             return result;
3288         },
3289 
3290         /**
3291          * Private method to format a given string by replacing the place holders (like {0}) with the
3292          * corresponding supplied arguments. For example, calling this method as follows:
3293          *     formatString("Hello {0}, {1} rocks!", "there", "Finesse");
3294          * results in the following output string:
3295          *     "Hello there, Finesse rocks!"
3296          * 
3297          * @param  {String} format - a string that holds the place holder to be replaced
3298          * 
3299          * @returns {String} - string where the place holders are replaced with respective values
3300          * @private
3301          */
3302         formatString : function(format) {
3303             if (!format || arguments.length <= 1) {
3304                 return format;
3305             }
3306 
3307             var i, retStr = format;
3308             for (i = 1; i < arguments.length; i += 1) {
3309                 retStr = retStr.replace(new RegExp("\\{" + (i - 1) + "\\}", "g"), arguments[i]);
3310             }
3311 
3312             // in order to fix French text with single quotes in it, we need to replace \' with '
3313             return retStr.replace(/\\'/g, "'");
3314         },
3315         
3316         /**
3317          *  @private
3318          *  This method is used to make the scheme and port secure when the admin gadgets are loaded in some other container.
3319          *  @param {object} config - The config which is passed to client services to handle restRequest in secure or non secure mode.  
3320          *  
3321          *  @returns {object} config - The modified config if scheme was https.
3322          */
3323         setSchemeAndPortForHttps : function(config) {
3324 			var scheme = window.location.protocol;
3325 			if(scheme === "https:") {
3326 				config["restScheme"] = "https";
3327 				config["localhostPort"] = "8445";
3328 			}
3329 			
3330 			return config;
3331         },
3332 
3333         /**
3334          * 
3335          * @private
3336          * If the browser tab is inactive, any calls to javascript function setTimeout(0,...) will not be executed
3337          * immediately since browser throttles events from background tabs. The delay sometimes could be about 5 seconds.
3338          * This utilitiy function provides a wrapper around ES5 promise to resolve this issue.
3339          * 
3340          * @param {function} funcOther Target function that will be invoked at the end of browser events.
3341          * 
3342          * @returns {Object} The promise
3343          */
3344         executeAsync: function(funcOther) {
3345             var promise = new Promise(
3346                 function( resolve, reject ) {              
3347                     resolve();
3348                 }
3349             );
3350             promise.then( funcOther );
3351 
3352             return promise;
3353         },
3354         
3355         /**
3356          * Extract hostname from a given url string
3357          */
3358         extractHostname : function (url) {
3359         	var hostname;
3360             //find & remove protocol (http, ftp, etc.) and get hostname
3361 
3362             if (url.indexOf("//") > -1) {
3363                 hostname = url.split('/')[2];
3364             }
3365             else {
3366                 hostname = url.split('/')[0];
3367             }
3368 
3369             //find & remove port number
3370             hostname = hostname.split(':')[0];
3371             //find & remove "?"
3372             hostname = hostname.split('?')[0];
3373 
3374             return hostname;
3375         },
3376         
3377         /**
3378          * Get the value of a querystring
3379          * @param  {String} field The field to get the value of
3380          * @param  {String} url   The URL to get the value from (optional)
3381          * @return {String}       The field value
3382          */
3383         getQueryString : function ( field, url ) {
3384             var href = url ? url : window.location.href;
3385             var reg = new RegExp( '[?&]' + field + '=([^&#]*)', 'i' );
3386             var string = reg.exec(href);
3387             return string ? decodeURIComponent(string[1]) : '';
3388 		},
3389 		
3390 		/**
3391 		 * Remove log schedule data from session storage
3392 		 */
3393 		removeLogScheduleDataFromSession : function () {
3394 			if (sessionStorage.getItem(finesse.utilities.Utilities.CONSTANTS.LOG_COLL_DATA)) {
3395 				sessionStorage.removeItem(finesse.utilities.Utilities.CONSTANTS.LOG_COLL_DATA);
3396 			}
3397 		},
3398 
3399 		/**
3400 		 * Remove log schedule array from session storage
3401 		 */
3402 		removeScheduleLogDataForFailoverFromSession : function () {
3403 			var logScheduleData = JSON.parse(sessionStorage.getItem(finesse.utilities.Utilities.CONSTANTS.LOG_COLL_DATA) || null);
3404 			if (logScheduleData && logScheduleData.scheduledLogArrayData) {
3405 					finesse.clientservices.ClientServices.log('Utilities.removeScheduleLogDataForFailoverFromSession(): Deleting the scheduled array data from session');
3406 					delete logScheduleData['scheduledLogArrayData'];
3407 					sessionStorage.setItem(window.finesse.utilities.Utilities.CONSTANTS.LOG_COLL_DATA, JSON.stringify(logScheduleData));
3408 				}
3409         },
3410         
3411        /**
3412          * Shows retry modal with spinner
3413          * @private
3414          * @param options
3415          * options.loginFailureRetryInterval : Retry Interval after which perRetryCallback will be called
3416          * options.retryAttempted :  Retry attempts that has already been made , should start from 1
3417          * options.loginFailureRetryAttempts :  Total numbers of re- attempts to be made
3418          * options.messageKey : Message key inside modal (will be pulled from properties file)
3419          * options.allRetriesFailedCallback: Callback function called when all retry are exhausted , it is followed by perRetryCallback
3420          * options.perRetryCallback : Callback for single retry attempt that needs to be made (Called when interval reduces to 0)
3421          * @returns boolean - return true if showRetry modal initialization was successful, false when retry attempts are greater than retryAttempted
3422          *  
3423 	    */
3424         showRetryModal: function (options) {
3425             return window.finesse.Dialog.showRetryModal(options);
3426         },
3427 
3428         isAbsoluteUrl: function(url) {
3429             if(url) {
3430                 return /^(http|https):\/\//i.test(url);
3431             }
3432 
3433             return false;   
3434         }
3435     };
3436 
3437     window.finesse = window.finesse || {};
3438     window.finesse.utilities = window.finesse.utilities || {};
3439     window.finesse.utilities.Utilities = Utilities;
3440     
3441     return Utilities;
3442 });
3443 
3444 /**
3445  * Allows gadgets to call the log function to publish client logging messages over the hub.
3446  *
3447  * @requires OpenAjax
3448  */
3449 /** @private */
3450 define('cslogger/ClientLogger',[], function () {
3451 
3452     var ClientLogger = ( function () { /** @lends finesse.cslogger.ClientLogger.prototype */
3453         var _hub, _logTopic, _originId, _sessId, _host,
3454             MONTH = { 0 : "Jan", 1 : "Feb", 2 : "Mar", 3 : "Apr", 4 : "May", 5 : "Jun", 
3455                       6 : "Jul", 7 : "Aug", 8 : "Sep", 9 : "Oct", 10 : "Nov", 11 : "Dec"},
3456  
3457         /**
3458          * Gets timestamp drift stored in sessionStorage
3459          * @returns drift in seconds if it is set in sessionStorage otherwise returns undefined.
3460          * @private
3461         */
3462         getTsDrift = function() {
3463             if (window.sessionStorage.getItem('clientTimestampDrift') !== null) {
3464                 return parseInt(window.sessionStorage.getItem('clientTimestampDrift'), 10);
3465             }
3466             else { 
3467                 return undefined;
3468             }
3469         },
3470          
3471         /**
3472           * Sets timestamp drift in sessionStorage
3473           * @param delta is the timestamp drift between server.and client.
3474           * @private
3475          */
3476         setTsDrift = function(delta) {
3477              window.sessionStorage.setItem('clientTimestampDrift', delta.toString());
3478         },
3479           
3480         /**
3481          * Gets Finesse server timezone offset from GMT in seconds 
3482          * @returns offset in seconds if it is set in sessionStorage otherwise returns undefined.
3483          * @private
3484         */
3485         getServerOffset = function() {
3486             if (window.sessionStorage.getItem('serverTimezoneOffset') !== null) {
3487                 return parseInt(window.sessionStorage.getItem('serverTimezoneOffset'), 10);
3488             }
3489             else { 
3490                 return undefined;
3491             }
3492         },
3493          
3494         /**
3495           * Sets server timezone offset 
3496           * @param sec is the server timezone GMT offset in seconds.
3497           * @private
3498          */
3499         setServerOffset = function(sec) {
3500              window.sessionStorage.setItem('serverTimezoneOffset', sec.toString());
3501         },
3502  
3503         /**
3504          * Checks to see if we have a console.
3505          * @returns Whether the console object exists.
3506          * @private
3507          */
3508         hasConsole = function () {
3509             try {
3510                 if (window.console !== undefined) {
3511                     return true;
3512                 }
3513             } 
3514             catch (err) {
3515               // ignore and return false
3516             }
3517     
3518             return false;
3519         },
3520         
3521         /**
3522          * Gets a short form (6 character) session ID from sessionStorage
3523          * @private
3524         */
3525         getSessId = function() {
3526             if (!_sessId) {
3527                //when _sessId not defined yet, initiate it
3528                if (window.sessionStorage.getItem('enableLocalLog') === 'true') {
3529                   _sessId= " "+window.sessionStorage.getItem('finSessKey');
3530                }
3531                else {
3532                   _sessId=" ";
3533                }
3534             }
3535             return _sessId;
3536          },
3537 
3538         /**
3539          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
3540          * @param num is the number to pad to 2 digits
3541          * @returns a two digit padded string
3542          * @private
3543          */
3544         padTwoDigits = function (num)
3545         {
3546             return (num < 10) ? '0' + num : num;
3547         },
3548         
3549         /**
3550          * Pads a single digit number for display purposes (e.g. '4' shows as '004')
3551          * @param num is the number to pad to 3 digits
3552          * @returns a three digit padded string
3553          * @private
3554          */
3555         padThreeDigits = function (num)
3556         {
3557             if (num < 10)
3558             {
3559               return '00'+num;
3560             }
3561             else if (num < 100)
3562             {
3563               return '0'+num;
3564             }
3565             else  
3566             {
3567                return num;
3568             }
3569         },
3570               
3571         /**
3572          * Compute the "hour"
3573          * 
3574          * @param s is time in seconds
3575          * @returns {String} which is the hour
3576          * @private
3577          */
3578         ho = function (s) {
3579              return ((s/60).toString()).split(".")[0];
3580         },
3581           
3582         /**
3583          * Gets local timezone offset string.
3584          * 
3585          * @param t is the time in seconds
3586          * @param s is the separator character between hours and minutes, e.g. ':'
3587          * @returns {String} is local timezone GMT offset in the following format: [+|-]hh[|:]MM
3588          * @private
3589          */
3590         getGmtOffString = function (min,s) {
3591             var t, sign;
3592             if (min<0) {
3593                t = -min;
3594                sign = "-";
3595             }
3596             else {
3597                t = min;
3598                sign = "+";
3599             }
3600             
3601             if (s===':') {
3602                 return sign+padTwoDigits(ho(t))+s+padTwoDigits(t%60);
3603             }
3604             else {
3605                 return sign+padTwoDigits(ho(t))+padTwoDigits(t%60);
3606             }    
3607         },
3608 
3609         /**
3610          * Gets short form of a month name in English 
3611          * 
3612          * @param monthNum is zero-based month number 
3613          * @returns {String} is short form of month name in English
3614          * @private
3615          */
3616         getMonthShortStr = function (monthNum) {
3617             var result;
3618             try {
3619                 result = MONTH[monthNum];
3620             } 
3621             catch (err) {
3622                 if (hasConsole()) {
3623                     window.console.log("Month must be between 0 and 11");
3624                 }
3625             }
3626             return result;
3627         },
3628           
3629         /**
3630           * Gets a timestamp.
3631           * @param aDate is a javascript Date object
3632           * @returns {String} is a timestamp in the following format: yyyy-mm-ddTHH:MM:ss.SSS [+|-]HH:MM
3633           * @private
3634           */
3635         getDateTimeStamp = function (aDate)
3636         {
3637             var date, off, timeStr;
3638             if (aDate === null) {
3639                 date = new Date();
3640             }
3641             else {
3642                 date = aDate;
3643             }
3644             off = -1*date.getTimezoneOffset();
3645             timeStr = date.getFullYear().toString() + "-" +
3646                       padTwoDigits(date.getMonth()+1) + "-" +
3647                       padTwoDigits (date.getDate()) + "T"+
3648                       padTwoDigits(date.getHours()) + ":" + 
3649                       padTwoDigits(date.getMinutes()) + ":" +
3650                       padTwoDigits(date.getSeconds())+"." + 
3651                       padThreeDigits(date.getMilliseconds()) + " "+
3652                       getGmtOffString(off, ':');
3653     
3654             return timeStr;
3655         },
3656         
3657         /**
3658          * Gets drift-adjusted timestamp.
3659          * @param aTimestamp is a timestamp in milliseconds
3660          * @param drift is a timestamp drift in milliseconds
3661          * @param serverOffset is a timezone GMT offset in minutes
3662          * @returns {String} is a timestamp in the Finesse server log format, e.g. Jan 07 2104 HH:MM:ss.SSS -0500
3663          * @private
3664          */
3665         getDriftedDateTimeStamp = function (aTimestamp, drift, serverOffset)
3666         {
3667            var date, timeStr, localOffset;
3668            if (aTimestamp === null) {
3669                return "--- -- ---- --:--:--.--- -----";
3670            }
3671            else if (drift === undefined || serverOffset === undefined) {
3672                if (hasConsole()) {
3673                    window.console.log("drift or serverOffset must be a number");
3674                }
3675                return "--- -- ---- --:--:--.--- -----";
3676            }
3677            else {
3678                //need to get a zone diff in minutes
3679                localOffset = (new Date()).getTimezoneOffset();
3680                date = new Date(aTimestamp+drift+(localOffset+serverOffset)*60000);
3681                timeStr = getMonthShortStr(date.getMonth()) + " "+
3682                          padTwoDigits (date.getDate())+ " "+
3683                          date.getFullYear().toString() + " "+
3684                          padTwoDigits(date.getHours()) + ":" + 
3685                          padTwoDigits(date.getMinutes()) + ":" +
3686                          padTwoDigits(date.getSeconds())+"." + 
3687                          padThreeDigits(date.getMilliseconds())+" "+
3688                          getGmtOffString(serverOffset, '');
3689                 return timeStr;
3690             }
3691         },
3692     
3693         /**
3694         * Logs a message to a hidden textarea element on the page
3695         *
3696         * @param msg is the string to log.
3697         * @private
3698         */
3699         writeToLogOutput = function (msg) {
3700             var logOutput = document.getElementById("finesseLogOutput");
3701     
3702             if (logOutput === null)
3703             {
3704                 logOutput = document.createElement("textarea");
3705                 logOutput.id = "finesseLogOutput";
3706                 logOutput.style.display = "none";
3707                 document.body.appendChild(logOutput);
3708             }
3709     
3710             if (logOutput.value === "")
3711             {
3712                 logOutput.value = msg;
3713             }
3714             else
3715             {
3716                 logOutput.value = logOutput.value + "\n" + msg;
3717             }
3718         },
3719 
3720         /**
3721         * Logs a message to console
3722         * @param str is the string to log.
3723         * @private
3724         */
3725         logToConsole = function (str, error)
3726         {
3727             var now, msg, timeStr, driftedTimeStr, sessKey=getSessId();
3728             now = new Date();
3729             timeStr = getDateTimeStamp(now);
3730             if (getTsDrift() !== undefined) {
3731                 driftedTimeStr = getDriftedDateTimeStamp(now.getTime(), getTsDrift(), getServerOffset());
3732             }
3733             else {
3734                driftedTimeStr = getDriftedDateTimeStamp(null, 0, 0);
3735             }
3736             msg = timeStr + ":"+sessKey+": "+ _host + ": "+driftedTimeStr+ ": " + str;
3737             // Log to console
3738             if (hasConsole()) {
3739                 if (error) {
3740                     window.console.error(msg, error);
3741                 } else {
3742                     window.console.log(msg);
3743                 }
3744             }
3745     
3746             //Uncomment to print logs to hidden textarea.
3747             //writeToLogOutput(msg);
3748     
3749             return msg;
3750         };
3751         return {
3752     
3753             /**
3754              * Publishes a Log Message over the hub.
3755              *
3756              * @param {String} message
3757              *     The string to log.
3758              * @param {Object} error - optional
3759              *     Javascript error object
3760              * @example
3761              * _clientLogger.log("This is some important message for MyGadget");
3762              * 
3763              */
3764             log : function (message, error) {
3765                 if(_hub) {
3766                     _hub.publish(_logTopic, logToConsole(_originId + message, error));
3767                 }
3768             },
3769             
3770             /**
3771              * @class
3772              * Allows gadgets to call the log function to publish client logging messages over the hub.
3773              * 
3774              * @constructs
3775              */
3776             _fakeConstuctor: function () {
3777                 /* This is here so we can document init() as a method rather than as a constructor. */
3778             },
3779             
3780             /**
3781              * Initiates the client logger with a hub a gadgetId and gadget's config object.
3782              * @param {Object} hub
3783              *      The hub to communicate with.
3784              * @param {String} gadgetId
3785              *      A unique string to identify which gadget is doing the logging.
3786              * @param {finesse.gadget.Config} config
3787              *      The config object used to get host name for that thirdparty gadget
3788              * @example
3789              * var _clientLogger = finesse.cslogger.ClientLogger;
3790              * _clientLogger.init(gadgets.Hub, "MyGadgetId", config);
3791              * 
3792              */
3793             init: function (hub, gadgetId, config) {
3794                 _hub = hub;
3795                 _logTopic = "finesse.clientLogging." + gadgetId;
3796                 _originId = gadgetId + " : ";
3797                 if ((config === undefined) || (config === "undefined")) 
3798                 {
3799                      _host = ((finesse.container && finesse.container.Config && finesse.container.Config.host)?finesse.container.Config.host : "?.?.?.?");
3800                  } 
3801                 else 
3802                 {
3803                      _host = ((config && config.host)?config.host : "?.?.?.?");
3804                  }
3805             }
3806         };
3807     }());
3808     
3809     window.finesse = window.finesse || {};
3810     window.finesse.cslogger = window.finesse.cslogger || {};
3811     window.finesse.cslogger.ClientLogger = ClientLogger;
3812     
3813     finesse = finesse || {};
3814     /** @namespace Supports writing messages to a central log. */
3815     finesse.cslogger = finesse.cslogger || {};
3816 
3817     return ClientLogger;
3818 });
3819 
3820 /** The following comment is to prevent jslint errors about 
3821  * using variables before they are defined.
3822  */
3823 /*global finesse*/
3824 
3825 /**
3826  * Initiated by the Master to create a shared BOSH connection.
3827  *
3828  * @requires Utilities
3829  */
3830 
3831 /**
3832  * @class
3833  * Establishes a shared event connection by creating a communication tunnel
3834  * with the notification server and consume events which could be published.
3835  * Public functions are exposed to register to the connection status information
3836  * and events.
3837  * @constructor
3838  * @param {String} host
3839  *     The host name/ip of the Finesse server.
3840  * @throws {Error} If required constructor parameter is missing.
3841  */
3842 /** @private */
3843 define('clientservices/MasterTunnel',["utilities/Utilities", "cslogger/ClientLogger"], function (Utilities, ClientLogger) {
3844      var MasterTunnel = function (host, scheme) { 
3845         if (typeof host !== "string" || host.length === 0) {
3846             throw new Error("Required host parameter missing.");
3847         }
3848 
3849         var
3850 
3851         /**
3852          * Flag to indicate whether the tunnel frame is loaded.
3853          * @private
3854          */
3855         _isTunnelLoaded = false,
3856 
3857         /**
3858          * Short reference to the Finesse utility.
3859          * @private
3860          */
3861         _util = Utilities,
3862 
3863         /**
3864          * The URL with host and port to the Finesse server.
3865          * @private
3866          */
3867         _tunnelOrigin,
3868 
3869         /**
3870          * Location of the tunnel HTML URL.
3871          * @private
3872          */
3873         _tunnelURL,
3874         
3875         /**
3876          * The port on which to connect to the Finesse server to load the eventing resources.
3877          * @private
3878          */
3879         _tunnelOriginPort,
3880         
3881         /**
3882          * Flag to indicate whether we have processed the tunnel config yet.
3883          * @private
3884          */
3885         _isTunnelConfigInit = false,
3886 
3887         /**
3888          * The tunnel frame window object.
3889          * @private
3890          */
3891         _tunnelFrame,
3892 
3893         /**
3894          * The handler registered with the object to be invoked when an event is
3895          * delivered by the notification server.
3896          * @private
3897          */
3898         _eventHandler,
3899         
3900         /**
3901          * The handler registered with the object to be invoked when presence is
3902          * delivered by the notification server.
3903          * @private
3904          */
3905         _presenceHandler,
3906 
3907         /**
3908          * The handler registered with the object to be invoked when the BOSH
3909          * connection has changed states. The object will contain the "status"
3910          * property and a "resourceID" property only if "status" is "connected".
3911          * @private
3912          */
3913         _connInfoHandler,
3914 
3915         /**
3916          * The last connection status published by the JabberWerx library.
3917          * @private
3918          */
3919         _statusCache,
3920 
3921         /**
3922          * The last event sent by notification server.
3923          * @private
3924          */
3925         _eventCache,
3926 
3927         /**
3928          * The ID of the user logged into notification server.
3929          * @private
3930          */
3931         _id,
3932    
3933         /**
3934          * The domain of the XMPP server, representing the portion of the JID
3935          * following '@': userid@domain.com
3936          * @private
3937          */
3938         _xmppDomain,
3939 
3940         /**
3941          * The password of the user logged into notification server.
3942          * @private
3943          */
3944         _password,
3945 
3946         /**
3947          * The jid of the pubsub service on the XMPP server
3948          * @private
3949          */
3950         _pubsubDomain,
3951 
3952         /**
3953          * The resource to use for the BOSH connection.
3954          * @private
3955          */
3956         _resource,
3957 
3958         /**
3959          * The resource ID identifying the client device (that we receive from the server).
3960          * @private
3961          */
3962         _resourceID,
3963         
3964         /**
3965          * The xmpp connection protocol type.
3966          * @private
3967          */
3968         _notificationConnectionType,
3969 
3970         /**
3971          * The different types of messages that could be sent to the parent frame.
3972          * The types here should be understood by the parent frame and used to
3973          * identify how the message is formatted.
3974          * @private
3975          */
3976         _TYPES = {
3977             EVENT: 0,
3978             ID: 1,
3979             PASSWORD: 2,
3980             RESOURCEID: 3,
3981             STATUS: 4,
3982             XMPPDOMAIN: 5,
3983             PUBSUBDOMAIN: 6,
3984             SUBSCRIBE: 7,
3985             UNSUBSCRIBE: 8,
3986             PRESENCE: 9,
3987             CONNECT_REQ: 10,
3988             DISCONNECT_REQ: 11,
3989             NOTIFICATION_CONNECTION_TYPE: 12,
3990             LOGGING: 13,
3991             SUBSCRIPTIONS_REQ: 14,
3992             XMPP_DISCONNECT_PROPERTIES:15
3993         },
3994 
3995         _handlers = {
3996             subscribe: {},
3997             unsubscribe: {}
3998         },
3999         
4000 
4001         /**
4002          * Create a connection info object.
4003          * @returns {Object}
4004          *     A connection info object containing a "status" and "resourceID".
4005          * @private
4006          */
4007         _createConnInfoObj = function () {
4008             return {
4009                 status: _statusCache,
4010                 resourceID: _resourceID
4011             };
4012         },
4013 
4014         /**
4015          * Utility function which sends a message to the dynamic tunnel frame
4016          * event frame formatted as follows: "type|message".
4017          * @param {Number} type
4018          *     The category type of the message.
4019          * @param {String} message
4020          *     The message to be sent to the tunnel frame.
4021          * @private
4022          */
4023         _sendMessage = function (type, message) {
4024             message = type + "|" + message;
4025             _util.sendMessage(message, _tunnelFrame, _tunnelOrigin);
4026         },
4027 
4028         /**
4029          * Utility to process the response of a subscribe request from
4030          * the tunnel frame, then invoking the stored callback handler
4031          * with the respective data (error, when applicable)
4032          * @param {String} data
4033          *     The response in the format of "node[|error]"
4034          * @private
4035          */
4036         _processSubscribeResponse = function (data) {
4037             var dataArray = data.split("|"),
4038             node = dataArray[0],
4039             err;
4040             
4041             //Error is optionally the second item in the array
4042             if (dataArray.length) {
4043                 err = dataArray[1];
4044             }
4045             
4046             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
4047             if (_handlers.subscribe[node]) {
4048                 _handlers.subscribe[node](err);
4049                 delete _handlers.subscribe[node];
4050             }
4051         },
4052 
4053         /**
4054          * Utility to process the response of an unsubscribe request from
4055          * the tunnel frame, then invoking the stored callback handler
4056          * with the respective data (error, when applicable)
4057          * @param {String} data
4058          *     The response in the format of "node[|error]"
4059          * @private
4060          */
4061         _processUnsubscribeResponse = function (data) {
4062             var dataArray = data.split("|"),
4063             node = dataArray[0],
4064             err;
4065             
4066             //Error is optionally the second item in the array
4067             if (dataArray.length) {
4068                 err = dataArray[1];
4069             }
4070             
4071             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
4072             if (_handlers.unsubscribe[node]) {
4073                 _handlers.unsubscribe[node](err);
4074                 delete _handlers.unsubscribe[node];
4075             }
4076         },
4077 
4078 
4079         /**
4080          * Utility to process the reponse of an getSubscriptions request from
4081          * the tunnel frame, then invoking the stored callback handler
4082          * with the respective data (error, when applicable)
4083          * @param {String} data
4084          *     The response containing subscriptions
4085          * @private
4086          */
4087         _processAllSubscriptionsResponse = function (data) {
4088             var dataArray = data.split("|"),
4089             content = dataArray[0],
4090             err;
4091 
4092             //Error is optionally the second item in the array
4093             if (dataArray.length) {
4094                 err = dataArray[1];
4095             }
4096             
4097             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
4098             if (_handlers.subscriptions) {
4099                 _handlers.subscriptions(content, err);
4100                 delete _handlers.subscriptions;
4101             }
4102         },
4103 
4104         /**
4105          * Handler for messages delivered by window.postMessage. Listens for events
4106          * published by the notification server, connection status published by
4107          * the JabberWerx library, and the resource ID created when the BOSH
4108          * connection has been established.
4109          * @param {Object} e
4110          *     The message object as provided by the window.postMessage feature.
4111          * @private
4112          */
4113         _messageHandler = function (e) {
4114         	if (typeof e.data !== 'string') {
4115         		return;
4116         	}
4117             var
4118 
4119             //Extract the message type and message data. The expected format is
4120             //"type|data" where type is a number represented by the TYPES object.
4121             delimPos = e.data.indexOf("|"),
4122             type = Number(e.data.substr(0, delimPos)),
4123             data =  e.data.substr(delimPos + 1);
4124             
4125             //Accepts messages and invoke the correct registered handlers.
4126             switch (type) {
4127             case _TYPES.EVENT:
4128                 _eventCache = data;
4129                 if (typeof _eventHandler === "function") {
4130                     _eventHandler(data);
4131                 }
4132                 break;
4133             case _TYPES.STATUS:
4134                 _statusCache = data;
4135 
4136                 //A "loaded" status means that the frame is ready to accept
4137                 //credentials for establishing a BOSH connection.
4138                 if (data === "loaded") {
4139                     _isTunnelLoaded = true;
4140                     if(_resource) {
4141                         _sendMessage(_TYPES.RESOURCEID, _resource);
4142                     }
4143                     var xmppProperties = JSON.stringify({xmppRequestTimeout: finesse.container.Config.xmppRequestTimeout,
4144                         xmppFailoverDetectionPingInterval: finesse.container.Config.xmppFailoverDetectionPingInterval,
4145                         xmppFailoverRequestMaxRetry: finesse.container.Config.xmppFailoverRequestMaxRetry
4146                     });
4147                	    _sendMessage(_TYPES.NOTIFICATION_CONNECTION_TYPE, _notificationConnectionType);
4148                     _sendMessage(_TYPES.ID, _id);
4149                     _sendMessage(_TYPES.XMPPDOMAIN, _xmppDomain);
4150                     _sendMessage(_TYPES.PASSWORD, _password);
4151                     _sendMessage(_TYPES.PUBSUBDOMAIN, _pubsubDomain);
4152                     _sendMessage(_TYPES.XMPP_DISCONNECT_PROPERTIES,xmppProperties);
4153 
4154 
4155                 } else if (typeof _connInfoHandler === "function") {
4156                     _connInfoHandler(_createConnInfoObj());
4157                 }
4158                 break;
4159             case _TYPES.RESOURCEID:
4160                 _resourceID = data;
4161                 break;
4162             case _TYPES.SUBSCRIBE:
4163                 _processSubscribeResponse(data);
4164                 break;
4165             case _TYPES.UNSUBSCRIBE:
4166                 _processUnsubscribeResponse(data);
4167                 break;
4168             case _TYPES.SUBSCRIPTIONS_REQ:
4169                 _processAllSubscriptionsResponse(data);
4170                 break;
4171             case _TYPES.PRESENCE:
4172                 if (typeof _presenceHandler === "function") {
4173                     _presenceHandler(data);
4174                 }
4175                 break;
4176             case _TYPES.LOGGING:
4177             	ClientLogger.log(data);
4178                 break;
4179             
4180             default:
4181                 break;
4182             }
4183         },
4184 
4185         /**
4186          * Initialize the tunnel config so that the url can be http or https with the appropriate port
4187          * @private
4188          */
4189         _initTunnelConfig = function () {
4190             if (_isTunnelConfigInit === true) {
4191                 return;
4192             }
4193             
4194             // For UCCX, Event tunnel is established via finesse desktop port 
4195             if (window.finesse.container.Config.deploymentType === 'UCCX') {
4196                 _tunnelOriginPort = window.location.port;
4197             } else {
4198                 _tunnelOriginPort = "7443";
4199             }
4200 
4201             if (scheme) {
4202                 _tunnelOrigin = scheme + "://" + host + ":" + _tunnelOriginPort;
4203             } else {
4204                 _tunnelOrigin = "https://" + host + ":" + _tunnelOriginPort;
4205             }
4206             _tunnelURL = _tunnelOrigin + "/tunnel/";
4207             
4208             _isTunnelConfigInit = true;
4209         },
4210 
4211         /**
4212          * Create the tunnel iframe which establishes the shared BOSH connection.
4213          * Messages are sent across frames using window.postMessage.
4214          * @private
4215          */
4216         _createTunnel = function () {
4217             var tunnelID = ((self === parent) ? "tunnel-frame" : "autopilot-tunnel-frame"),
4218             iframe = document.createElement("iframe");         
4219             iframe.style.display = "none";
4220             iframe.setAttribute("id", tunnelID);
4221             iframe.setAttribute("name", tunnelID);
4222             iframe.setAttribute("src", _tunnelURL);
4223             document.body.appendChild(iframe);
4224             _tunnelFrame = window.frames[tunnelID];
4225         };
4226 
4227         /**
4228          * Sends a message via postmessage to the EventTunnel to attempt to connect to the XMPP server
4229          * @private
4230          */
4231         this.makeConnectReq = function () {
4232             _sendMessage(_TYPES.PASSWORD, _password);
4233         };
4234         
4235         /**
4236          * @private
4237          * Returns the host of the Finesse server.
4238          * @returns {String}
4239          *     The host specified during the creation of the object.
4240          */
4241         this.getHost = function () {
4242             return host;
4243         };
4244 
4245         /**
4246          * @private
4247          * The resource ID of the user who is logged into the notification server.
4248          * @returns {String}
4249          *     The resource ID generated by the notification server.
4250          */
4251         this.getResourceID = function () {
4252             return _resourceID;
4253         };
4254 
4255         /**
4256          * @private
4257          * Indicates whether the tunnel frame is loaded.
4258          * @returns {Boolean}
4259          *     True if the tunnel frame is loaded, false otherwise.
4260          */
4261         this.isTunnelLoaded = function () {
4262             return _isTunnelLoaded;
4263         };
4264 
4265         /**
4266          * @private
4267          * The location of the tunnel HTML URL.
4268          * @returns {String}
4269          *     The location of the tunnel HTML URL.
4270          */
4271         this.getTunnelURL = function () {
4272             return _tunnelURL;
4273         };
4274 
4275         /**
4276          * @private
4277          * Tunnels a subscribe request to the eventing iframe.
4278          * @param {String} node
4279          *     The node to subscribe to
4280          * @param {Function} handler
4281          *     Handler to invoke upon success or failure
4282          */
4283         this.subscribe = function (node, handler) {
4284             if (handler && typeof handler !== "function") {
4285                 throw new Error("Parameter is not a function.");
4286             }
4287             _handlers.subscribe[node] = handler;
4288             _sendMessage(_TYPES.SUBSCRIBE, node);
4289         };
4290 
4291 
4292         /**
4293          * @private
4294          * Tunnels a get subscription request to the eventing iframe.
4295          * @param {Function} handler
4296          *     Handler to invoke upon success or failure
4297          */
4298         this.getSubscriptions = function (handler) {
4299             if (handler && typeof handler !== "function") {
4300                 throw new Error("Parameter is not a function.");
4301             }
4302             _handlers.subscriptions = handler;
4303             _sendMessage(_TYPES.SUBSCRIPTIONS_REQ);
4304         };
4305 
4306         /**
4307          * @private
4308          * Tunnels an unsubscribe request to the eventing iframe.
4309          * @param {String} node
4310          *     The node to unsubscribe from
4311          * @param {Function} handler
4312          *     Handler to invoke upon success or failure
4313          */
4314         this.unsubscribe = function (node, handler) {
4315             if (handler && typeof handler !== "function") {
4316                 throw new Error("Parameter is not a function.");
4317             }
4318             _handlers.unsubscribe[node] = handler;
4319             _sendMessage(_TYPES.UNSUBSCRIBE, node);
4320         };
4321 
4322         /**
4323          * @private
4324          * Registers a handler to be invoked when an event is delivered. Only one
4325          * is registered at a time. If there has already been an event that was
4326          * delivered, the handler will be invoked immediately.
4327          * @param {Function} handler
4328          *     Invoked when an event is delivered through the event connection.
4329          */
4330         this.registerEventHandler = function (handler) {
4331             if (typeof handler !== "function") {
4332                 throw new Error("Parameter is not a function.");
4333             }
4334             _eventHandler = handler;
4335             if (_eventCache) {
4336                 handler(_eventCache);
4337             }
4338         };
4339 
4340         /**
4341          * @private
4342          * Unregisters the event handler completely.
4343          */
4344         this.unregisterEventHandler = function () {
4345             _eventHandler = undefined;
4346         };
4347         
4348         /**
4349          * @private
4350          * Registers a handler to be invoked when a presence event is delivered. Only one
4351          * is registered at a time. 
4352          * @param {Function} handler
4353          *     Invoked when a presence event is delivered through the event connection.
4354          */
4355         this.registerPresenceHandler = function (handler) {
4356             if (typeof handler !== "function") {
4357                 throw new Error("Parameter is not a function.");
4358             }
4359             _presenceHandler = handler;
4360         };
4361         
4362         /**
4363          * @private
4364          * Unregisters the presence event handler completely.
4365          */
4366         this.unregisterPresenceHandler = function () {
4367             _presenceHandler = undefined;
4368         };
4369 
4370         /**
4371          * @private
4372          * Registers a handler to be invoked when a connection status changes. The
4373          * object passed will contain a "status" property, and a "resourceID"
4374          * property, which will contain the most current resource ID assigned to
4375          * the client. If there has already been an event that was delivered, the
4376          * handler will be invoked immediately.
4377          * @param {Function} handler
4378          *     Invoked when a connection status changes.
4379          */
4380         this.registerConnectionInfoHandler = function (handler) {
4381             if (typeof handler !== "function") {
4382                 throw new Error("Parameter is not a function.");
4383             }
4384             _connInfoHandler = handler;
4385             if (_statusCache) {
4386                 handler(_createConnInfoObj());
4387             }
4388         };
4389 
4390         /**
4391          * @private
4392          * Unregisters the connection information handler.
4393          */
4394         this.unregisterConnectionInfoHandler = function () {
4395             _connInfoHandler = undefined;
4396         };
4397 
4398         /**
4399          * @private
4400          * Start listening for events and create a event tunnel for the shared BOSH
4401          * connection.
4402          * @param {String} id
4403          *     The ID of the user for the notification server.
4404          * @param {String} password
4405          *     The password of the user for the notification server.
4406          * @param {String} xmppDomain
4407          *     The XMPP domain of the notification server
4408          * @param {String} pubsubDomain
4409          *     The location (JID) of the XEP-0060 PubSub service
4410          * @param {String} resource
4411          *     The resource to connect to the notification servier with.
4412          * @param {String} notificationConnectionType
4413          *     The xmpp connection protocol type : websocket or BOSH.
4414          */
4415         this.init = function (id, password, xmppDomain, pubsubDomain, resource, notificationConnectionType) {
4416             
4417             if (typeof id !== "string" || typeof password !== "string" || typeof xmppDomain !== "string" || typeof pubsubDomain !== "string" || typeof notificationConnectionType !== "string") {
4418                 throw new Error("Invalid or missing required parameters.");
4419             }
4420 
4421             _initTunnelConfig();
4422             
4423             _id = id;
4424             _password = password;
4425             _xmppDomain = xmppDomain;
4426             _pubsubDomain = pubsubDomain;
4427             _resource = resource;
4428             _notificationConnectionType = notificationConnectionType;
4429 
4430             //Attach a listener for messages sent from tunnel frame.
4431             _util.receiveMessage(_messageHandler, _tunnelOrigin);
4432 
4433             //Create the tunnel iframe which will establish the shared connection.
4434             _createTunnel();
4435         };
4436 
4437         //BEGIN TEST CODE//
4438 //        /**
4439 //         * Test code added to expose private functions that are used by unit test
4440 //         * framework. This section of code is removed during the build process
4441 //         * before packaging production code. The [begin|end]TestSection are used
4442 //         * by the build to identify the section to strip.
4443 //         * @ignore
4444 //         */
4445 //        this.beginTestSection = 0;
4446 //
4447 //        /**
4448 //         * @ignore
4449 //         */
4450 //        this.getTestObject = function () {
4451 //            //Load mock dependencies.
4452 //            var _mock = new MockControl();
4453 //            _util = _mock.createMock(finesse.utilities.Utilities);
4454 //
4455 //            return {
4456 //                //Expose mock dependencies
4457 //                mock: _mock,
4458 //                util: _util,
4459 //
4460 //                //Expose internal private functions
4461 //                types: _TYPES,
4462 //                createConnInfoObj: _createConnInfoObj,
4463 //                sendMessage: _sendMessage,
4464 //                messageHandler: _messageHandler,
4465 //                createTunnel: _createTunnel,
4466 //                handlers: _handlers,
4467 //                initTunnelConfig : _initTunnelConfig
4468 //            };
4469 //        };
4470 //
4471 //        /**
4472 //         * @ignore
4473 //         */
4474 //        this.endTestSection = 0;
4475 //        //END TEST CODE//
4476     };
4477     
4478     /** @namespace JavaScript class objects and methods to handle the subscription to Finesse events.*/
4479     finesse.clientservices = finesse.clientservices || {};
4480 
4481     window.finesse = window.finesse || {};
4482     window.finesse.clientservices = window.finesse.clientservices || {};
4483     window.finesse.clientservices.MasterTunnel = MasterTunnel;
4484 
4485     return MasterTunnel;
4486 
4487 });
4488 
4489 /**
4490  * Contains a list of topics used for client side pubsub.
4491  *
4492  */
4493 
4494 /** @private */
4495 define('clientservices/Topics',[], function () {
4496     
4497    var Topics = (function () {
4498 
4499         /**
4500          * @private
4501          * The namespace prepended to all Finesse topics.
4502          */
4503         this.namespace = "finesse.info";
4504     
4505         /**
4506          * @private
4507          * Gets the full topic name with the Finesse namespace prepended.
4508          * @param {String} topic
4509          *     The topic category.
4510          * @returns {String}
4511          *     The full topic name with prepended namespace.
4512          */
4513         var _getNSTopic = function (topic) {
4514             return this.namespace + "." + topic;
4515         };
4516         
4517         /** @scope finesse.clientservices.Topics */
4518         return {
4519             /** 
4520              * @private
4521              * Client side request channel. 
4522              */
4523             REQUESTS: _getNSTopic("requests"),
4524     
4525             /** 
4526              * @private
4527              * Client side response channel. 
4528              */
4529             RESPONSES: _getNSTopic("responses"),
4530 
4531             /** 
4532              * @private
4533              * Connection status. 
4534              */
4535             EVENTS_CONNECTION_INFO: _getNSTopic("connection"),
4536             
4537             /** 
4538              * @private
4539              * Presence channel 
4540              */
4541             PRESENCE: _getNSTopic("presence"),
4542             
4543             /**
4544              * Topic for listening to token refresh events.
4545              * The provided callback will be invoked when the access token is refreshed.
4546              * This event is only meant for updating the access token in gadget Config object
4547              */
4548             ACCESS_TOKEN_REFRESHED_EVENT: _getNSTopic("accessTokenRefresh"),
4549     
4550             /**
4551              * @private
4552              * Convert a Finesse REST URI to a OpenAjax compatible topic name.
4553              */
4554             getTopic: function (restUri) {
4555                 //The topic should not start with '/' else it will get replaced with
4556                 //'.' which is invalid.
4557                 //Thus, remove '/' if it is at the beginning of the string
4558                 if (restUri.indexOf('/') === 0) {
4559                     restUri = restUri.substr(1);
4560                 }
4561     
4562                 //Replace every instance of "/" with ".". This is done to follow the
4563                 //OpenAjaxHub topic name convention.
4564                 return restUri.replace(/\//g, ".");
4565             }
4566         };
4567     }());
4568     window.finesse = window.finesse || {};
4569     window.finesse.clientservices = window.finesse.clientservices || {};
4570     /** @private */
4571     window.finesse.clientservices.Topics = Topics;
4572     
4573     return Topics;
4574 });
4575 /** The following comment is to prevent jslint errors about 
4576  * using variables before they are defined.
4577  */
4578 /*global finesse*/
4579 
4580 /**
4581  * Registers with the MasterTunnel to receive events, which it
4582  *     could publish to the OpenAjax gadget pubsub infrastructure.
4583  *
4584  * @requires OpenAjax, finesse.clientservices.MasterTunnel, finesse.clientservices.Topics
4585  */
4586 
4587 /** @private */
4588 define('clientservices/MasterPublisher',[
4589     "clientservices/MasterTunnel",
4590     "clientservices/Topics",
4591     "utilities/Utilities"
4592 ],
4593 function (MasterTunnel, Topics, Utilities) {
4594     
4595      var MasterPublisher = function (tunnel, hub) {
4596         if (!(tunnel instanceof MasterTunnel)) {
4597             throw new Error("Required tunnel object missing or invalid.");
4598         }
4599 
4600         var
4601         
4602         ClientServices = finesse.clientservices.ClientServices,
4603 
4604         /**
4605          * Reference to the gadget pubsub Hub instance.
4606          * @private
4607          */
4608         _hub = hub,
4609 
4610         /**
4611          * Reference to the Topics class.
4612          * @private
4613          */
4614         _topics = Topics,
4615         
4616         /**
4617          * Reference to conversion utilities class.
4618          * @private
4619          */
4620         _utils = Utilities,
4621         
4622         /**
4623          * References to ClientServices logger methods
4624          * @private
4625          */
4626         _logger = {
4627             log: ClientServices.log
4628         },
4629         
4630         /**
4631          * Store the passed in tunnel.
4632          * @private
4633          */
4634         _tunnel = tunnel,
4635 
4636         /**
4637          * Caches the connection info event so that it could be published if there
4638          * is a request for it.
4639          * @private
4640          */
4641         _connInfoCache = {},
4642 
4643         /**
4644          * The types of possible request types supported when listening to the
4645          * requests channel. Each request type could result in different operations.
4646          * @private
4647          */
4648         _REQTYPES = {
4649             CONNECTIONINFO: "ConnectionInfoReq",
4650             SUBSCRIBE: "SubscribeNodeReq",
4651             UNSUBSCRIBE: "UnsubscribeNodeReq",
4652             SUBSCRIPTIONSINFO: "SubscriptionsInfoReq",
4653             CONNECT: "ConnectionReq"
4654         },
4655 
4656         /**
4657          * Will store list of nodes that have OF subscriptions created
4658          *     _nodesList[node][subscribing].reqIds[subid]
4659          *     _nodesList[node][active].reqIds[subid]
4660          *     _nodesList[node][unsubscribing].reqIds[subid]
4661          *     _nodesList[node][holding].reqIds[subid]
4662          * @private
4663          */
4664         _nodesList = {},
4665         
4666         /**
4667          * The states that a subscription can be in
4668          * @private
4669          */
4670         _CHANNELSTATES = {
4671             UNINITIALIZED: "Uninitialized",
4672             PENDING: "Pending",
4673             OPERATIONAL: "Operational"
4674         },
4675 
4676         /**
4677           * Checks if the payload is JSON 
4678           * @returns {Boolean}
4679           * @private
4680           */
4681         _isJsonPayload = function(event) {
4682             var delimStart, delimEnd, retval = false;
4683             
4684             try { 
4685               delimStart = event.indexOf('{');
4686               delimEnd = event.lastIndexOf('}');
4687 
4688               if ((delimStart !== -1 ) && (delimEnd === (event.length - 1))) {
4689                 retval = true;  //event contains JSON payload
4690               }
4691             } catch (err) {
4692               _logger.log("MasterPublisher._isJsonPayload() - Caught error: " + err);
4693             }
4694             return retval;
4695         },
4696         
4697                 /**
4698           * Parses a JSON event and then publishes.
4699           *
4700           * @param {String} event
4701           *     The full event payload.
4702           * @throws {Error} If the payload object is malformed.
4703           * @private
4704           */
4705         _parseAndPublishJSONEvent = function(event) {
4706             var topic, eventObj, publishEvent,
4707             delimPos = event.indexOf("{"),
4708             node, parser,
4709             eventJson = event,
4710             returnObj = {node: null, data: null};
4711 
4712             try {
4713                //Extract and strip the node path from the message
4714                if (delimPos > 0) 
4715                {
4716                   //We need to decode the URI encoded node path
4717                   //TODO: make sure this is kosher with OpenAjax topic naming
4718                   node = decodeURI(event.substr(0, delimPos));
4719                   eventJson = event.substr(delimPos);
4720                   
4721                   //Converting the node path to openAjaxhub topic
4722                   topic = _topics.getTopic(node);
4723                   
4724                   returnObj.node = node;
4725                   returnObj.payload = eventJson;
4726                } 
4727                else 
4728                {
4729                   _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] node is not given in postMessage: " + eventJson);
4730                   throw new Error("node is not given in postMessage: " + eventJson);
4731                }
4732 
4733                parser = _utils.getJSONParser();
4734 
4735                eventObj = parser.parse(eventJson);
4736                returnObj.data = eventObj;
4737 
4738             } catch (err) {
4739                _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] Malformed event payload: " + err);
4740                throw new Error("Malformed event payload : " + err);
4741             }
4742             
4743             _logger.log("MasterPublisher._parseAndPublishJSONEvent() - Received JSON event on node '" + node + "': " + eventJson); 
4744             
4745             publishEvent = {content : event, object : eventObj };
4746 
4747             //Publish event to proper event topic.
4748             if (topic && eventObj) {
4749                _hub.publish(topic, publishEvent);
4750             }
4751         },
4752         
4753         /**
4754           * Parses an XML event and then publishes.
4755           *
4756           * @param {String} event
4757           *     The full event payload.
4758           * @throws {Error} If the payload object is malformed.
4759           * @private
4760           */
4761         _parseAndPublishXMLEvent = function(event) {
4762             var topic, eventObj, publishEvent, restTopic,
4763             delimPos = event.indexOf("<"),
4764             node,
4765             eventXml = event;
4766             
4767             try {
4768                //Extract and strip the node path from the message
4769                if (delimPos > 0) {
4770                   //We need to decode the URI encoded node path
4771                   //TODO: make sure this is kosher with OpenAjax topic naming
4772                   node = decodeURI(event.substr(0, delimPos));
4773                   eventXml = event.substr(delimPos);
4774                   //Converting the node path to openAjaxhub topic
4775                   topic = _topics.getTopic(node);
4776                } else {
4777                   _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] node is not given in postMessage: " + eventXml);
4778                   throw new Error("node is not given in postMessage: " + eventXml);
4779                }
4780 
4781                eventObj = _utils.xml2JsObj(eventXml);
4782                   
4783            } catch (err) {
4784                _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] Malformed event payload: " + err);
4785                throw new Error("Malformed event payload : " + err);
4786            }
4787            
4788            _logger.log("MasterPublisher._parseAndPublishXMLEvent() - Received XML event on node '" + node + "': " + eventXml);
4789            
4790            publishEvent = {content : event, object : eventObj };
4791 
4792            //Publish event to proper event topic.
4793            if (topic && eventObj) {
4794             // Making sure only User object sent by User api response are updated in cache
4795             if(topic.indexOf('User.') > -1 && eventObj.Update && eventObj.Update.data && eventObj.Update.data.user) {
4796                 var removeDataElement = eventXml.substring(eventXml.indexOf('<user>'), eventXml.indexOf('</user>')+ 7);
4797                 /**
4798                  * event is for user object then replace <user> with <User>. 
4799                  * In case of xmpp event <user> element starts with small u letter. 
4800                  * Where as in rest response <User> element starts with capital U letter. 
4801                  */
4802                    var userObjXml = removeDataElement.replace("<user>", "<User>").replace("</user>", "</User>");
4803                    sessionStorage.setItem('User',userObjXml);
4804 
4805                }
4806                _hub.publish(topic, publishEvent);
4807            }
4808         },
4809         
4810         /**
4811          * Publishes events to the appropriate topic. The topic name is determined
4812          * by fetching the source value from the event.
4813          * @param {String} event
4814          *     The full event payload.
4815          * @throws {Error} If the payload object is malformed.
4816          * @private
4817          */
4818         _eventHandler = function (event) {
4819             
4820             //Handle JSON or XML events
4821             if (!_isJsonPayload(event))
4822             {
4823                //XML
4824                _parseAndPublishXMLEvent(event);
4825             }
4826             else
4827             {
4828                //JSON
4829                _parseAndPublishJSONEvent(event);
4830             }
4831         },
4832         
4833         
4834         /**
4835          * Handler for when presence events are sent through the MasterTunnel.
4836          * @returns {Object}
4837          *     A presence xml event.
4838          * @private
4839          */
4840         _presenceHandler = function (event) {
4841             var eventObj = _utils.xml2JsObj(event), publishEvent;
4842             
4843             publishEvent = {content : event, object : eventObj};
4844             
4845             if (eventObj) {
4846                 _hub.publish(_topics.PRESENCE, publishEvent);
4847             }
4848         },
4849 
4850         /**
4851          * Clone the connection info object from cache.
4852          * @returns {Object}
4853          *     A connection info object containing a "status" and "resourceID".
4854          * @private
4855          */
4856         _cloneConnInfoObj = function () {
4857             if (_connInfoCache) {
4858                 return {
4859                     status: _connInfoCache.status,
4860                     resourceID: _connInfoCache.resourceID
4861                 };
4862             } else {
4863                 return null;
4864             }
4865         },
4866 
4867         /**
4868          * Cleans up any outstanding subscribe/unsubscribe requests and notifies them of errors.
4869          * This is done if we get disconnected because we cleanup explicit subscriptions on disconnect.
4870          * @private
4871          */
4872         _cleanupPendingRequests = function () {
4873             var node, curSubid, errObj = {
4874                 error: {
4875                     errorType: "Disconnected",
4876                     errorMessage: "Outstanding request will never complete."
4877                 }
4878             };
4879 
4880             // Iterate through all outstanding subscribe requests to notify them that it will never return
4881             for (node in _nodesList) {
4882                 if (_nodesList.hasOwnProperty(node)) {
4883                     for (curSubid in _nodesList[node].subscribing.reqIds) {
4884                         if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
4885                             // Notify this outstanding subscribe request to give up and error out
4886                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4887                         }
4888                     }
4889                     for (curSubid in _nodesList[node].unsubscribing.reqIds) {
4890                         if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
4891                             // Notify this outstanding unsubscribe request to give up and error out
4892                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4893                         }
4894                     }
4895                 }
4896             }
4897         },
4898 
4899         /**
4900          * Publishes the connection info to the connection info topic.
4901          * @param {Object} connInfo
4902          *     The connection info object containing the status and resource ID.
4903          * @private
4904          */
4905         _connInfoHandler = function (connInfo) {
4906             var before = _connInfoCache.status;
4907             _connInfoCache = connInfo;
4908             _logger.log("MasterPublisher._connInfoHandler() - Connection status: " + connInfo.status);
4909             _hub.publish(_topics.EVENTS_CONNECTION_INFO, _cloneConnInfoObj());
4910             if (before === "connected" && connInfo.status !== "connected") {
4911                 // Fail all pending requests when we transition to disconnected
4912                 _cleanupPendingRequests();
4913             }
4914         },
4915 
4916         /**
4917          * Get's all the subscriptions in the Openfire through EventTunnel and publishes the info to the topic
4918          * @param {String} invokeId
4919          *      The request id
4920          * @private
4921          */
4922         _getAllSubscriptions = function(invokeId) {
4923             // _logger.log("MasterPublisher._getAllSubscriptions() - Getting all subscriptions ");
4924 
4925             _tunnel.getSubscriptions (function (content) {
4926                 _hub.publish(_topics.RESPONSES + "." + invokeId, content);
4927             });
4928             
4929         },
4930 
4931         
4932         /**
4933          * Utility method to bookkeep node subscription requests and determine
4934          * whehter it is necessary to tunnel the request to JabberWerx.
4935          * @param {String} node
4936          *     The node of interest
4937          * @param {String} reqId
4938          *     A unique string identifying the request/subscription
4939          * @private
4940          */
4941         _subscribeNode = function (node, subid) {
4942             if (_connInfoCache.status !== "connected") {
4943                 _hub.publish(_topics.RESPONSES + "." + subid, {
4944                     error: {
4945                         errorType: "Not connected",
4946                         errorMessage: "Cannot subscribe without connection."
4947                     }
4948                 });
4949                 return;
4950             }
4951             // NODE DOES NOT YET EXIST
4952             if (!_nodesList[node]) {
4953                 _nodesList[node] = {
4954                     "subscribing": {
4955                         "reqIds": {},
4956                         "length": 0
4957                     },
4958                     "active": {
4959                         "reqIds": {},
4960                         "length": 0
4961                     },
4962                     "unsubscribing": {
4963                         "reqIds": {},
4964                         "length": 0
4965                     },
4966                     "holding": {
4967                         "reqIds": {},
4968                         "length": 0
4969                     }
4970                 };
4971             }
4972             if (_nodesList[node].active.length === 0) {
4973                 if (_nodesList[node].unsubscribing.length === 0) {
4974                     if (_nodesList[node].subscribing.length === 0) {
4975                         _nodesList[node].subscribing.reqIds[subid] = true;
4976                         _nodesList[node].subscribing.length += 1;
4977 
4978                         _logger.log("MasterPublisher._subscribeNode() - Attempting to subscribe to node '" + node + "'");
4979                         _tunnel.subscribe(node, function (err) {
4980                             var errObj, curSubid;
4981                             if (err) {
4982                                 errObj = {
4983                                     subscribe: {
4984                                         content: err
4985                                     }
4986                                 };
4987 
4988                                 try {
4989                                     errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
4990                                 } catch (e) {
4991                                     errObj.error = {
4992                                         errorType: "parseError",
4993                                         errorMessage: "Could not serialize XML: " + e
4994                                     };
4995                                 }
4996                                 _logger.log("MasterPublisher._subscribeNode() - Error subscribing to node '" + node + "': " + err);
4997                             } else {
4998                                 _logger.log("MasterPublisher._subscribeNode() - Subscribed to node '" + node + "'");
4999                             }
5000 
5001                             for (curSubid in _nodesList[node].subscribing.reqIds) {
5002                                 if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
5003                                     _hub.publish(_topics.RESPONSES + "." + curSubid, errObj);
5004                                     if (!err) {
5005                                         _nodesList[node].active.reqIds[curSubid] = true;
5006                                         _nodesList[node].active.length += 1;
5007                                     }
5008                                     delete _nodesList[node].subscribing.reqIds[curSubid];
5009                                     _nodesList[node].subscribing.length -= 1;
5010                                 }
5011                             }
5012                         });
5013                         
5014                     } else { //other ids are subscribing
5015                         _nodesList[node].subscribing.reqIds[subid] = true;
5016                         _nodesList[node].subscribing.length += 1;
5017                     }                       
5018                 } else { //An unsubscribe request is pending, hold onto these subscribes until it is done
5019                     _nodesList[node].holding.reqIds[subid] = true;
5020                     _nodesList[node].holding.length += 1;
5021                 }
5022             } else { // The node has active subscriptions; add this subid and return successful response
5023                 _nodesList[node].active.reqIds[subid] = true;
5024                 _nodesList[node].active.length += 1;
5025                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
5026             }
5027         },
5028 
5029         /**
5030          * Utility method to bookkeep node unsubscribe requests and determine
5031          * whehter it is necessary to tunnel the request to JabberWerx.
5032          * @param {String} node
5033          *     The node to unsubscribe from
5034          * @param {String} reqId
5035          *     A unique string identifying the subscription to remove
5036          * @param {Boolean} isForceOp
5037          *     Boolean flag if true then the subscription will be forefully removed even when active references to the node are nil.
5038          * @private
5039          */
5040         _unsubscribeNode = function (node, subid, isForceOp) {
5041             if (!_nodesList[node] && !isForceOp ) { //node DNE, publish success response
5042                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
5043             } else {
5044                 if (_connInfoCache.status !== "connected") {
5045                     _hub.publish(_topics.RESPONSES + "." + subid, {
5046                         error: {
5047                             errorType: "Not connected",
5048                             errorMessage: "Cannot unsubscribe without connection."
5049                         }
5050                     });
5051                     return;
5052                 }
5053                 if (_nodesList[node] && _nodesList[node].active.length > 1) {
5054                     delete _nodesList[node].active.reqIds[subid];
5055                     _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
5056                     _nodesList[node].active.length -= 1;
5057                 } else if (_nodesList[node] && _nodesList[node].active.length === 1 || isForceOp) { // transition subid from active category to unsubscribing category
5058 
5059                     if (_nodesList[node]) {
5060                         _nodesList[node].unsubscribing.reqIds[subid] = true;
5061                         _nodesList[node].unsubscribing.length += 1;
5062                         delete _nodesList[node].active.reqIds[subid];
5063                         _nodesList[node].active.length -= 1;
5064                     }
5065 
5066                     _logger.log("MasterPublisher._unsubscribeNode() - Attempting to unsubscribe from node '" + node + "'");
5067                     var requestId = subid;
5068                     _tunnel.unsubscribe(node, function (err) {
5069                         var errObj, curSubid;
5070                         if (err) {
5071                             errObj = {
5072                                 subscribe: {
5073                                     content: err
5074                                 }
5075                             };
5076 
5077                             try {
5078                                 errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
5079                             } catch (e) {
5080                                 errObj.error = {
5081                                     errorType: "parseError",
5082                                     errorMessage: "Could not serialize XML: " + e
5083                                 };
5084                             }
5085                             _logger.log("MasterPublisher._unsubscribeNode() - Error unsubscribing from node '" + node + "': " + err);
5086                         } else {
5087                             _logger.log("MasterPublisher._unsubscribeNode() - Unsubscribed from node '" + node + "'");
5088                         }
5089 
5090                         if (_nodesList[node]) {
5091 
5092                             for (curSubid in _nodesList[node].unsubscribing.reqIds) {
5093                                 if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
5094                                     // publish to all subids whether unsubscribe failed or succeeded
5095                                     _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
5096                                     if (!err) {
5097                                         delete _nodesList[node].unsubscribing.reqIds[curSubid];
5098                                         _nodesList[node].unsubscribing.length -= 1;
5099                                     } else { // Just remove the subid from unsubscribing; the next subscribe request will operate with node already created
5100                                         delete _nodesList[node].unsubscribing.reqIds[curSubid];
5101                                         _nodesList[node].unsubscribing.length -= 1;
5102                                     }   
5103                                 }
5104                             }
5105                             
5106                             if (!err && _nodesList[node].holding.length > 0) { // if any subscribe requests came in while unsubscribing from OF, now transition from holding to subscribing
5107                                 for (curSubid in _nodesList[node].holding.reqIds) {
5108                                     if (_nodesList[node].holding.reqIds.hasOwnProperty(curSubid)) {
5109                                         delete _nodesList[node].holding.reqIds[curSubid];
5110                                         _nodesList[node].holding.length -= 1;
5111                                         _subscribeNode(node, curSubid);                             
5112                                     }
5113                                 }
5114                             }
5115                         } else {
5116                             _hub.publish(_topics.RESPONSES + "." + requestId, undefined);
5117                         }
5118                     });
5119                 } else { // length <= 0?
5120                     _hub.publish(_topics.RESPONSES + "." + subid, undefined);
5121                 }
5122             }
5123         },
5124 
5125         
5126         
5127         /**
5128          * Handles client requests to establish a BOSH connection.
5129          * @param {String} id
5130          *     id of the xmpp user
5131          * @param {String} password
5132          *     password of the xmpp user
5133          * @param {String} xmppDomain
5134          *     xmppDomain of the xmpp user account
5135          * @private
5136          */
5137         _connect = function (id, password, xmppDomain) {
5138             _tunnel.makeConnectReq(id, password, xmppDomain);
5139         },
5140 
5141         /**
5142          * Handles client requests made to the request topic. The type of the
5143          * request is described in the "type" property within the data payload. Each
5144          * type can result in a different operation.
5145          * @param {String} topic
5146          *     The topic which data was published to.
5147          * @param {Object} data
5148          *     The data containing requests information published by clients.
5149          * @param {String} data.type
5150          *     The type of the request. Supported: "ConnectionInfoReq"
5151          * @param {Object} data.data
5152          *     May contain data relevant for the particular requests.
5153          * @param {String} [data.invokeID]
5154          *     The ID used to identify the request with the response. The invoke ID
5155          *     will be included in the data in the publish to the topic. It is the
5156          *     responsibility of the client to correlate the published data to the
5157          *     request made by using the invoke ID.
5158          * @private
5159          */
5160         _clientRequestHandler = function (topic, data) {
5161             var dataCopy;
5162 
5163             //Ensure a valid data object with "type" and "data" properties.
5164             if (typeof data === "object" &&
5165                     typeof data.type === "string" &&
5166                     typeof data.data === "object") {
5167                 switch (data.type) {
5168                 case _REQTYPES.CONNECTIONINFO:
5169                     //It is possible that Slave clients come up before the Master
5170                     //client. If that is the case, the Slaves will need to make a
5171                     //request for the Master to send the latest connection info to the
5172                     //connectionInfo topic.
5173                     dataCopy = _cloneConnInfoObj();
5174                     if (dataCopy) {
5175                         if (data.invokeID !== undefined) {
5176                             dataCopy.invokeID = data.invokeID;
5177                         }
5178                         _hub.publish(_topics.EVENTS_CONNECTION_INFO, dataCopy);
5179                     }
5180                     break;
5181                 case _REQTYPES.SUBSCRIBE:
5182                     if (typeof data.data.node === "string") {
5183                         _subscribeNode(data.data.node, data.invokeID);
5184                     }
5185                     break;
5186                 case _REQTYPES.UNSUBSCRIBE:
5187                     if (typeof data.data.node === "string") {
5188                         _unsubscribeNode(data.data.node, data.invokeID, data.isForceOp );
5189                     }
5190                     break;
5191                 case _REQTYPES.SUBSCRIPTIONSINFO:
5192                     _getAllSubscriptions(data.invokeID);
5193                     break;
5194                 case _REQTYPES.CONNECT:
5195                     // Deprecated: Disallow others (non-masters) from administering BOSH connections that are not theirs
5196                     _logger.log("MasterPublisher._clientRequestHandler(): F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
5197                     break;
5198                 default:
5199                     break;
5200                 }
5201             }
5202         };
5203 
5204         (function () {
5205             //Register to receive events and connection status from tunnel.
5206             _tunnel.registerEventHandler(_eventHandler);
5207             _tunnel.registerPresenceHandler(_presenceHandler);
5208             _tunnel.registerConnectionInfoHandler(_connInfoHandler);
5209 
5210             //Listen to a request channel to respond to any requests made by other
5211             //clients because the Master may have access to useful information.
5212             _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
5213         }());
5214 
5215         /**
5216          * @private
5217          * Handles client requests to establish a BOSH connection.
5218          * @param {String} id
5219          *     id of the xmpp user
5220          * @param {String} password
5221          *     password of the xmpp user
5222          * @param {String} xmppDomain
5223          *     xmppDomain of the xmpp user account
5224          */
5225         this.connect = function (id, password, xmppDomain) {
5226           _connect(id, password, xmppDomain);
5227         };
5228 
5229         /**
5230          * @private
5231          * Resets the list of explicit subscriptions
5232          */
5233         this.wipeout = function () {
5234             _cleanupPendingRequests();
5235             _nodesList = {};
5236        };
5237 
5238         //BEGIN TEST CODE//
5239        /**
5240         * Test code added to expose private functions that are used by unit test
5241         * framework. This section of code is removed during the build process
5242         * before packaging production code. The [begin|end]TestSection are used
5243         * by the build to identify the section to strip.
5244         * @ignore
5245         */
5246        this.beginTestSection = 0;
5247 
5248        /**
5249         * @ignore
5250         */
5251        this.getTestObject = function () {
5252            //Load mock dependencies.
5253            var _mock = new MockControl();
5254            _hub = _mock.createMock(gadgets.Hub);
5255            _tunnel = _mock.createMock();
5256 
5257            return {
5258                //Expose mock dependencies
5259                mock: _mock,
5260                hub: _hub,
5261                tunnel: _tunnel,
5262                setTunnel: function (tunnel) {
5263                    _tunnel = tunnel;
5264                },
5265                getTunnel: function () {
5266                    return _tunnel;
5267                },
5268 
5269                //Expose internal private functions
5270                reqtypes: _REQTYPES,
5271                eventHandler: _eventHandler,
5272                presenceHandler: _presenceHandler,
5273                
5274                subscribeNode: _subscribeNode,
5275                unsubscribeNode: _unsubscribeNode,
5276                
5277                getNodeList: function () {
5278                    return _nodesList;
5279                },
5280                setNodeList: function (nodelist) {
5281                    _nodesList = nodelist;
5282                },
5283                
5284                cloneConnInfoObj: _cloneConnInfoObj,
5285                connInfoHandler: _connInfoHandler,
5286                clientRequestHandler: _clientRequestHandler
5287 
5288            };
5289        };
5290 
5291 
5292        /**
5293         * @ignore
5294         */
5295        this.endTestSection = 0;
5296        //END TEST CODE//
5297 
5298     };
5299     
5300     window.finesse = window.finesse || {};
5301     window.finesse.clientservices = window.finesse.clientservices || {};
5302     window.finesse.clientservices.MasterPublisher = MasterPublisher;
5303 
5304     return MasterPublisher;
5305 });
5306 
5307 /** The following comment is to prevent jslint errors about
5308  * using variables before they are defined.
5309  */
5310 /*global publisher:true */
5311 
5312 /**
5313  * Exposes a set of API wrappers that will hide the dirty work of
5314  *     constructing Finesse API requests and consuming Finesse events.
5315  *
5316  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
5317  */
5318 
5319 
5320 /**
5321  * Allow clients to make Finesse API requests and consume Finesse events by
5322  * calling a set of exposed functions. The Services layer will do the dirty
5323  * work of establishing a shared BOSH connection (for designated Master
5324  * modules), consuming events for client subscriptions, and constructing API
5325  * requests.
5326  */
5327 /** @private */
5328 define('clientservices/ClientServices',[
5329     "clientservices/MasterTunnel",
5330     "clientservices/MasterPublisher",
5331     "clientservices/Topics",
5332     "utilities/Utilities"
5333 ],
5334 function (MasterTunnel, MasterPublisher, Topics, Utilities) {
5335 
5336      var ClientServices = (function () {/** @lends finesse.clientservices.ClientServices.prototype */
5337         var
5338 
5339         /**
5340          * Shortcut reference to the master tunnel
5341          * @private
5342          */
5343         _tunnel,
5344 
5345         _publisher,
5346 
5347         /**
5348          * Shortcut reference to the finesse.utilities.Utilities singleton
5349          * This will be set by init()
5350          * @private
5351          */
5352         _util,
5353 
5354         /**
5355          * Shortcut reference to the gadgets.io object.
5356          * This will be set by init()
5357          * @private
5358          */
5359         _io,
5360 
5361         /**
5362          * Shortcut reference to the gadget pubsub Hub instance.
5363          * This will be set by init()
5364          * @private
5365          */
5366         _hub,
5367 
5368         /**
5369          * Logger object set externally by setLogger, defaults to nothing.
5370          * @private
5371          */
5372         _logger = {},
5373 
5374         /**
5375          * Shortcut reference to the Topics class.
5376          * This will be set by init()
5377          * @private
5378          */
5379         _topics,
5380 
5381         /**
5382          * Config object needed to initialize this library
5383          * This must be set by init()
5384          * @private
5385          */
5386         _config,
5387 
5388         /**
5389          * @private
5390          * Whether or not this ClientService instance is a Master.
5391          */
5392         _isMaster = false,
5393 
5394         /**
5395          * @private
5396          * Whether the Client Services have been initiated yet.
5397          */
5398         _inited = false,
5399 
5400         /**
5401          * Stores the list of subscription IDs for all subscriptions so that it
5402          * could be retrieve for unsubscriptions.
5403          * @private
5404          */
5405         _subscriptionID = {},
5406         
5407         _restResponseCache = {},
5408 
5409         /**
5410          * The possible states of the JabberWerx BOSH connection.
5411          * @private
5412          */
5413         _STATUS = {
5414             CONNECTING: "connecting",
5415             CONNECTED: "connected",
5416             DISCONNECTED: "disconnected",
5417             DISCONNECTED_CONFLICT: "conflict",
5418             DISCONNECTED_UNAUTHORIZED: "unauthorized",
5419             DISCONNECTING: "disconnecting",
5420             RECONNECTING: "reconnecting",
5421             UNLOADING: "unloading",
5422             FAILING: "failing",
5423             RECOVERED: "recovered"
5424         },
5425 
5426         /**
5427          * Local reference for authMode enum object.
5428          * @private
5429          */
5430         _authModes,
5431 
5432         _failoverMode = false,
5433 
5434         /**
5435          * Handler function to be invoked when BOSH connection is connecting.
5436          * @private
5437          */
5438         _onConnectingHandler,
5439 
5440         /**
5441          * Handler function to be invoked when BOSH connection is connected
5442          * @private
5443          */
5444         _onConnectHandler,
5445 
5446         /**
5447          * Handler function to be invoked when BOSH connection is disconnecting.
5448          * @private
5449          */
5450         _onDisconnectingHandler,
5451 
5452         /**
5453          * Handler function to be invoked when the BOSH is disconnected.
5454          * @private
5455          */
5456         _onDisconnectHandler,
5457 
5458         /**
5459          * Handler function to be invoked when the BOSH is reconnecting.
5460          * @private
5461          */
5462         _onReconnectingHandler,
5463 
5464         /**
5465          * Handler function to be invoked when the BOSH is unloading.
5466          * @private
5467          */
5468         _onUnloadingHandler,
5469 
5470         /**
5471          * Contains a cache of the latest connection info containing the current
5472          * state of the BOSH connection and the resource ID.
5473          * @private
5474          */
5475         _connInfo,
5476 
5477         /**
5478          * Keeps track of all the objects that need to be refreshed when we recover
5479          * due to our resilient connection. Only objects that we subscribe to will
5480          * be added to this list.
5481          * @private
5482          */
5483         _refreshList = [],
5484 
5485         /**
5486          * Needs to be passed as authorization header inside makeRequest wrapper function
5487          */
5488         _authHeaderString,
5489 
5490         /**
5491          * @private
5492          * Centralized logger.log method for external logger
5493          * @param {String} msg
5494          * @param {Object} [Optional] Javascript error object 
5495          */
5496         _log = function (msg, e) {
5497             // If the external logger throws up, it stops here.
5498             try {
5499                 if (_logger.log) {
5500                     _logger.log("[ClientServices] " + msg, e);
5501                 }
5502             } catch (e) { }
5503         },
5504 
5505         /**
5506          * Go through each object in the _refreshList and call its refresh() function
5507          * @private
5508          */
5509         _refreshObjects = function () {
5510             var i;
5511 
5512             // wipe out the explicit subscription list before we refresh objects
5513             if (_publisher) {
5514                 _publisher.wipeout();
5515             }
5516 
5517             // refresh each item in the refresh list
5518             for (i = _refreshList.length - 1; i >= 0; i -= 1) {
5519                 try{
5520                     _log("Refreshing " + _refreshList[i].getRestUrl());
5521                     _refreshList[i].refresh(10);
5522                 }
5523                 catch(e){
5524                     _log("ClientServices._refreshObjects() unexpected error while refreshing object" + e);
5525                 }
5526             }
5527         },
5528 
5529         /**
5530          * Handler to process connection info publishes.
5531          * @param {Object} data
5532          *     The connection info data object.
5533          * @param {String} data.status
5534          *     The BOSH connection status.
5535          * @param {String} data.resourceID
5536          *     The resource ID for the connection.
5537          * @private
5538          */
5539         _connInfoHandler =  function (data) {
5540 
5541             //Invoke registered handler depending on status received. Due to the
5542             //request topic where clients can make request for the Master to publish
5543             //the connection info, there is a chance that duplicate connection info
5544             //events may be sent, so ensure that there has been a state change
5545             //before invoking the handlers.
5546             if (_connInfo === undefined || _connInfo.status !== data.status) {
5547                 _connInfo = data;
5548                 switch (data.status) {
5549                 case _STATUS.CONNECTING:
5550                     if (_isMaster && _onConnectingHandler) {
5551                         _onConnectingHandler();
5552                     }
5553                     break;
5554                 case _STATUS.CONNECTED:
5555                     if ((_isMaster || !_failoverMode) && _onConnectHandler) {
5556                         _onConnectHandler();
5557                     }
5558                     break;
5559                 case _STATUS.DISCONNECTED:
5560                     if (_isMaster && _onDisconnectHandler) {
5561                         _onDisconnectHandler();
5562                     }
5563                     break;
5564                 case _STATUS.DISCONNECTED_CONFLICT:
5565                     if (_isMaster && _onDisconnectHandler) {
5566                         _onDisconnectHandler("conflict");
5567                     }
5568                     break;
5569                 case _STATUS.DISCONNECTED_UNAUTHORIZED:
5570                     if (_isMaster && _onDisconnectHandler) {
5571                         _onDisconnectHandler("unauthorized");
5572                     }
5573                     break;
5574                 case _STATUS.DISCONNECTING:
5575                     if (_isMaster && _onDisconnectingHandler) {
5576                         _onDisconnectingHandler();
5577                     }
5578                     break;
5579                 case _STATUS.RECONNECTING:
5580                     if (_isMaster && _onReconnectingHandler) {
5581                         _onReconnectingHandler();
5582                     }
5583                     break;
5584                 case _STATUS.UNLOADING:
5585                     if (_isMaster && _onUnloadingHandler) {
5586                         _onUnloadingHandler();
5587                     }
5588                     break;
5589                 case _STATUS.FAILING:
5590                     if (!_isMaster) {
5591                         // Stop
5592                         _failoverMode = true;
5593                         if (_onDisconnectHandler) {
5594                             _onDisconnectHandler();
5595                         }
5596                     }
5597                     break;
5598                 case _STATUS.RECOVERED:
5599                     if (!_isMaster) {
5600                         _failoverMode = false;
5601                         if (_onConnectHandler) {
5602                             _onConnectHandler();
5603                         }
5604                     }
5605                     // Whenever we are recovered, we need to refresh any objects
5606                     // that are stored.
5607                     _refreshObjects();
5608                     break;
5609                 }
5610             }
5611         },
5612 
5613         /**
5614          * Ensure that ClientServices have been inited.
5615          * @private
5616          */
5617         _isInited = function () {
5618             if (!_inited) {
5619                 throw new Error("ClientServices needs to be inited.");
5620             }
5621         },
5622 
5623         /**
5624          * Have the client become the Master by initiating a tunnel to a shared
5625          * event BOSH connection. The Master is responsible for publishing all
5626          * events to the pubsub infrastructure.
5627          *
5628          * TODO: Currently we only check the global auth mode. This code has to
5629          * handle mixed mode - in this case the user specfic SSO mode has to be
5630          * exposed via an API.
5631          *
5632          * @private
5633          */
5634         _becomeMaster = function () {
5635             var creds , id;
5636             _tunnel = new MasterTunnel(_config.host, _config.scheme);
5637             _publisher = new MasterPublisher(_tunnel, _hub);
5638             if(_authModes.SSO === _config.systemAuthMode ) {
5639                 creds = _config.authToken;
5640             } else {
5641                 creds = _config.password;
5642             }
5643             _util = Utilities;
5644              id = _util.encodeNodeName(_config.id);
5645             _tunnel.init(id, creds, _config.xmppDomain, _config.pubsubDomain, _config.resource, _config.notificationConnectionType);
5646             _isMaster = true;
5647         },
5648 
5649         /**
5650          * Make a request to the request channel to have the Master publish the
5651          * connection info object.
5652          * @private
5653          */
5654         _makeConnectionInfoReq = function () {
5655             var data = {
5656                 type: "ConnectionInfoReq",
5657                 data: {},
5658                 invokeID: (new Date()).getTime()
5659             };
5660             _hub.publish(_topics.REQUESTS, data);
5661         },
5662 
5663         /**
5664          * Utility method to register a handler which is associated with a
5665          * particular connection status.
5666          * @param {String} status
5667          *     The connection status string.
5668          * @param {Function} handler
5669          *     The handler to associate with a particular connection status.
5670          * @throws {Error}
5671          *     If the handler provided is not a function.
5672          * @private
5673          */
5674         _registerHandler = function (status, handler) {
5675             if (typeof handler === "function") {
5676                 if (_connInfo && _connInfo.status === status) {
5677                     handler();
5678                 }
5679                 switch (status) {
5680                 case _STATUS.CONNECTING:
5681                     _onConnectingHandler = handler;
5682                     break;
5683                 case _STATUS.CONNECTED:
5684                     _onConnectHandler = handler;
5685                     break;
5686                 case _STATUS.DISCONNECTED:
5687                     _onDisconnectHandler = handler;
5688                     break;
5689                 case _STATUS.DISCONNECTING:
5690                     _onDisconnectingHandler = handler;
5691                     break;
5692                 case _STATUS.RECONNECTING:
5693                     _onReconnectingHandler = handler;
5694                     break;
5695                 case _STATUS.UNLOADING:
5696                     _onUnloadingHandler = handler;
5697                     break;
5698                 }
5699 
5700             } else {
5701                 throw new Error("Callback is not a function");
5702             }
5703         },
5704 
5705         /**
5706          * Callback function that is called when a refresh access token event message is posted to the Hub.
5707          *
5708          * Get access token from the data and update the finesse.gadget.Config object!
5709          *
5710          * @param {String} topic
5711          *      which topic the event came on (unused)
5712          * @param {Object} data
5713          *      the data published with the event
5714          * @private
5715          */
5716         _accessTokenRefreshHandler = function(topic , data){
5717             _log("Access token refreshed - topic :" + topic + ", authToken :" + data.authToken);
5718 
5719             if(data.authToken){
5720                _config.authToken = data.authToken;
5721                if(finesse.gadget && finesse.gadget.Config){
5722                    finesse.gadget.Config.authToken = data.authToken;
5723                }
5724             }
5725          },
5726 
5727         /**
5728          * @private
5729          * Retrieves systemAuthMode from parent Finesse Container. If parent is not available, mode will be retrieved from the systemInfo rest object
5730          * @throws {Error}
5731          *     If unable to retrieve systemAuthMode
5732          */
5733         _getSystemAuthMode = function(){
5734           var parentFinesse , sysInfo;
5735           // For gadgets hosted outside of finesse container , finesse parent object will not be available
5736             try{
5737                 parentFinesse = window.parent.finesse;
5738             } catch (e){
5739                 parentFinesse = undefined;
5740             }
5741 
5742             if( parentFinesse ){
5743                _config.systemAuthMode =  parentFinesse.container.Config.systemAuthMode;
5744             } else {
5745                sysInfo = new finesse.restservices.SystemInfo({
5746                    id: _config.id,
5747                    onLoad: function (systemInfo) {
5748                         _config.systemAuthMode = systemInfo.getSystemAuthMode();
5749                    },
5750                    onError: function (errRsp) {
5751                         throw new Error("Unable to retrieve systemAuthMode from config. Initialization failed......");
5752                    }
5753               });
5754 
5755             }
5756        }
5757        
5758         return {
5759 
5760             /**
5761              * @private
5762              * Adds an item to the list to be refreshed upon reconnect
5763              * @param {RestBase} object - rest object to be refreshed
5764              */
5765             addToRefreshList: function (object) {
5766                 _refreshList.push(object);
5767             },
5768             
5769             /**
5770              * Get the current state of the Bosh/Websocket connection.
5771              * Returns undefined when information not available.
5772              * @return {String} Connection status of Bosh/Websocket connection.
5773              * @example finesse.clientservices.ClientServices.getNotificationConnectionState();
5774              */
5775             getNotificationConnectionState: function () {
5776                 return _connInfo && _connInfo.status;
5777             },
5778 
5779             /**
5780              * @private
5781              * Removes the given item from the refresh list
5782              * @param  {RestBase} object - rest object to be removed
5783              */
5784             removeFromRefreshList: function (object) {
5785                 var i;
5786                 for (i = _refreshList.length - 1; i >= 0; i -= 1) {
5787                     if (_refreshList[i] === object) {
5788                         _refreshList.splice(i, 1);
5789                         break;
5790                     }
5791                 }
5792             },
5793 
5794             /**
5795              * @private
5796              * The location of the tunnel HTML URL.
5797              * @returns {String}
5798              *     The location of the tunnel HTML URL.
5799              */
5800             getTunnelURL: function () {
5801                 return _tunnel.getTunnelURL();
5802             },
5803 
5804             /**
5805              * @private
5806              * Indicates whether the tunnel frame is loaded.
5807              * @returns {Boolean}
5808              *     True if the tunnel frame is loaded, false otherwise.
5809              */
5810             isTunnelLoaded: function () {
5811                 return _tunnel.isTunnelLoaded();
5812             },
5813 
5814             /**
5815              * @private
5816              * Indicates whether the ClientServices instance is a Master.
5817              * @returns {Boolean}
5818              *     True if this instance of ClientServices is a Master, false otherwise.
5819              */
5820             isMaster: function () {
5821                 return _isMaster;
5822             },
5823 
5824             /**
5825              * @private
5826              * Get the resource ID. An ID is only available if the BOSH connection has
5827              * been able to connect successfully.
5828              * @returns {String}
5829              *     The resource ID string. Null if the BOSH connection was never
5830              *     successfully created and/or the resource ID has not been associated.
5831              */
5832             getResourceID: function () {
5833                 if (_connInfo !== undefined) {
5834                     return _connInfo.resourceID;
5835                 }
5836                 return null;
5837             },
5838 
5839             /*
5840             getHub: function () {
5841                 return _hub;
5842             },
5843         */
5844             /**
5845              * @private
5846              * Add a callback to be invoked when the BOSH connection is attempting
5847              * to connect. If the connection is already trying to connect, the
5848              * callback will be invoked immediately.
5849              * @param {Function} handler
5850              *      An empty param function to be invoked on connecting. Only one
5851              *      handler can be registered at a time. Handlers already registered
5852              *      will be overwritten.
5853              */
5854             registerOnConnectingHandler: function (handler) {
5855                 _registerHandler(_STATUS.CONNECTING, handler);
5856             },
5857 
5858             /**
5859              * @private
5860              * Removes the on connecting callback that was registered.
5861              */
5862             unregisterOnConnectingHandler: function () {
5863                 _onConnectingHandler = undefined;
5864             },
5865 
5866             /**
5867              * Add a callback to be invoked when all of the following conditions are met:
5868              * <ul>
5869              *   <li>When Finesse goes IN_SERVICE</li>
5870              *   <li>The BOSH connection is established</li>
5871              *   <li>The Finesse user presence becomes available</li>
5872              * </ul>
5873              * If all these conditions are met at the time this function is called, then
5874              * the handler will be invoked immediately.
5875              * @param {Function} handler
5876              *      An empty param function to be invoked on connect. Only one handler
5877              *      can be registered at a time. Handlers already registered will be
5878              *      overwritten.
5879              * @example
5880              *      finesse.clientservices.ClientServices.registerOnConnectHandler(gadget.myCallback);
5881              */
5882             registerOnConnectHandler: function (handler) {
5883                 _registerHandler(_STATUS.CONNECTED, handler);
5884             },
5885 
5886             /**
5887              * @private
5888              * Removes the on connect callback that was registered.
5889              */
5890             unregisterOnConnectHandler: function () {
5891                 _onConnectHandler = undefined;
5892             },
5893 
5894             /**
5895              * Add a callback to be invoked when any of the following occurs:
5896              * <ul>
5897              *   <li>Finesse is no longer IN_SERVICE</li>
5898              *   <li>The BOSH connection is lost</li>
5899              *   <li>The presence of the Finesse user is no longer available</li>
5900              * </ul>
5901              * If any of these conditions are met at the time this function is
5902              * called, the callback will be invoked immediately.
5903              * @param {Function} handler
5904              *      An empty param function to be invoked on disconnected. Only one
5905              *      handler can be registered at a time. Handlers already registered
5906              *      will be overwritten.
5907              * @example
5908              *      finesse.clientservices.ClientServices.registerOnDisconnectHandler(gadget.myCallback);
5909              */
5910             registerOnDisconnectHandler: function (handler) {
5911                 _registerHandler(_STATUS.DISCONNECTED, handler);
5912             },
5913 
5914             /**
5915              * @private
5916              * Removes the on disconnect callback that was registered.
5917              */
5918             unregisterOnDisconnectHandler: function () {
5919                 _onDisconnectHandler = undefined;
5920             },
5921 
5922             /**
5923              * @private
5924              * Add a callback to be invoked when the BOSH is currently disconnecting. If
5925              * the connection is already disconnecting, invoke the callback immediately.
5926              * @param {Function} handler
5927              *      An empty param function to be invoked on disconnected. Only one
5928              *      handler can be registered at a time. Handlers already registered
5929              *      will be overwritten.
5930              */
5931             registerOnDisconnectingHandler: function (handler) {
5932                 _registerHandler(_STATUS.DISCONNECTING, handler);
5933             },
5934 
5935             /**
5936              * @private
5937              * Removes the on disconnecting callback that was registered.
5938              */
5939             unregisterOnDisconnectingHandler: function () {
5940                 _onDisconnectingHandler = undefined;
5941             },
5942 
5943             /**
5944              * @private
5945              * Add a callback to be invoked when the BOSH connection is attempting
5946              * to connect. If the connection is already trying to connect, the
5947              * callback will be invoked immediately.
5948              * @param {Function} handler
5949              *      An empty param function to be invoked on connecting. Only one
5950              *      handler can be registered at a time. Handlers already registered
5951              *      will be overwritten.
5952              */
5953             registerOnReconnectingHandler: function (handler) {
5954                 _registerHandler(_STATUS.RECONNECTING, handler);
5955             },
5956 
5957             /**
5958              * @private
5959              * Removes the on reconnecting callback that was registered.
5960              */
5961             unregisterOnReconnectingHandler: function () {
5962                 _onReconnectingHandler = undefined;
5963             },
5964 
5965             /**
5966              * @private
5967              * Add a callback to be invoked when the BOSH connection is unloading
5968              *
5969              * @param {Function} handler
5970              *      An empty param function to be invoked on connecting. Only one
5971              *      handler can be registered at a time. Handlers already registered
5972              *      will be overwritten.
5973              */
5974             registerOnUnloadingHandler: function (handler) {
5975                 _registerHandler(_STATUS.UNLOADING, handler);
5976             },
5977 
5978             /**
5979              * @private
5980              * Removes the on unloading callback that was registered.
5981              */
5982             unregisterOnUnloadingHandler: function () {
5983                 _onUnloadingHandler = undefined;
5984             },
5985 
5986             /**
5987              * @private
5988              * Proxy method for gadgets.io.makeRequest. The will be identical to gadgets.io.makeRequest
5989              * ClientServices will mixin the BASIC Auth string, locale, and host, since the
5990              * configuration is encapsulated in here anyways.
5991              * This removes the dependency
5992              * @param {String} url
5993              *     The url(relative/absolute) to make the request to.
5994              *     It is expected that any encoding to the URL is already done.
5995              * @param {Function} handler
5996              *     Callback handler for makeRequest to invoke when the response returns.
5997              *     Completely passed through to gadgets.io.makeRequest
5998              * @param {Object} params
5999              *     The params object that gadgets.io.makeRequest expects. Authorization and locale
6000              *     headers are mixed in.
6001              */
6002             makeRequest: function (url, handler, params) {
6003                 // ClientServices needs to be initialized with a config for restHost, auth, and locale
6004                 _isInited();
6005 
6006                 // Allow mixin of auth and locale headers
6007                 params = params || {};
6008 
6009                 // Override refresh interval to 0 instead of default 3600 as a way to workaround makeRequest
6010                 // using GET http method because then the params are added to the url as query params, which
6011                 // exposes the authorization string in the url. This is a placeholder until oauth comes in
6012                 params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = params[gadgets.io.RequestParameters.REFRESH_INTERVAL] || 0;
6013 
6014                 params[gadgets.io.RequestParameters.HEADERS] = params[gadgets.io.RequestParameters.HEADERS] || {};
6015 
6016                 // Add Basic auth to request header
6017                 params[gadgets.io.RequestParameters.HEADERS].Authorization = _util.getAuthHeaderString(_config);
6018 
6019                 //Locale
6020                 params[gadgets.io.RequestParameters.HEADERS].locale = _config.locale;
6021 
6022                 if( _util.isAbsoluteUrl(url) ) {
6023                     _log("makeRequest to absolute url: " + url);
6024                     gadgets.io.makeRequest(url, handler, params);
6025                 } else {
6026                     _log("makeRequest to relative url: " + url);
6027                     var restHost = "http://localhost:8082";
6028                     var config = finesse.container.Config;
6029                     var windowLocation = window.location.hostname;
6030 
6031                     /**
6032                      * if makeRequest is called from cfadmin gadget in SPOG, resthost shoule be actual restRest from the Config,
6033                      * if we use localhost in SPOG it will be resilved to PCCE
6034                     */
6035                     if ( finesse.vars && finesse.vars.isCfAdminGadget && config.restHost && config.restHost.toLocaleLowerCase() !== windowLocation ) {
6036                         _log("makeRequest to relative url. hostname(" + windowLocation + ") and restHost(" + config.restHost + ") are different");
6037                         restHost = config.scheme + "://" + config.restHost + ":" + config.hostPort;
6038                     }
6039                     gadgets.io.makeRequest( restHost + (url.startsWith("/") ? url : "/" + url), handler, params);
6040                 }
6041             },
6042 
6043             /**
6044              * @private
6045              * Utility function to make a subscription to a particular topic. Only one
6046              * callback function is registered to a particular topic at any time.
6047              * @param {String} topic
6048              *     The full topic name. The topic name should follow the OpenAjax
6049              *     convention using dot notation (ex: finesse.api.User.1000).
6050              * @param {Function} callback
6051              *     The function that should be invoked with the data when an event
6052              *     is delivered to the specific topic.
6053              * @param {Function} contextId
6054              *     A unique id which gets appended to the topic for storing subscription details,
6055              *     when multiple subscriptions to the same topic is required.
6056              * @returns {Boolean}
6057              *     True if the subscription was made successfully and the callback was
6058              *     been registered. False if the subscription already exist, the
6059              *     callback was not overwritten.
6060              */
6061             subscribe: function (topic, callback, disableDuringFailover, contextId) {
6062                 _isInited();
6063                 
6064                 var topicId = topic + (contextId === undefined ? "" : contextId);
6065 
6066                 //Ensure that the same subscription isn't made twice.
6067                 if (!_subscriptionID[topicId]) {
6068                     //Store the subscription ID using the topic name as the key.
6069                     _subscriptionID[topicId] = _hub.subscribe(topic,
6070                         //Invoke the callback just with the data object.
6071                         function (topic, data) {
6072                             if (!disableDuringFailover || _isMaster || !_failoverMode) {
6073                                 // Halt all intermediate event processing while the master instance attempts to rebuild the connection. This only occurs:
6074                                 // - For RestBase objects (which pass the disableDuringFailover flag), so that intermediary events while recovering are hidden away until everything is all good
6075                                 //    - We shouldn't be halting anything else because we have infrastructure requests that use the hub pub sub
6076                                 // - Master instance will get all events regardless, because it is responsible for managing failover
6077                                 // - If we are not in a failover mode, everything goes
6078                                 // _refreshObjects will reconcile anything that was missed once we are back in action
6079                                 callback(data);
6080                             }
6081                         });
6082                     return true;
6083                 }
6084                 return false;
6085             },
6086 
6087             /**
6088              * @private
6089              * Unsubscribe from a particular topic.
6090              * @param {String} topic
6091              *     The full topic name.
6092              * @param {String} contextId
6093              *     A unique id which gets appended to the topic for storing subscription details,
6094              *     when multiple subscriptions to the same topic is required.
6095              */
6096             unsubscribe: function (topic, contextId) {
6097                 _isInited();
6098                 topic = topic + (contextId === undefined ? "" : contextId);
6099                 
6100                 //Unsubscribe from the topic using the subscription ID recorded when
6101                 //the subscription was made, then delete the ID from data structure.
6102                 if (_subscriptionID[topic]) {
6103                     _hub.unsubscribe(_subscriptionID[topic]);
6104                     delete _subscriptionID[topic];
6105                 }
6106             },
6107 
6108 
6109             /**
6110              * @private
6111              * Make a request to the request channel to have the Master subscribe
6112              * to a node.
6113              * @param {String} node
6114              * The node to subscribe to.
6115              */
6116             subscribeNode: function (node, handler) {
6117                 if (handler && typeof handler !== "function") {
6118                     throw new Error("ClientServices.subscribeNode: handler is not a function");
6119                 }
6120 
6121                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
6122                 var data = {
6123                     type: "SubscribeNodeReq",
6124                     data: {node: node},
6125                     invokeID: _util.generateUUID()
6126                 },
6127                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
6128                 _this = this;
6129 
6130                 // We need to first subscribe to the response channel
6131                 this.subscribe(responseTopic, function (rsp) {
6132                     // Since this channel is only used for this singular request,
6133                     // we are not interested anymore.
6134                     // This is also critical to not leaking memory by having OpenAjax
6135                     // store a bunch of orphaned callback handlers that enclose on
6136                     // our entire ClientServices singleton
6137                     _this.unsubscribe(responseTopic);
6138                     if (handler) {
6139                         handler(data.invokeID, rsp);
6140                     }
6141                 });
6142                 // Then publish the request on the request channel
6143                 _hub.publish(_topics.REQUESTS, data);
6144             },
6145 
6146             /**
6147              * @private
6148              * Make a request to the request channel to have the Master unsubscribe
6149              * from a node.
6150              * @param {String} node
6151              * The node to unsubscribe from.
6152              */
6153             unsubscribeNode: function (node, subid, handler) {
6154                 if (handler && typeof handler !== "function") {
6155                     throw new Error("ClientServices.unsubscribeNode: handler is not a function");
6156                 }
6157 
6158                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
6159                 var data = {
6160                     type: "UnsubscribeNodeReq",
6161                     data: {
6162                         node: node,
6163                         subid: subid
6164                     },
6165                     isForceOp: (typeof handler.isForceOp != 'undefined') ? handler.isForceOp: false,
6166                     invokeID: _util.generateUUID()
6167                 },
6168                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
6169                 _this = this;
6170 
6171                 // We need to first subscribe to the response channel
6172                 this.subscribe(responseTopic, function (rsp) {
6173                     // Since this channel is only used for this singular request,
6174                     // we are not interested anymore.
6175                     // This is also critical to not leaking memory by having OpenAjax
6176                     // store a bunch of orphaned callback handlers that enclose on
6177                     // our entire ClientServices singleton
6178                     _this.unsubscribe(responseTopic);
6179                     if (handler) {
6180                         handler(rsp);
6181                     }
6182                 });
6183                 // Then publish the request on the request channel
6184                 _hub.publish(_topics.REQUESTS, data);
6185             },
6186 
6187 
6188             /**
6189              * @private
6190              * Make a request to the request channel to get the list of all subscriptions for the logged in user.
6191              */
6192             getNodeSubscriptions: function (handler) {
6193                 if (handler && typeof handler !== "function") {
6194                     throw new Error("ClientServices.getNodeSubscriptions: handler is not a function");
6195                 }
6196 
6197                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
6198                 var data = {
6199                     type: "SubscriptionsInfoReq",
6200                     data: {
6201                     },
6202                     invokeID: _util.generateUUID()
6203                 },
6204                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
6205                 _this = this;
6206 
6207                 // We need to first subscribe to the response channel
6208                 this.subscribe(responseTopic, function (rsp) {
6209                     // Since this channel is only used for this singular request,
6210                     // we are not interested anymore.
6211                     // This is also critical to not leaking memory by having OpenAjax
6212                     // store a bunch of orphaned callback handlers that enclose on
6213                     // our entire ClientServices singleton
6214                     _this.unsubscribe(responseTopic);
6215                     if (handler) {
6216                         handler(JSON.parse(rsp));
6217                     }
6218                 });
6219                 // Then publish the request on the request channel
6220                 _hub.publish(_topics.REQUESTS, data);                
6221             },
6222 
6223             /**
6224              * @private
6225              * Make a request to the request channel to have the Master connect to the XMPP server via BOSH
6226              */
6227             makeConnectionReq : function () {
6228                 // Disallow others (non-masters) from administering BOSH connections that are not theirs
6229                 if (_isMaster && _publisher) {
6230                     _publisher.connect(_config.id, _config.password, _config.xmppDomain);
6231                 } else {
6232                     _log("F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
6233                 }
6234             },
6235 
6236             /**
6237              * @private
6238              * Set's the global logger for this Client Services instance.
6239              * @param {Object} logger
6240              *     Logger object with the following attributes defined:<ul>
6241              *         <li><b>log:</b> function (msg) to simply log a message
6242              *     </ul>
6243              */
6244             setLogger: function (logger) {
6245                 // We want to check the logger coming in so we don't have to check every time it is called.
6246                 if (logger && typeof logger === "object" && typeof logger.log === "function") {
6247                     _logger = logger;
6248                 } else {
6249                     // We are resetting it to an empty object so that _logger.log in .log is falsy.
6250                     _logger = {};
6251                 }
6252             },
6253 
6254             /**
6255              * @private
6256              * Centralized logger.log method for external logger
6257              * @param {String} msg
6258              * Message to log
6259              */
6260             log: _log,
6261 
6262             /**
6263              * @class
6264              * Allow clients to make Finesse API requests and consume Finesse events by
6265              * calling a set of exposed functions. The Services layer will do the dirty
6266              * work of establishing a shared BOSH connection (for designated Master
6267              * modules), consuming events for client subscriptions, and constructing API
6268              * requests.
6269              *
6270              * @constructs
6271              */
6272             _fakeConstuctor: function () {
6273                 /* This is here so we can document init() as a method rather than as a constructor. */
6274             },
6275 
6276             /**
6277              * Initiates the Client Services with the specified config parameters.
6278              * Enabling the Client Services as Master will trigger the establishment
6279              * of a BOSH event connection.
6280              * @param {finesse.gadget.Config} config
6281              *     Configuration object containing properties used for making REST requests:<ul>
6282              *         <li><b>host:</b> The Finesse server IP/host as reachable from the browser
6283              *         <li><b>restHost:</b> The Finesse API IP/host as reachable from the gadget container
6284              *         <li><b>id:</b> The ID of the user. This is an optional param as long as the
6285              *         appropriate authorization string is provided, otherwise it is
6286              *         required.</li>
6287              *         <li><b>password:</b> The password belonging to the user. This is an optional param as
6288              *         long as the appropriate authorization string is provided,
6289              *         otherwise it is required.</li>
6290              *         <li><b>authorization:</b> The base64 encoded "id:password" authentication string. This
6291              *         param is provided to allow the ability to hide the password
6292              *         param. If provided, the id and the password extracted from this
6293              *         string will be used over the config.id and config.password.</li>
6294              *     </ul>
6295              * @throws {Error} If required constructor parameter is missing.
6296              * @example
6297              *      finesse.clientservices.ClientServices.init(finesse.gadget.Config);
6298              */
6299             init: function (config) {
6300                 if (!_inited) {
6301                     //Validate the properties within the config object if one is provided.
6302                     if (!(typeof config === "object" &&
6303                          typeof config.host === "string" && config.host.length > 0 && config.restHost &&
6304                          (typeof config.authorization === "string" ||
6305                                  (typeof config.id === "string")))) {
6306                         throw new Error("Config object contains invalid properties.");
6307                     }
6308 
6309                     // Initialize configuration
6310                     _config = config;
6311 
6312                     // Set shortcuts
6313                     _util = Utilities;
6314                     _authModes = _util.getAuthModes();
6315                     _topics = Topics;
6316 
6317                     //TODO: document when this is properly supported
6318                     // Allows hub and io dependencies to be passed in. Currently only used for unit tests.
6319                     _hub = config.hub || gadgets.Hub;
6320                     _io = config.io || gadgets.io;
6321 
6322                     //If the authorization string is provided, then use that to
6323                     //extract the ID and the password. Otherwise use the ID and
6324                     //password from the respective ID and password params.
6325                     if (_config.authorization) {
6326                         var creds = _util.getCredentials(_config.authorization);
6327                         _config.id = creds.id;
6328                         _config.password = creds.password;
6329                     }
6330                     else {
6331                         _config.authorization = _util.b64Encode(
6332                                 _config.id + ":" + _config.password);
6333                     }
6334 
6335                     //In case if gadgets create their own config instance , add systemAuthMode property inside config object
6336                     if(!_config.systemAuthMode || _config.systemAuthMode === ""){
6337                         _getSystemAuthMode();
6338                     }
6339 
6340                     if(_config.systemAuthMode === _authModes.SSO){
6341                         _accessTokenRefreshHandler(undefined , {authToken : _util.getToken()});
6342                         if(!_config.authToken){
6343                             throw new Error("ClientServices.init() - Access token is unavailable inside Config object.");
6344                         }
6345 
6346                         if (_hub){
6347                               _hub.subscribe(_topics.ACCESS_TOKEN_REFRESHED_EVENT, _accessTokenRefreshHandler);
6348                         }
6349                     }
6350 
6351                     _inited = true;
6352 
6353                     if (_hub) {
6354                         //Subscribe to receive connection information. Since it is possible that
6355                         //the client comes up after the Master comes up, the client will need
6356                         //to make a request to have the Master send the latest connection info.
6357                         //It would be possible that all clients get connection info again.
6358                         this.subscribe(_topics.EVENTS_CONNECTION_INFO, _connInfoHandler);
6359                         _makeConnectionInfoReq();
6360                     }
6361                 }
6362 
6363                 //Return the CS object for object chaining.
6364                 return this;
6365             },
6366 
6367             /**
6368              * @private
6369              * Initializes the BOSH component of this ClientServices instance. This establishes
6370              * the BOSH connection and will trigger the registered handlers as the connection
6371              * status changes respectively:<ul>
6372              *     <li>registerOnConnectingHandler</li>
6373              *     <li>registerOnConnectHandler</li>
6374              *     <li>registerOnDisconnectHandler</li>
6375              *     <li>registerOnDisconnectingHandler</li>
6376              *     <li>registerOnReconnectingHandler</li>
6377              *     <li>registerOnUnloadingHandler</li>
6378              * <ul>
6379              *
6380              * @param {Object} config
6381              *     An object containing the following (optional) handlers for the request:<ul>
6382              *         <li><b>xmppDomain:</b> {String} The domain of the XMPP server. Available from the SystemInfo object.
6383              *         This is used to construct the JID: user@domain.com</li>
6384              *         <li><b>pubsubDomain:</b> {String} The pub sub domain where the pub sub service is running.
6385              *         Available from the SystemInfo object.
6386              *         This is used for creating or removing subscriptions.</li>
6387              *         <li><b>resource:</b> {String} The resource to connect to the notification server with.</li>
6388              *     </ul>
6389              */
6390             initBosh: function (config) {
6391                 //Validate the properties within the config object if one is provided.
6392                 if (!(typeof config === "object" && typeof config.xmppDomain === "string" && typeof config.pubsubDomain === "string")) {
6393                     throw new Error("Config object contains invalid properties.");
6394                 }
6395 
6396                 // Mixin the required information for establishing the BOSH connection
6397                 _config.xmppDomain = config.xmppDomain;
6398                 _config.pubsubDomain = config.pubsubDomain;
6399                 _config.resource = config.resource;
6400 
6401                 //Initiate Master launch sequence
6402                 _becomeMaster();
6403             },
6404 
6405             /**
6406              * @private
6407              * Sets the failover mode to either FAILING or RECOVERED. This will only occur in the master instance and is meant to be
6408              * used by a stereotypical failover monitor type module to notify non-master instances (i.e. in gadgets)
6409              * @param {Object} failoverMode
6410              *     true if failing, false or something falsy when recovered
6411              */
6412             setFailoverMode: function (failoverMode) {
6413                 if (_isMaster) {
6414                     _hub.publish(_topics.EVENTS_CONNECTION_INFO, {
6415                         status: (failoverMode ? _STATUS.FAILING : _STATUS.RECOVERED)
6416                     });
6417                 }
6418             },
6419             
6420             /**
6421              * Method to get the destination host where the rest calls will be made proxied through shindig.
6422              * @return {String}
6423              * @example finesse.clientservices.ClientServices.getRestHost();
6424              */
6425             getRestHost: function () {
6426               return (_config && _config.restHost) || 'localhost';
6427             },
6428 
6429             /**
6430              * @private
6431              * Private accessor used to inject mocked private dependencies for unit testing
6432              */
6433             _getTestObj: function () {
6434                 return {
6435                     setPublisher: function (publisher) {
6436                         _publisher = publisher;
6437                     }
6438                 };
6439             }
6440         };
6441     }());
6442 
6443     window.finesse = window.finesse || {};
6444     window.finesse.clientservices = window.finesse.clientservices || {};
6445     window.finesse.clientservices.ClientServices = ClientServices;
6446 
6447     return ClientServices;
6448 
6449 });
6450 /**
6451  * The following comment prevents JSLint errors concerning undefined global variables.
6452  * It tells JSLint that these identifiers are defined elsewhere.
6453  */
6454 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
6455 
6456 /** The following comment is to prevent jslint errors about 
6457  * using variables before they are defined.
6458  */
6459 /*global Handlebars */
6460 
6461 /**
6462  * JavaScript class to implement common notification
6463  *               functionality.
6464  * 
6465  * @requires Class
6466  * @requires finesse.FinesseBase
6467  */
6468 /** @private */
6469 define('restservices/Notifier',[
6470     'FinesseBase',
6471     'clientservices/ClientServices'
6472 ],
6473 function (FinesseBase, ClientServices) {
6474     var Notifier = FinesseBase.extend({
6475 		/**
6476          * Initializes the notifier object.
6477          */
6478         init : function () {
6479             this._super();
6480             this._listenerCallback = [];
6481         },
6482 
6483         /**
6484          * Add a listener.
6485          * 
6486          * @param callback_function
6487          * @param scope
6488          *            is the callback function to add
6489          */
6490         addListener : function (callback_function, scope) {
6491             var len = this._listenerCallback.length, i, cb, add = true;
6492             for (i = 0; i < len; i += 1) {
6493                 cb = this._listenerCallback[i].callback;
6494                 if (cb === callback_function) {
6495                     // this callback already exists
6496                     add = false;
6497                     break;
6498                 }
6499             }
6500             if (add) {
6501                 this._listenerCallback.push({ "callback": this._isAFunction(callback_function), "scope": (scope || window) });
6502             }            
6503         },
6504 
6505         /**
6506          * Remove a listener.
6507          * 
6508          * @param callback_function
6509          *            is the callback function to remove
6510          * @return {Boolean} true if removed
6511          */
6512         removeListener : function (callback_function) {
6513 
6514             var result = false, len = this._listenerCallback.length, i, cb;
6515             for (i = len - 1; i >= 0; i -=1) {
6516                 cb = this._listenerCallback[i].callback;
6517                 if (cb === callback_function) {
6518                     this._listenerCallback[i] = undefined;
6519                     this._listenerCallback.splice(i, 1);
6520                     result = true;
6521                     break;
6522                 }
6523             }
6524             
6525             return result;
6526         },
6527 
6528         /**
6529 	 * Removes all listeners
6530 	 * @return {undefined}
6531 	 */
6532 	reset: function () {
6533 		this._listenerCallback = [];
6534 	},
6535 
6536 	/**
6537          * Notify all listeners.
6538          * 
6539          * @param obj
6540          *            is the object that has changed
6541          */
6542         notifyListeners : function (obj) {
6543             var len = this._listenerCallback.length, i, callbackFunction, scope;
6544 
6545             for (i = 0; i < len; i += 1) {
6546                 // Be sure that one bad callback does not prevent other listeners
6547                 // from receiving.
6548                 try {
6549                     callbackFunction = this._listenerCallback[i].callback;
6550                     scope = this._listenerCallback[i].scope;
6551                     if (typeof callbackFunction === 'function') {
6552                         callbackFunction.call(scope, obj);
6553                     }
6554                 } catch (err) {
6555                     ClientServices.log("Notifier.js#notifyListeners: Exception caught: ", err);
6556                 }
6557             }
6558         },
6559 
6560         /**
6561          * Gets a copy of the listeners.
6562          * @return changeListenerCopy (array of callbacks)
6563          */
6564         getListeners : function () {
6565             var changeListenerCopy = [], len = this._listenerCallback.length, i;
6566 
6567             for (i = 0; i < len; i += 1) {
6568                 changeListenerCopy.push(this._listenerCallback[i].callback);
6569             }
6570 
6571             return changeListenerCopy;
6572         },
6573         
6574         /**
6575          * Verifies that the handler is function.
6576          * @param handler to verify
6577          * @return the handler 
6578          * @throws Error if not a function
6579          */
6580         _isAFunction : function (handler) {
6581             if (handler === undefined || typeof handler === "function") {
6582                 return handler;
6583             } else {
6584                 throw new Error("handler must be a function");
6585             }
6586         }
6587 	});
6588 	
6589 	window.finesse = window.finesse || {};
6590 	window.finesse.restservices = window.finesse.restservices || {};
6591 	window.finesse.restservices.Notifier = Notifier;
6592 
6593 	/** @namespace JavaScript classes and methods that represent REST objects and collections. */
6594     finesse.restservices = finesse.restservices || {};
6595 	
6596     return Notifier;
6597 });
6598 
6599 /**
6600  * Service to handle caching for rest object in Session storage
6601  */
6602 define('utilities/SessionStorageCachingService',[], function () {
6603     var SessionStorageCachingService = (function () {
6604     	var logger = {};
6605     	return {
6606     		/**
6607     		 * list of objects to be cached in session storage
6608     		 */
6609     		SESSION_STORAGE_CACHED_LIST: ['User'],
6610     		/**
6611     		 * add/set item in the session storage
6612     		 */
6613     		setItem: function(key, value) {
6614     			 sessionStorage.setItem(key,value);
6615     		},
6616     		
6617     		/**
6618     		 * get value from session storage
6619     		 */
6620     		getItem: function(key) {
6621     			return sessionStorage.getItem(key);
6622     		},
6623     		
6624     		/**
6625     		 * Remove item from cache (sesion storage)
6626     		 */
6627     		removeItem: function(key) {
6628     			sessionStorage.removeItem(key);
6629     		},
6630     		
6631     		/**
6632     		 * Check if rest need to be cached 
6633     		 */
6634     		isCacheable: function(restType) {
6635     			return this.SESSION_STORAGE_CACHED_LIST.indexOf(restType) > -1;
6636     		},
6637     		
6638     		/**
6639     		 * clear the cache
6640     		 */
6641     		clearCache: function() {
6642     			var self = this;
6643     			this.SESSION_STORAGE_CACHED_LIST.forEach(function(value) {
6644     				self.removeItem(value);
6645     			});
6646     		}
6647     	}
6648     	
6649     }());
6650     
6651     window.finesse = window.finesse || {};
6652     window.finesse.SessionStorageCachingService = SessionStorageCachingService;
6653     return SessionStorageCachingService;
6654 });
6655 
6656 /**
6657  * Service to handle caching for rest objects.
6658  */
6659 define('utilities/RestCachingService',[], function () {
6660     var RestCachingService = (function () {
6661     	var logger = {};
6662     	return {
6663     		/**
6664     		 * These list of object should be served from cache only in case of failover
6665     		 */
6666     		CACHED_REST_LIST_FOR_FAILOVER: [
6667             	'WrapUpReasons',
6668             	'Media',
6669                 'Workflows',
6670                 'Team',
6671             	'PhoneBooks',
6672                 'MediaPropertiesLayouts',
6673                 'ECCVariableConfig',
6674                 'ReasonCodes',
6675                 'WrapUpReasons'
6676             ],
6677             /**
6678              * List of rest names, where object should be created from cache if these rest objects are already in the cache
6679              */
6680             REST_TO_BE_SERVRED_FROM_CACHE: ['WrapUpReasons', 'ReasonCodes'],
6681             
6682             getRestNameForRequestFromCache: function (restName, url) {
6683             	var cachedList = this.getFailoverCacheableRestList(), restFound = "";;
6684             	if(cachedList){
6685             		if(cachedList.indexOf(restName) > -1){
6686             			return restName;
6687             		}
6688             	}
6689             	
6690             },
6691             
6692             getFailoverCacheableRestList: function(){
6693             	return this.CACHED_REST_LIST_FOR_FAILOVER;
6694             },
6695             
6696             getCacheableRestList: function(){
6697             	return this.REST_TO_BE_SERVRED_FROM_CACHE;
6698             },
6699             
6700             /**
6701              * Check if Rest Cache Data service is reachable. 
6702              * Reachable means RestCacheDataContainer is initialized and IndexDB is supported by the browser.
6703              */
6704             isRestCacheServiceReachable: function() {
6705                 return finesse.idb && finesse.idb.RestCacheDataContainer && finesse.idb.RestCacheDataContainer.isReachable();
6706             },
6707             
6708             isCacheableForFailover: function(restType) {
6709                 // Return false for supervisor as .getUsers on Team object have dynamic data in case of failover and is used in TPG
6710             	// Returns false incase of cfadmin for team.
6711                 if(restType === 'Team' && (!top.window.finesse.container.PageServices || 
6712                 		top.window.finesse.container.PageServices.getUser().hasSupervisorRole())) {
6713                     return false;
6714                 }
6715             	return this.CACHED_REST_LIST_FOR_FAILOVER.indexOf(restType) > -1
6716             },
6717             
6718             isCacheable: function(restType) {
6719             	return this.REST_TO_BE_SERVRED_FROM_CACHE.indexOf(restType) > -1
6720             }
6721     	}
6722     	
6723     }());
6724     
6725     window.finesse = window.finesse || {};
6726     window.finesse.RestCachingService = RestCachingService;
6727     return RestCachingService;
6728 });
6729 
6730 /**
6731  * JavaScript base object that all REST objects should inherit
6732  * from because it encapsulates and provides the common functionality that
6733  * all REST objects need.
6734  *
6735  * @requires finesse.clientservices.ClientServices
6736  * @requires Class
6737  */
6738 
6739 /** @private */
6740 define('restservices/RestBase',[
6741     "FinesseBase",
6742     "utilities/Utilities",
6743     "restservices/Notifier",
6744     "clientservices/ClientServices",
6745     "clientservices/Topics",
6746     "utilities/SessionStorageCachingService",
6747     "utilities/RestCachingService"
6748 ],
6749 function (FinesseBase, Utilities, Notifier, ClientServices, Topics, SessionStorageCachingService, RestCachingService) {
6750     
6751     var RestBase = FinesseBase.extend(/** @lends finesse.restservices.RestBase.prototype */{
6752 
6753         doNotLog: false,        
6754 
6755         /**
6756          * Used by _processUpdate() and restRequest().
6757          * Maps requestIds to object-wrapped callbacks passed to restRequest(),
6758          * so that one of the callbacks can be fired when a corresponding event is
6759          * received inside _processUpdate().
6760          * @private
6761          */
6762         _pendingCallbacks: {},
6763         
6764         /**
6765          * Gets the REST class for the current object.  This object throws an
6766          * exception because subtype must implement.
6767          * @throws {Error} because subtype must implement
6768          * @private
6769          */
6770         getRestClass: function () {
6771             throw new Error("getRestClass(): Not implemented in subtype.");
6772         },
6773 
6774         /**
6775          * Gets the REST type for the current object.  This object throws an
6776          * exception because subtype must implement.
6777          * @throws {Error} because subtype must implement.
6778          * @private
6779          */
6780         getRestType: function () {
6781             throw new Error("getRestType(): Not implemented in subtype.");
6782         },
6783 
6784         /**
6785          * Gets the node path for the current object.
6786          * @private
6787          * @returns {String} The node path
6788          */
6789         getXMPPNodePath: function () {
6790             return this.getRestUrl();
6791         },
6792         
6793         /**
6794          * Boolean function that specifies whether the REST object supports
6795          * requests. True by default. Subclasses should override if false.
6796          * @private
6797          */
6798         supportsRequests: true,
6799 
6800         /**
6801          * Boolean function that specifies whether the REST object supports
6802          * subscriptions. True by default. Subclasses should override if false.
6803          * @private
6804          */
6805         supportsSubscriptions: true,
6806         
6807         /**
6808          * Boolean function that specifies whether the REST object should retain
6809          * a copy of the REST response. False by default. Subclasses should override if true.
6810          * @private
6811          */
6812         keepRestResponse: false,
6813 
6814         /**
6815          * Number that represents the REST Response status that is returned. Only
6816          * set if keepRestResponse is set to true.
6817          * @public
6818          */
6819         restResponseStatus: null,
6820 
6821         /**
6822          * Object to store additional headers to be sent with the REST Request, null by default.
6823          * @private
6824          */
6825         extraHeaders: null,
6826 
6827         /**
6828          * Boolean function that specifies whether the REST object explicitly
6829          * subscribes. False by default. Subclasses should override if true.
6830          * @private
6831          */
6832         explicitSubscription: false,
6833 
6834         /**
6835          * Boolean function that specifies whether subscribing should be
6836          * automatically done at construction. Defaults to true.
6837          * This be overridden at object construction, not by implementing subclasses
6838          * @private
6839          */
6840         autoSubscribe: true,
6841         
6842         /**
6843          *  A unique id which gets appended to the topic for storing subscription details,
6844          *  when multiple subscriptions to the same topic is required.
6845          * @private
6846          */
6847         contextId: '',
6848                 
6849         /**
6850          Default ajax request timout value
6851         */
6852         ajaxRequestTimeout : 5000,
6853         
6854         /**
6855          * Private reference to default logger
6856          * @private
6857          */
6858         _logger: {
6859             log: ClientServices.log,
6860             error: ClientServices.log
6861         },
6862 
6863         /**
6864          * @class
6865          * JavaScript representation of a REST object. Also exposes methods to operate
6866          * on the object against the server.  This object is typically extended into individual
6867          * REST Objects (like Dialog, User, etc...), and shouldn't be used directly.
6868          *
6869          * @constructor
6870          * @param {String} id
6871          *     The ID that uniquely identifies the REST object.
6872          * @param {Object} callbacks
6873          *     An object containing callbacks for instantiation and runtime
6874          *     Callback to invoke upon successful instantiation, passes in REST object.
6875          * @param {Function} callbacks.onLoad(this)
6876          *     Callback to invoke upon loading the data for the first time.
6877          * @param {Function} callbacks.onChange(this)
6878          *     Callback to invoke upon successful update object (PUT)
6879          * @param {Function} callbacks.onAdd(this)
6880          *     Callback to invoke upon successful update to add object (POST)
6881          * @param {Function} callbacks.onDelete(this)
6882          *     Callback to invoke upon successful update to delete object (DELETE)
6883          * @param {Function} callbacks.onError(rsp)
6884          *     Callback to invoke on update error (refresh or event)
6885          *     as passed by finesse.restservices.RestBase.restRequest() <br>
6886          *     { <br>
6887          *         status: {Number} The HTTP status code returned <br>
6888          *         content: {String} Raw string of response <br>
6889          *         object: {Object} Parsed object of response <br>
6890          *         error: {Object} Wrapped exception that was caught <br>
6891          *         error.errorType: {String} Type of error that was caught <br>
6892          *         error.errorMessage: {String} Message associated with error <br>
6893          *     } <br>
6894          * @param {RestBase} [restObj]
6895          *     A RestBase parent object which this object has an association with.
6896          * @constructs
6897          */
6898         init: function (options, callbacks, restObj) {
6899             /**
6900               * Initialize the base class
6901               */
6902             var _this = this;
6903 
6904             this._super();
6905 
6906             if (typeof options === "object") {
6907                 this._id = options.id;
6908                 this._restObj = options.parentObj;
6909                 this.autoSubscribe = (options.autoSubscribe === false) ? false : true;
6910                 this.contextId = options.contextId ? options.contextId : this.contextId;
6911                 // Pass timeout value in options object if we want to override default ajax request timeout of 5 seconds while fetching the resource
6912                 this.ajaxRequestTimeout = options.timeout || this.ajaxRequestTimeout;
6913                 this.doNotSubscribe = options.doNotSubscribe;
6914                 this.doNotRefresh = this.doNotRefresh || options.doNotRefresh;
6915                 if (typeof options.supportsRequests != "undefined") {
6916 	              this.supportsRequests = options.supportsRequests;
6917 	            }
6918                 callbacks = {
6919                     onLoad: options.onLoad,
6920                     onChange: options.onChange,
6921                     onAdd: options.onAdd,
6922                     onDelete: options.onDelete,
6923                     onError: options.onError
6924                 };
6925             } else {
6926                 this._id = options;
6927                 this._restObj = restObj;
6928             }
6929             
6930             // Common stuff
6931             
6932             this._data = {};
6933             
6934             //Contains the full rest response to be processed by upper layers if needed
6935             this._restResponse = undefined;
6936 
6937             this._lastUpdate = {};
6938 
6939             this._util = Utilities;
6940             
6941             this._rcs = RestCachingService;
6942 
6943             //Should be correctly initialized in either a window OR gadget context
6944             this._config = finesse.container.Config;
6945 
6946             // Setup all the notifiers - change, load and error.
6947             this._changeNotifier = new Notifier();
6948             this._loadNotifier = new Notifier();
6949             this._addNotifier = new Notifier();
6950             this._deleteNotifier = new Notifier();
6951             this._errorNotifier = new Notifier();
6952 
6953             this._loaded = false;
6954 
6955             // Protect against null dereferencing of options allowing its
6956             // (nonexistent) keys to be read as undefined
6957             callbacks = callbacks || {};
6958 
6959             this.addHandler('load', callbacks.onLoad);
6960             this.addHandler('change', callbacks.onChange);
6961             this.addHandler('add', callbacks.onAdd);
6962             this.addHandler('delete', callbacks.onDelete);
6963             this.addHandler('error', callbacks.onError);
6964 
6965             // Attempt to get the RestType then synchronize
6966             try {
6967                 this.getRestType();
6968 
6969                 // Only subscribe if this REST object supports subscriptions
6970                 // and autoSubscribe was not requested to be disabled as a construction option
6971                 if (this.supportsSubscriptions && this.autoSubscribe && !this.doNotSubscribe) {
6972                     this.subscribe({
6973                         success: function () {
6974                             //TODO: figure out how to use Function.call() or Function.apply() here...
6975                             //this is exactly the same as the below else case other than the scope of "this"
6976                             if (typeof options === "object" && options.data) {
6977                                 if (!_this._processObject(_this._normalize(options.data))) {
6978                                     // notify of error if we fail to construct
6979                                     _this._errorNotifier.notifyListeners(_this);
6980                                 }
6981                             } else {
6982                                 // Only subscribe if this REST object supports requests
6983                                 if (_this.supportsRequests) {
6984                                     _this._synchronize();
6985                                 }
6986                             }
6987                         },
6988                         error: function (err) {
6989                             _this._errorNotifier.notifyListeners(err);
6990                         }
6991                     });
6992                 } else {
6993                     if (typeof options === "object" && options.data) {
6994                         if (!this._processObject(this._normalize(options.data))) {
6995                             // notify of error if we fail to construct
6996                             this._errorNotifier.notifyListeners(this);
6997                         }
6998                     } else {
6999                         // Only subscribe if this REST object supports requests
7000                         if (this.supportsRequests) {
7001                             this._synchronize();
7002                         }
7003                     }
7004                 }
7005 
7006             } catch (err) {
7007                 this._logger.error('id=' + this._id + ': ' + err);
7008             }
7009         },
7010 
7011         /**
7012          * Determines if the object has a particular property.
7013          * @param obj is the object to examine
7014          * @param property is the property to check for
7015          * @returns {Boolean}
7016          */
7017         hasProperty: function (obj, prop) {
7018             return (obj !== null) && (obj.hasOwnProperty(prop));
7019         },
7020 
7021         /**
7022          * Gets a property from the object.
7023          * @param obj is the object to examine
7024          * @param property is the property to get
7025          * @returns {Property Value} or {Null} if not found
7026          */
7027         getProperty: function (obj, property) {
7028             var result = null;
7029 
7030             if (this.hasProperty(obj, property) === false) {
7031                 result = null;
7032             } else {
7033                 result = obj[property];
7034             }
7035             return result;
7036         },
7037 
7038         /**
7039          * Utility to extracts the ID from the specified REST URI. This is with the
7040          * assumption that the ID is always the last element in the URI after the
7041          * "/" delimiter.
7042          * @param {String} restUri
7043          *     The REST uri (i.e. /finesse/api/User/1000).
7044          * @private
7045          * @returns {String} Id
7046          */
7047         _extractId: function (restObj) {
7048             var obj, restUri = "", strLoc;
7049             for (obj in restObj) {
7050                 if (restObj.hasOwnProperty(obj)) {
7051                     restUri = restObj[obj].uri;
7052                     break;
7053                 }
7054             }
7055             return Utilities.getId(restUri);
7056         },
7057 
7058         /**
7059          * Gets the data for this object.
7060          * @returns {Object} which is contained in data
7061          */
7062         getData: function () {
7063             return this._data;
7064         },
7065         
7066         /**
7067          * Gets the complete REST response to the request made
7068          * @returns {Object} which is contained in data
7069          * @private
7070          */
7071         getRestResponse: function () {
7072             return this._restResponse;
7073         },
7074 
7075         getBaseRestUrl: function() {
7076         	return "/finesse/api";
7077         },
7078          /**
7079          * Gets the REST response status to the request made
7080          * @returns {Integer} response status
7081          * @private
7082          */
7083         getRestResponseStatus: function () {
7084             return this.restResponseStatus;
7085         },
7086 
7087         /**
7088          * The REST URL in which this object can be referenced.
7089          * @return {String}
7090          *     The REST URI for this object.
7091          * @private
7092          */
7093         getRestUrl: function () {
7094             var
7095             restObj = this._restObj,
7096             restUrl = "";
7097 
7098             //Prepend the base REST object if one was provided.
7099             if (restObj instanceof RestBase) {
7100                 restUrl += restObj.getRestUrl();
7101             }
7102             //Otherwise prepend with the default webapp name.
7103             else {
7104                 restUrl += this.getBaseRestUrl();
7105             }
7106 
7107             //Append the REST type.
7108             restUrl += "/" + this.getRestType();
7109 
7110             //Append ID if it is not undefined, null, or empty.
7111             if (this._id) {
7112                 restUrl += "/" + this._id;
7113             }
7114             return restUrl;
7115         },
7116         
7117         /**
7118          * Append additional query parameter in the rest url
7119          */
7120         getRestUrlAdditionalParameters: function () {
7121         	return "";
7122         },
7123 
7124         /**
7125          * Getter for the id of this RestBase
7126          * @returns {String}
7127          *     The id of this RestBase
7128          */
7129         getId: function () {
7130             return this._id;
7131         },
7132 
7133         /**
7134          * Synchronize this object with the server using REST GET request.
7135          * @returns {Object} <br>
7136          *     { <br>
7137          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request <br>
7138          *     } <br>
7139          * @private
7140          */
7141         _synchronize: function (retries) {
7142             // Fetch this REST object
7143             if (typeof this._id === "string") {
7144                 var _this = this, isLoaded = this._loaded, _RETRY_INTERVAL = 10 * 1000, cachedRestResponse, clientServices = ClientServices;
7145                 
7146              
7147                 var url = this.getRestUrl();
7148                 var restName = this.getRestType();
7149                 var cacheKey = Topics.getTopic(url);
7150                 
7151                 // if there is failover scenario the verify which all rest object should be servred from cache.
7152                 var isCacheable = this._config.failoverReload === 'true' ? _this._rcs.isCacheableForFailover.bind(_this._rcs) : _this._rcs.isCacheable.bind(_this._rcs);
7153                 
7154                 // check if index db is accessiable and the rest object should be cerarted from cache.
7155 			    if(_this._rcs.isRestCacheServiceReachable() && isCacheable(restName)) {
7156 			    	var options = {
7157                         key: cacheKey,
7158                         onsuccess: function (result) {
7159                         	// get rest object from cache, otherwise fetch from server
7160                             if(result && result.data && result.data.text) {
7161                             	_this._logger.log("Rest Caching: Rest data successfully fetched from cache : "+ _this.getRestType());
7162                             	var cachedRestResponse = result.data;
7163                             	cachedRestResponse.object = _this._util.xml2js(cachedRestResponse.text);
7164                             	cachedRestResponse.status = cachedRestResponse.rc;
7165                             	_this._processSuccessObject(_this, cachedRestResponse, isLoaded, retries);
7166                             } else {
7167                             	_this._logger.log("Rest Caching: Rest data not available in the cache, making rest call : " + _this.getRestType());
7168                             	return _this._makeRestRequest(_this, isLoaded,retries);
7169                             }
7170                         },
7171                         onerror: function (err) {
7172                         	_this._logger.log("Rest Caching: Error while fetching rest object from cache. Making rest call",url, err);
7173                             return _this._makeRestRequest(_this, isLoaded,retries);
7174                         }
7175                     }
7176 			    	_this._logger.log("Rest Caching: Check if rest data is available in the cache for : "+ cacheKey);
7177                     finesse.idb.RestCacheDataContainer.fetchData(options);
7178 			    	return;
7179                 }
7180 
7181                 if(SessionStorageCachingService.isCacheable(this.getRestType()) && SessionStorageCachingService.getItem(this.getRestType())) {
7182                     var cachedRestResponse = {};
7183                     cachedRestResponse.object = _this._util.xml2js(SessionStorageCachingService.getItem(this.getRestType()));
7184                     _this._logger.log("Rest Caching: Object is created from cached data for : "+ cacheKey);
7185                     //CSCvs00013 fixed. making asynchronous to object be created
7186                     setTimeout(function(){
7187                     	_this._processSuccessObject(_this, cachedRestResponse, isLoaded, retries); 
7188                     }, 0);
7189                     return;
7190                  }
7191                 
7192                 this._makeRestRequest(_this, isLoaded,retries);
7193 
7194             } else {
7195                 throw new Error("Can't construct a <" + this.getRestType() + "> due to invalid id type.");
7196             }
7197         },
7198         
7199         /**
7200          * Make rest calls
7201          */
7202         _makeRestRequest: function(_this, isLoaded,retries) {
7203         	return this._doGET(
7204                 {
7205                     success: function (rsp) {
7206                     	_this._processSuccessObject(_this, rsp, isLoaded, retries);
7207                     },
7208                     error: function (rsp) {
7209                         if (retries > 0) {
7210                             setTimeout(function () {
7211                                 _this._synchronize(retries - 1);
7212                             }, _RETRY_INTERVAL);
7213                             
7214                         } else {
7215                             _this._errorNotifier.notifyListeners(rsp);
7216                         }
7217                     }
7218                 }
7219             );
7220         },
7221 
7222         /**
7223          * Adds an handler to this object.
7224          * If notifierType is 'load' and the object has already loaded, the callback is invoked immediately
7225          * @param {String} notifierType
7226          *     The type of notifier to add to ('load', 'change', 'add', 'delete', 'error')
7227          * @param {Function} callback
7228          *     The function callback to invoke.
7229          * @example
7230          *   // Handler for additions to the Dialogs collection object.  
7231          *   // When Dialog (a RestBase object) is created, add a change handler.
7232          *   _handleDialogAdd = function(dialog) {
7233          *      dialog.addHandler('change', _handleDialogChange);
7234          *   }
7235          */
7236         addHandler: function (notifierType, callback, scope) {
7237             var notifier = null;
7238             try {
7239                 Utilities.validateHandler(callback);
7240 
7241                 notifier = this._getNotifierReference(notifierType);
7242 
7243                 notifier.addListener(callback, scope);
7244                 
7245                 // If load handler is added and object has
7246                 // already been loaded, invoke callback
7247                 // immediately
7248                 if (notifierType === 'load' && this._loaded) {
7249                     callback.call((scope || window), this);
7250                 }
7251             } catch (err) {
7252                 this._logger.error('id=' + this._id + ': ' + err);
7253             }
7254         },
7255 
7256         /**
7257          * Removes a handler from this object.
7258          * @param {String} notifierType
7259          *     The type of notifier to remove ('load', 'change', 'add', 'delete', 'error')
7260          * @param {Function} callback
7261          *     The function to remove.
7262          */
7263         removeHandler: function (notifierType, callback) {
7264             var notifier = null;
7265             try {
7266                 Utilities.validateHandler(callback);
7267 
7268                 notifier = this._getNotifierReference(notifierType);
7269 
7270                 if (typeof(callback) === "undefined")
7271                 {
7272                     // Remove all listeners for the type
7273                     notifier.reset();
7274                 }
7275                 else
7276                 {
7277                     // Remove the specified listener
7278                     finesse.utilities.Utilities.validateHandler(callback);
7279                     notifier.removeListener(callback);
7280                 }
7281             } catch (err) {
7282                 this._logger.error('id=' + this._id + ': ' + err);
7283             }
7284         },
7285 
7286         /**
7287          * Utility method gating any operations that require complete instantiation
7288          * @throws Error
7289          *     If this object was not fully instantiated yet
7290          * @returns {finesse.restservices.RestBase}
7291          *     This RestBase object to allow cascading
7292          */
7293         isLoaded: function () {
7294             if (!this._loaded) {
7295                 throw new Error("Cannot operate on object that is not fully instantiated, use onLoad/onLoadError handlers");
7296             }
7297             return this; // Allow cascading
7298         },
7299 
7300 
7301 
7302         /**
7303          * Force an update on this object. Since an asynchronous GET is performed,
7304          * it is necessary to have an onChange handler registered in order to be
7305          * notified when the response of this returns.
7306          * @param {Integer} retries
7307          *    The number or retry attempts to make.
7308          * @returns {Object} <br>
7309          *     { <br>
7310          *         abort: {function} Function that signifies the callback handler to NOT process the response of the asynchronous request <br>
7311          *     } <br>
7312          */
7313         refresh: function (retries) {
7314             var _this = this;
7315 
7316             if (this.explicitSubscription) {
7317                 this._subscribeNode({
7318                     success: function () {
7319                         //Disallow GETs if object doesn't support it.
7320                         if (!_this.supportsRequests) {
7321                             throw new Error("Object doesn't support request operations.");
7322                         }
7323 
7324                         _this._synchronize(retries);
7325 
7326                         return this; // Allow cascading
7327                     },
7328                     error: function (err) {
7329                         _this._errorNotifier.notifyListeners(err);
7330                     }
7331                 });
7332             } else {
7333                 //Disallow GETs if object doesn't support it.
7334                 if (!this.supportsRequests) {
7335                     throw new Error("Object doesn't support request operations.");
7336                 }
7337 
7338                 return this._synchronize(retries);
7339             }
7340         },
7341 
7342         /**
7343          * Utility method to validate against the known schema of this RestBase
7344          * @param {Object} obj
7345          *     The object to validate
7346          * @returns {Boolean}
7347          *     True if the object fits the schema of this object. This usually
7348          *     means all required keys or nested objects are present.
7349          *     False otherwise.
7350          * @private
7351          */
7352         _validate: function (obj) {
7353             var valid = (typeof obj === "object" && this.hasProperty(obj, this.getRestType()));
7354             if (!valid)
7355             {
7356                 this._logger.error(this.getRestType() + " failed validation! Does your JS REST class return the correct string from getRestType()?");
7357             }
7358             return valid;
7359         },
7360         
7361         /**
7362          * Utility method to check if GET request to be bypassed by Webproxy Server
7363          * @returns {Boolean}
7364          * 
7365          * @private
7366          */
7367         _shouldAllowBypassServerCache: function () {
7368         	return window.finesse.restservices.bypassServerCache;
7369         },
7370 
7371         /**
7372          * Utility method to fetch this RestBase from the server
7373          * @param {finesse.interfaces.RequestHandlers} handlers
7374          *     An object containing the handlers for the request
7375          * @returns {Object} <br>
7376          *     { <br>
7377          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request <br>
7378          *     } <br>
7379          * @private
7380          */
7381         _doGET: function (handlers) {
7382             return this.restRequest(this.getRestUrl(), handlers);
7383         },
7384 
7385         /**
7386          * Common update event handler used by the pubsub callback closure.
7387          * Processes the update event then notifies listeners.
7388          * @param {Object} scope
7389          *     An object containing callbacks to handle the asynchronous get
7390          * @param {Object} update
7391          *     An object containing callbacks to handle the asynchronous get
7392          * @private
7393          */
7394         _updateEventHandler: function (scope, update) {
7395             if (scope._processUpdate(update)) {
7396                 switch (update.object.Update.event) {
7397                 case "POST":
7398                     scope._addNotifier.notifyListeners(scope);
7399                     break;
7400                 case "PUT":
7401                     scope._changeNotifier.notifyListeners(scope);
7402                     break;
7403                 case "DELETE":
7404                     scope._deleteNotifier.notifyListeners(scope);
7405                     break;
7406                 }
7407             }   
7408         },
7409 
7410         /**
7411          * Utility method to create a callback to be given to OpenAjax to invoke when a message
7412          * is published on the topic of our REST URL (also XEP-0060 node).
7413          * This needs to be its own defined method so that subclasses can have their own implementation.
7414          * @returns {Function} callback(update)
7415          *     The callback to be invoked when an update event is received. This callback will
7416          *     process the update and notify listeners.
7417          * @private
7418          */
7419         _createPubsubCallback: function () {
7420             var _this = this;
7421             return function (update) {
7422                 _this._updateEventHandler(_this, update);
7423             };
7424         },
7425 
7426         /**
7427          * Subscribe to pubsub infra using the REST URL as the topic name.
7428          * @param {finesse.interfaces.RequestHandlers} handlers
7429          *     An object containing the handlers for the request
7430          * @private
7431          * @returns {finesse.restservices.RestBase}
7432          */
7433         subscribe: function (callbacks) {
7434             // Only need to do a subscription to client pubsub. No need to trigger
7435             // a subscription on the Finesse server due to implicit subscribe (at
7436             // least for now).
7437             var _this = this,
7438             topic = Topics.getTopic(this.getXMPPNodePath()),
7439             handlers,
7440             successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true, this.contextId);
7441 
7442             callbacks = callbacks || {};
7443 
7444             handlers = {
7445                 /** @private */
7446                 success: function () {
7447                     // Add item to the refresh list in ClientServices to refresh if
7448                     // we recover due to our resilient connection. However, do
7449                     // not add if doNotRefresh flag is set.
7450                     if (!_this.doNotRefresh) {
7451                         ClientServices.addToRefreshList(_this);
7452                     }
7453 
7454                     if (typeof callbacks.success === "function") {
7455                         callbacks.success();
7456                     }
7457                 },
7458                 /** @private */
7459                 error: function (err) {
7460                     if (successful) {
7461                         ClientServices.unsubscribe(topic, this.contextId);
7462                     }
7463 
7464                     if (typeof callbacks.error === "function") {
7465                         callbacks.error(err);
7466                     }
7467                 }
7468             };
7469 
7470             // Request a node subscription only if this object requires explicit subscriptions
7471             if (this.explicitSubscription === true) {
7472                 this._subscribeNode(handlers);
7473             } else {
7474                 if (successful) {
7475                     this._subid = "OpenAjaxOnly";
7476                     handlers.success();
7477                 } else {
7478                     handlers.error();
7479                 }
7480             }
7481 
7482             return this;
7483         },
7484 
7485         /**
7486          * Unsubscribe to pubsub infra using the REST URL as the topic name.
7487          * @param {finesse.interfaces.RequestHandlers} handlers
7488          *     An object containing the handlers for the request
7489          * @private
7490          * @returns {finesse.restservices.RestBase}
7491          */
7492         unsubscribe: function (callbacks) {
7493             // Only need to do a subscription to client pubsub. No need to trigger
7494             // a subscription on the Finesse server due to implicit subscribe (at
7495             // least for now).
7496             var _this = this,
7497             topic = Topics.getTopic(this.getRestUrl()),
7498             handlers;
7499 
7500             // no longer keep track of object to refresh on reconnect
7501             ClientServices.removeFromRefreshList(_this);
7502 
7503             callbacks = callbacks || {};
7504 
7505             handlers = {
7506                 /** @private */
7507                 success: function () {
7508                     if (typeof callbacks.success === "function") {
7509                         callbacks.success();
7510                     }
7511                 },
7512                 /** @private */
7513                 error: function (err) {
7514                     if (typeof callbacks.error === "function") {
7515                         callbacks.error(err);
7516                     }
7517                 }
7518             };
7519 
7520             if (this._subid) {
7521                 ClientServices.unsubscribe(topic, this.contextId);
7522                 // Request a node unsubscribe only if this object requires explicit subscriptions
7523                 if (this.explicitSubscription === true) {
7524                     this._unsubscribeNode(handlers);
7525                 } else {
7526                     this._subid = undefined;
7527                     handlers.success();
7528                 }
7529             } else {
7530                 handlers.success();
7531             }
7532 
7533             return this;
7534         },
7535 
7536         /**
7537          * Private utility to perform node subscribe requests for explicit subscriptions
7538          * @param {finesse.interfaces.RequestHandlers} handlers
7539          *     An object containing the handlers for the request
7540          * @private
7541          */
7542         _subscribeNode: function (callbacks) {
7543             var _this = this;
7544 
7545             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
7546             callbacks = callbacks || {};
7547 
7548             ClientServices.subscribeNode(this.getXMPPNodePath(), function (subid, err) {
7549                 if (err) {
7550                     if (typeof callbacks.error === "function") {
7551                         callbacks.error(err);
7552                     }
7553                 } else {
7554                     // Store the subid on a successful subscribe
7555                     _this._subid = subid;
7556                     if (typeof callbacks.success === "function") {
7557                         callbacks.success();
7558                     }
7559                 }
7560             });
7561         },
7562 
7563         /**
7564          * Private utility to perform node unsubscribe requests for explicit subscriptions
7565          * @param {finesse.interfaces.RequestHandlers} handlers
7566          *     An object containing the handlers for the request
7567          * @private
7568          */
7569         _unsubscribeNode: function (callbacks) {
7570             var _this = this;
7571 
7572             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
7573             callbacks = callbacks || {};
7574 
7575             ClientServices.unsubscribeNode(this.getXMPPNodePath(), this._subid, function (err) {
7576                 _this._subid = undefined;
7577                 if (err) {
7578                     if (typeof callbacks.error === "function") {
7579                         callbacks.error(err);
7580                     }
7581                 } else {
7582                     if (typeof callbacks.success === "function") {
7583                         callbacks.success();
7584                     }
7585                 }
7586             });
7587         },
7588 
7589         /**
7590          * Validate and store the object into the internal data store.
7591          * @param {Object} object
7592          *     The JavaScript object that should match of schema of this REST object.
7593          * @returns {Boolean}
7594          *     True if the object was validated and stored successfully.
7595          * @private
7596          */
7597         _processObject: function (object) {
7598             if (this._validate(object)) {
7599                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
7600 
7601                 // If loaded for the first time, call the load notifiers.
7602                 if (!this._loaded) {
7603                     this._loaded = true;
7604                     this._loadNotifier.notifyListeners(this);
7605                 }
7606 
7607                 return true;
7608             }
7609             return false;
7610         },
7611         
7612         /**
7613          * process the success response
7614          */
7615         _processSuccessObject: function(_this, rsp, isLoaded, retries) {
7616         	if (!_this._processResponse(rsp)) {
7617                 if (retries > 0) {
7618                     setTimeout(function () {
7619                         _this._synchronize(retries - 1);
7620                     }, _RETRY_INTERVAL);
7621                 } else {
7622                     _this._errorNotifier.notifyListeners(_this);
7623                 }
7624             } else {
7625                 // If this object was already "loaded" prior to
7626                 // the _doGET request, then call the
7627                 // changeNotifier
7628                 if (isLoaded) {
7629                     _this._changeNotifier.notifyListeners(_this);
7630                 }
7631             }
7632         },
7633 
7634         /**
7635          * Normalize the object to mitigate the differences between the backend
7636          * and what this REST object should hold. For example, the backend sends
7637          * send an event with the root property name being lower case. In order to
7638          * match the GET, the property should be normalized to an upper case.
7639          * @param {Object} object
7640          *     The object which should be normalized.
7641          * @returns {Object}
7642          *     Return the normalized object.
7643          * @private
7644          */
7645         _normalize: function (object) {
7646             var
7647             restType = this.getRestType(),
7648             // Get the REST object name with first character being lower case.
7649             objRestType = restType.charAt(0).toLowerCase() + restType.slice(1);
7650 
7651             // Normalize payload to match REST object. The payload for an update
7652             // use a lower case object name as oppose to upper case. Only normalize
7653             // if necessary.
7654             if (!this.hasProperty(object, restType) && this.hasProperty(object, objRestType)) {
7655                 //Since the object is going to be modified, clone the object so that
7656                 //it doesn't affect others (due to OpenAjax publishing to other
7657                 //subscriber.
7658                 object = jQuery.extend(true, {}, object);
7659 
7660                 object[restType] = object[objRestType];
7661                 delete(object[objRestType]);
7662             }
7663             return object;
7664         },
7665 
7666         /**
7667          * Utility method to process the response of a successful get
7668          * @param {Object} rsp
7669          *     The response of a successful get
7670          * @returns {Boolean}
7671          *     True if the update was successfully processed (the response object
7672          *     passed the schema validation) and updated the internal data cache,
7673          *     false otherwise.
7674          * @private
7675          */
7676         _processResponse: function (rsp) {
7677             try {
7678                 if (this.keepRestResponse) {
7679                     this._restResponse = rsp.content;
7680                     this.restResponseStatus = rsp.status;
7681                 }
7682                 return this._processObject(rsp.object);
7683             }
7684             catch (err) {
7685                 this._logger.error(this.getRestType() + ': ' + err);
7686             }
7687             return false;
7688         },
7689 
7690         /**
7691          * Method that is called at the end of _processUpdate() which by default
7692          * will just delete the requestId-to-callbacks mapping but can be overridden.
7693          * @param  {String} requestId The requestId of the event
7694          */
7695         _postProcessUpdateStrategy: function (requestId) {
7696             //Clean up _pendingCallbacks now that we fired a callback corresponding to the received requestId.
7697             delete this._pendingCallbacks[requestId];
7698         },
7699 
7700         /**
7701          * Utility method to process the update notification.
7702          * @param {Object} update
7703          *     The payload of an update notification.
7704          * @returns {Boolean}
7705          *     True if the update was successfully processed (the update object
7706          *     passed the schema validation) and updated the internal data cache,
7707          *     false otherwise.
7708          * @private
7709          */
7710         _processUpdate: function (update) {
7711             try {
7712                 var updateObj, requestId, fakeResponse, receivedError;
7713 
7714                 // The backend will send the data object with a lower case. To be
7715                 // consistent with what should be represented in this object, the
7716                 // object name should be upper case. This will normalize the object.
7717                 updateObj = this._normalize(update.object.Update.data);
7718 
7719                 // Store the last event.
7720                 this._lastUpdate = update.object;
7721 
7722                 requestId = this._lastUpdate.Update ? this._lastUpdate.Update.requestId : undefined;
7723 
7724                 if (requestId && this._pendingCallbacks[requestId]) {
7725 
7726                     /*
7727                      * The passed success/error callbacks are expecting to be passed an AJAX response, so construct
7728                      * a simulated/"fake" AJAX response object from the information in the received event.
7729                      * The constructed object should conform to the contract for response objects specified
7730                      * in _createAjaxHandler().
7731                      */
7732                     fakeResponse = {};
7733 
7734                     //The contract says that rsp.content should contain the raw text of the response so we simulate that here.
7735                     //For some reason json2xml has trouble with the native update object, so we serialize a clone of it by
7736                     //doing a parse(stringify(update)).
7737                     fakeResponse.content = this._util.json2xml(gadgets.json.parse(gadgets.json.stringify(update)));
7738 
7739                     fakeResponse.object = {};
7740 
7741                     if (updateObj.apiErrors && updateObj.apiErrors.apiError) { //Error case
7742 
7743                         //TODO: The lowercase -> uppercase ApiErrors translation method below is undesirable, can it be improved?
7744                         receivedError = updateObj.apiErrors.apiError;
7745                         fakeResponse.object.ApiErrors = {};
7746                         fakeResponse.object.ApiErrors.ApiError = {};
7747                         fakeResponse.object.ApiErrors.ApiError.ErrorData = receivedError.errorData || undefined;
7748                         fakeResponse.object.ApiErrors.ApiError.ErrorMessage = receivedError.errorMessage || undefined;
7749                         fakeResponse.object.ApiErrors.ApiError.ErrorType = receivedError.errorType || undefined;
7750                         /**
7751                          * Adding peripheral error codes and desc in response object
7752                          */
7753                         fakeResponse.object.ApiErrors.ApiError.PeripheralErrorCode = receivedError.peripheralErrorCode || undefined;
7754                         fakeResponse.object.ApiErrors.ApiError.PeripheralErrorMsg = receivedError.peripheralErrorMsg || undefined;
7755                         fakeResponse.object.ApiErrors.ApiError.PeripheralErrorText = receivedError.peripheralErrorText || undefined;
7756 
7757                         /*
7758                          * Since this is the error case, supply the error callback with a '400 BAD REQUEST' status code. We don't know what the real
7759                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
7760                          * This is just to conform to the contract for the error callback in _createAjaxHandler().
7761                          **/
7762                         fakeResponse.status = 400;
7763 
7764                     } else { //Success case
7765 
7766                         fakeResponse.object = this._lastUpdate;
7767 
7768                         /*
7769                          * Since this is the success case, supply the success callback with a '200 OK' status code. We don't know what the real
7770                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
7771                          * This is just to conform to the contract for the success callback in _createAjaxHandler().
7772                          **/
7773                         fakeResponse.status = 200;
7774                     }
7775 
7776                     try {
7777 
7778                         if (fakeResponse.object.ApiErrors && this._pendingCallbacks[requestId].error) {
7779                             this._pendingCallbacks[requestId].error(fakeResponse);
7780                         } 
7781                         // HTTP 202 is handled as a success, besides, we cannot infer that a non-error is a success.
7782                         /*else if (this._pendingCallbacks[requestId].success) {
7783                             this._pendingCallbacks[requestId].success(fakeResponse);
7784                         }*/
7785 
7786                     } catch (callbackErr) {
7787 
7788                         this._logger.error(this.getRestType() + ": Caught error while firing callback: " + callbackErr);
7789 
7790                     }
7791 
7792                     this._postProcessUpdateStrategy(requestId);
7793 
7794                 } 
7795 
7796                 return this._processObject(updateObj);
7797             }
7798             catch (err) {
7799                 this._logger.error(this.getRestType() + ': ' + err);
7800             }
7801             return false;
7802         },
7803 
7804         /**
7805          * Utility method to create ajax response handler closures around the
7806          * provided callbacks. Callbacks should be passed through from .ajax().
7807          * makeRequest is responsible for garbage collecting these closures.
7808          * @param {finesse.interfaces.RequestHandlers} handlers
7809          *     An object containing the handlers for the request
7810          * @returns {Object} <br>
7811          *     { <br>
7812          *         abort: {function} Function that signifies the callback handler to NOT process the response when the response returns <br>
7813          *         callback: {function} Callback handler to be invoked when the response returns <br>
7814          *     } <br>
7815          * @private
7816          */
7817         _createAjaxHandler: function (options) {
7818             //We should not need to check this again since it has already been done in .restRequest()
7819             //options = options || {};
7820 
7821             //Flag to indicate whether or not to process the response
7822             var abort = false,
7823 
7824             //Get a reference to the parent User object
7825             _this = this;
7826 
7827             return {
7828 
7829                 abort: function () {
7830                     abort = true;
7831                 },
7832 
7833                 callback: function (rsp) {
7834 
7835                     if (abort) {
7836                         // Do not process the response
7837                         return;
7838                     }
7839 
7840                     var requestId, error = false, rspObj;
7841 
7842                     if (options.success || options.error) {
7843                         rspObj = {
7844                             status: rsp.rc,
7845                             content: rsp.text,
7846                             isUnsent: rsp.isUnsent
7847                         };
7848 
7849                         if (!_this.doNotLog) {
7850                         	_this._logger.log(_this.getRestType() + ": requestId='" + options.uuid + "', Returned with status=" + rspObj.status + ", content='" + rspObj.content + "', isUnsent = " + rspObj.isUnsent);
7851                         }
7852 
7853                         //Some responses may not have a body.
7854                         if (rsp.text && rsp.text.length > 0) {
7855                             try {
7856                                 rspObj.object = _this._util.xml2js(rsp.text);
7857                             } catch (e) {
7858                                 error = true;
7859                                 rspObj.error = {
7860                                     errorType: "parseError",
7861                                     errorMessage: "Could not serialize XML: " + e
7862                                 };
7863                             }
7864                         } else {
7865                             rspObj.object = {};
7866                         }
7867 
7868                         if (!error && rspObj.status >= 200 && rspObj.status < 300) {
7869                             if (options.success) {
7870                                 options.success(rspObj);
7871                             }
7872                         } else {
7873                             if (options.error) {
7874                                 options.error(rspObj);
7875                             }
7876                         }
7877 
7878                         /*
7879                          * If a synchronous error happened after a non-GET request (usually a validation error), we
7880                          * need to clean up the request's entry in _pendingCallbacks since no corresponding event
7881                          * will arrive later. The corresponding requestId should be present in the response headers.
7882                          *
7883                          * It appears Shindig changes the header keys to lower case, hence 'requestid' instead of
7884                          * 'requestId' below.
7885                          **/
7886                         if (rspObj.status !== 202 && rsp.headers && rsp.headers.requestid) {
7887                             requestId = rsp.headers.requestid[0];
7888                             if (_this._pendingCallbacks[requestId]) {
7889                                 delete _this._pendingCallbacks[requestId];
7890                             }
7891                         }
7892                     }
7893                 }
7894             };
7895         },
7896         
7897         /**
7898          * Utility method to make an asynchronous request
7899          * @param {String} url
7900          *     The unencoded URL to which the request is sent (will be encoded)
7901          * @param {Object} options
7902          *     An object containing additional options for the request.
7903          * @param {Object} options.content
7904          *     An object to send in the content body of the request. Will be
7905          *     serialized into XML before sending.
7906          * @param {String} options.method
7907          *     The type of request. Defaults to "GET" when none is specified.
7908          * @param {Function} options.success(rsp)
7909          *     A callback function to be invoked for a successful request.
7910          *     {
7911          *         status: {Number} The HTTP status code returned
7912          *         content: {String} Raw string of response
7913          *         object: {Object} Parsed object of response
7914          *     }
7915          * @param {Function} options.error(rsp)
7916          *     A callback function to be invoked for an unsuccessful request.
7917          *     {
7918          *         status: {Number} The HTTP status code returned
7919          *         content: {String} Raw string of response
7920          *         object: {Object} Parsed object of response
7921          *         error: {Object} Wrapped exception that was caught
7922          *         error.errorType: {String} Type of error that was caught
7923          *         error.errorMessage: {String} Message associated with error
7924          *     }
7925          * @returns {Object} <br>
7926          *     { <br>
7927          *         abort: {function} Function that signifies the callback handler to NOT process the response of this asynchronous request <br>
7928          *     } <br>
7929          * @private
7930         */
7931 		restRequest: function(url,options) {
7932 			// get the resource name of the rest to use it as cache key
7933 			var restName = url.substring(url.lastIndexOf('/') + 1, url.length);
7934 		    // Protect against null dereferencing of options
7935 		    // allowing its (nonexistent) keys to be read as
7936 		    // undefined
7937 		    options = options || {};
7938 		    options.success = this._util
7939 			    .validateHandler(options.success);
7940 		    options.error = this._util
7941 			    .validateHandler(options.error);
7942 	
7943 		    // true if this should be a GET request, false
7944 		    // otherwise
7945 		    if (!options.method || options.method === "GET") {
7946 				// Disable caching for GETs
7947 				if (url.indexOf("?") > -1) {
7948 				    url += "&";
7949 				} else {
7950 				    url += "?";
7951                 }
7952 
7953 				// append additional query parameters
7954 	            url += this.getRestUrlAdditionalParameters();
7955 				url += "nocache=" + this._util.currentTimeMillis();
7956 				
7957 				if(this._shouldAllowBypassServerCache()) {
7958 					url += '&bypassServerCache=true';
7959 				}
7960 				
7961 		    } else {
7962 				/**
7963 				 * If not GET, generate a requestID and add it
7964 				 * to the headers, then wrap callbacks into an
7965 				 * object and store it in _pendingCallbacks. If
7966 				 * we receive a synchronous error response
7967 				 * instead of a 202 as expected, the AJAX
7968 				 * handler will clean up _pendingCallbacks.
7969 				 */
7970 				/*
7971 				 * TODO: Clean up _pendingCallbacks if an entry
7972 				 * persists after a certain amount of time has
7973 				 * passed. In the block below, can store the
7974 				 * current time (new Date().getTime()) alongside
7975 				 * the callbacks in the new _pendingCallbacks
7976 				 * entry. Then iterate through a copty of
7977 				 * _pendingCallbacks, deleting all entries
7978 				 * inside _pendingCallbacks that are older than
7979 				 * a certain threshold (2 minutes for example.)
7980 				 * This solves a potential memory leak issue if
7981 				 * we never receive an event for a given stored
7982 				 * requestId; we don't want to store unfired
7983 				 * callbacks forever.
7984 				 */
7985 				/** @private */
7986 				options.uuid = this._util.generateUUID();
7987 				// params[gadgets.io.RequestParameters.HEADERS].requestId
7988 				// = options.uuid;
7989 				// By default, Shindig strips nearly all of the
7990 				// response headers, but this parameter tells
7991 				// Shindig
7992 				// to send the headers through unmodified; we
7993 				// need to be able to read the 'requestId'
7994 				// header if we
7995 				// get a synchronous error as a result of a
7996 				// non-GET request. (See the bottom of
7997 				// _createAjaxHandler().)
7998 				// params[gadgets.io.RequestParameters.GET_FULL_HEADERS]
7999 				// = "true";
8000 				this._pendingCallbacks[options.uuid] = {};
8001 				this._pendingCallbacks[options.uuid].success = options.success;
8002 				this._pendingCallbacks[options.uuid].error = options.error;
8003 		    }
8004 		    
8005 		    /**
8006 		     * only checking for the host name for now. We could have extended it to scheme and port, but at this point it is not required.
8007 		     */
8008 			var restHost = ClientServices.getRestHost().toLowerCase();
8009 			if(restHost === "localhost" || restHost === window.location.hostname.toLowerCase()) {
8010 				return this._restRequestThroughAjax(url,options, restName);
8011 			} else {
8012 				return this._restRequestThroughShindig(url,options);
8013 			}
8014 		},	 
8015         
8016         /**
8017          * Utility method to make AJAX request
8018          * @param {String} url
8019          *     The unencoded URL to which the request is sent
8020          * @param {Object} options
8021          *     An object containing additional options for the request.
8022          * @returns {Object} <br>
8023          *     { <br>
8024          *         abort: {function} Function that signifies the callback handler to NOT process the response of this request <br>
8025          *     } <br>
8026          */
8027          _restRequestThroughAjax : function(url, options, restName) {
8028 		    var encodedUrl, ajaxHandler, scheme = window.location.protocol, host = window.location.hostname,
8029 		    port = window.location.port, dataTypeAX, contentTypeAX, mtype, postdata = "", auth, rspObj,
8030 		    locale = this._config.locale, userName=this._config.id, processdata=true;
8031 
8032 		    encodedUrl = encodeURI(url)
8033 		    + (window.errorOnRestRequest ? "ERROR" : "");
8034 		    
8035 		    ajaxHandler = this._createAjaxHandler(options);
8036 		    // ClientServices.makeRequest(encodedUrl,
8037 		    // ajaxHandler.callback, params);
8038 		    mtype = options.method || 'GET';
8039 		    encodedUrl = scheme + "//" + host + ":" + port
8040 			    + encodedUrl;
8041 	
8042 		    if (typeof options.content === "object"
8043 			    && typeof options.content !== "undefined") {
8044 				// Except get, all the other operations accepts
8045 				// application/xml only.
8046 				// @Consumes in all the webservices except GET
8047 				// are having this.
8048 				// Sending the local storage logs in zip file uses multipart REST API as of now.
8049 				if (options.contentType === 'multipart') {
8050 					contentTypeAX = false;
8051 					postdata = options.content;
8052 					processdata=false;
8053 				} else {
8054 				
8055 				postdata = this._util.js2xml(options.content);
8056 				if (postdata !== null && postdata !== "") {
8057 				    contentTypeAX = "application/xml";
8058 				} else {
8059 				    // in desktop, reason code GET request was
8060 				    // called with empty object content
8061 				    // if not able to parse any content to post
8062 				    // data, then it is GET only
8063 				    contentTypeAX = "";
8064 				    dataTypeAX = "text";
8065 				}
8066 				}
8067 		    } else {
8068 				// No content type for GET operation, by default
8069 				// it will take text/plain || application/xml.
8070 				// for dataType - GET will result plain text,
8071 				// which we are taking as xml.
8072 				// Queried list of @Produces from code base, all
8073 				// the GET operations accepts text/plain OR
8074 				// application/xml and produces application/xml
8075 				contentTypeAX = "";
8076 				dataTypeAX = "text";
8077 			    }
8078 			    auth = this._util.getAuthHeaderString(this._config);
8079 	
8080 			    if (!this.doNotLog) {
8081 				this._logger.log(this.getRestType()
8082 					+ ": requestId='" + options.uuid
8083 					+ "', Making REST request: method="
8084 					+ (options.method || "GET") + ", url='"
8085 					+ encodedUrl + "'");
8086 			    }
8087 			    
8088 			    // find if rest response should be returned from cache or server call need to make
8089 			    var cacheKey = "";
8090 			    
8091 			    if(this._rcs.getRestNameForRequestFromCache(restName, url)) {
8092 			    	//remove the cache query parameter
8093 			    	if(url.indexOf('?') > -1){
8094 			    		cacheKey = Topics.getTopic(url.split('?')[0]);
8095 			    	} else {
8096 			    		cacheKey = Topics.getTopic(url);
8097 			    	}
8098 			    }
8099 			    	
8100 			    if(this._rcs.isRestCacheServiceReachable() && cacheKey && (!options.method || options.method === 'GET')) {
8101 			    	_this = this;
8102                     var dbOptions = {
8103                         key: cacheKey,
8104                         onsuccess: function (result) {
8105                         	if(result && result.data && result.data.text){
8106                         		_this._logger.log("Rest Caching: Rest data successfully fetched from cache : "+ _this.getRestType());
8107                             	// Used in Automation - To confirm that cache data was used during failover
8108                             	sessionStorage.setItem("restCacheUsed", "true");
8109                             	ajaxHandler.callback(result.data);
8110                         	} else {
8111                         		_this._logger.log("Rest Caching: Rest data not available in the cache, making rest call : " + _this.getRestType());
8112                         		callAjaxRequest(_this);
8113                         	}
8114                         },
8115                         onerror: function () {
8116                             callAjaxRequest();
8117                         }
8118                     }
8119                     _this._logger.log("Rest Caching: Check if rest data is available in the cache for : "+ cacheKey);
8120                     finesse.idb.RestCacheDataContainer.fetchData(dbOptions);
8121 			    	return;
8122                 }
8123 
8124                 if(SessionStorageCachingService.isCacheable(this.getRestType()) && options.method === 'GET' && SessionStorageCachingService.getItem(this.getRestType())) {
8125                     ajaxHandler.callback(Utilities.xml2js(SessionStorageCachingService.getItem(this.getRestType())));
8126                     return;
8127                  }
8128 				    
8129                 var self = this;
8130                 callAjaxRequest();
8131 			    function callAjaxRequest(_this) {
8132 			    	self = _this? _this : self;
8133                     $.ajax({
8134                         url : encodedUrl,
8135                         headers : self.extraHeaders || {},
8136                         beforeSend : function(xhr) {
8137                             xhr.setRequestHeader(
8138                                 "Authorization", auth);
8139                             xhr.setRequestHeader("locale",
8140                                 locale);
8141                             xhr.setRequestHeader("f_username",
8142                                     userName);
8143                             if (options.uuid) {
8144                             xhr.setRequestHeader(
8145                                 "requestId",
8146                                 options.uuid);
8147                             }
8148                         },
8149                         dataType : dataTypeAX, // xml or json?
8150                         contentType : contentTypeAX,
8151                         processData: processdata,
8152                         type : mtype,
8153                         data : postdata,
8154                         timeout : self.ajaxRequestTimeout,
8155                         success : function(response, status,
8156                             xhr) {
8157                             var rspObj = {
8158                             rc : xhr.status,
8159                             text : response,
8160                             isUnsent : xhr.readyState==0,
8161                             headers : {
8162                                 requestid : xhr
8163                                     .getResponseHeader("requestId")
8164                             },
8165                             restType: self.getRestType()
8166                             };
8167                             // check of rest responsed should be cached or not
8168                             var _rcs = window.finesse.RestCachingService;
8169                             if(_rcs.isCacheableForFailover(self.getRestType()) || _rcs.isCacheableForFailover(options.restType)){
8170                             	var cacheKey = "";
8171                             	if(options.restType){
8172                             		cacheKey = Topics.getTopic(self.getRestUrl() + options.restType);
8173                             	} else {
8174                             		cacheKey = Topics.getTopic(self.getRestUrl());
8175                             	}
8176                             	
8177 	                            if(_rcs.isRestCacheServiceReachable()){
8178 	                            	self._logger.log("Saving rest data in the cache: "+cacheKey)
8179 	                                finesse.idb.RestCacheDataContainer.saveOrUpdateData([{
8180 	                                    key: cacheKey, data: rspObj
8181 	                                }]);
8182 	                            }
8183                             }
8184                             if(SessionStorageCachingService.isCacheable(self.getRestType()) && rspObj.text !== null) {
8185                             	self._logger.log("Saving rest data in the session storage cache: "+self.getRestType())
8186                             	SessionStorageCachingService.setItem(self.getRestType(),rspObj.text);
8187                             }
8188                             ajaxHandler.callback(rspObj);
8189                         },
8190                         error : function(jqXHR, request, error) {
8191                             var resObj = {
8192                             rc : jqXHR.status,
8193                             text : jqXHR.responseText,
8194                             isUnsent : jqXHR.readyState==0,
8195                             headers : {
8196                                 requestid : jqXHR
8197                                     .getResponseHeader("requestId")
8198                             }
8199                             };
8200                             ajaxHandler.callback(resObj);
8201                         }
8202                         });
8203                 }
8204 			    return {
8205 				abort : ajaxHandler.abort
8206 			    };
8207 			},
8208 
8209         /**
8210          * Utility method to make request through Shindig
8211          * @param {String} url
8212          *     The unencoded URL to which the request is sent
8213          * @param {Object} options
8214          *     An object containing additional options for the request.
8215          * @returns {Object} <br>
8216          *     { <br>
8217          *         abort: {function} Function that signifies the callback handler to NOT process the response of this request <br>
8218          *     } <br>
8219          */
8220 	  _restRequestThroughShindig: function(url, options) {
8221           var params, encodedUrl, ajaxHandler;
8222 
8223           params = {};
8224           options = options || {};
8225 
8226           params[gadgets.io.RequestParameters.HEADERS] = this.extraHeaders || {};
8227           params[gadgets.io.RequestParameters.METHOD] = options.method;
8228 
8229 
8230           if (!options.method || options.method === "GET") {
8231 
8232           } else {
8233               params[gadgets.io.RequestParameters.HEADERS].requestId = options.uuid;           
8234               params[gadgets.io.RequestParameters.GET_FULL_HEADERS] = "true";
8235 
8236           }
8237 
8238           encodedUrl = encodeURI(url) + (window.errorOnRestRequest ? "ERROR" : "");
8239 
8240           if (!this.doNotLog) {
8241               this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', Making REST request: method=" + (options.method || "GET") + ", url='" + encodedUrl + "'");
8242           }
8243 
8244          
8245           if (typeof options.content === "object") {
8246             
8247               params[gadgets.io.RequestParameters.HEADERS]["Content-Type"] = "application/xml";
8248               params[gadgets.io.RequestParameters.POST_DATA] = this._util.js2xml(options.content);
8249               
8250               if (!this.doNotLog) {
8251                   this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', POST_DATA='" + params[gadgets.io.RequestParameters.POST_DATA] + "'");
8252               }
8253           }
8254 
8255           ajaxHandler = this._createAjaxHandler(options);
8256           ClientServices.makeRequest(encodedUrl, ajaxHandler.callback, params);
8257 
8258           return {
8259               abort: ajaxHandler.abort
8260           };
8261 	  },
8262 	  
8263     /**
8264 	 * Retrieves a reference to a particular notifierType.
8265 	 * 
8266 	 * @param notifierType
8267 	 *                is a string which indicates the notifier to retrieve
8268 	 *                ('load', 'change', 'add', 'delete', 'error')
8269 	 * @return {Notifier}
8270 	 * @private
8271 	 */
8272         _getNotifierReference: function (notifierType) {
8273             var notifierReference = null;
8274             if (notifierType === 'load') {
8275                 notifierReference = this._loadNotifier;
8276             } else if (notifierType === 'change') {
8277                 notifierReference = this._changeNotifier;
8278             } else if (notifierType === 'add') {
8279                 notifierReference = this._addNotifier;
8280             } else if (notifierType === 'delete') {
8281                 notifierReference = this._deleteNotifier;
8282             } else if (notifierType === 'error') {
8283                 notifierReference = this._errorNotifier;
8284             } else {
8285                 throw new Error("_getNotifierReference(): Trying to get unknown notifier(notifierType=" + notifierType + ")");
8286             }
8287 
8288             return notifierReference;
8289         }
8290     });
8291 
8292     window.finesse = window.finesse || {};
8293     window.finesse.restservices = window.finesse.restservices || {};
8294     window.finesse.restservices.RestBase = RestBase;
8295     
8296     return RestBase;
8297 });
8298 
8299 /** The following comment is to prevent jslint errors about 
8300  * using variables before they are defined.
8301  */
8302 /*global finesse*/
8303 
8304 /**
8305  * JavaScript base object that all REST collection objects should
8306  * inherit from because it encapsulates and provides the common functionality
8307  * that all REST objects need.
8308  *
8309  * @requires finesse.clientservices.ClientServices
8310  * @requires Class
8311  * @requires finesse.FinesseBase
8312  * @requires finesse.restservices.RestBase
8313  */
8314 
8315 /**
8316  * @class
8317  * JavaScript representation of a REST collection object.
8318  *
8319  * @constructor
8320  * @param {Function} callbacks.onCollectionAdd(this)
8321  *     Callback to invoke upon successful item addition to the collection.
8322  * @param {Function} callbacks.onCollectionDelete(this)
8323  *     Callback to invoke upon successful item deletion from the collection.
8324  * @borrows finesse.restservices.RestBase as finesse.restservices.RestCollectionBase
8325  */
8326 /** @private */
8327 define('restservices/RestCollectionBase',[
8328     'restservices/RestBase',
8329     'utilities/Utilities',
8330     'restservices/Notifier'
8331 ],
8332 function (RestBase, Utilities, Notifier) {
8333     var RestCollectionBase = RestBase.extend(/** @lends finesse.restservices.RestCollectionBase.prototype */{
8334 
8335         /**
8336          * Boolean function that specifies whether the collection handles subscribing
8337          * and propagation of events for the individual REST object items the
8338          * collection holds. False by default. Subclasses should override if true.
8339          * @private
8340          */
8341         supportsRestItemSubscriptions: false,
8342 
8343         /**
8344          * Gets the constructor the individual items that make of the collection.
8345          * For example, a Dialogs collection object will hold a list of Dialog items.
8346          * @throws Error because subtype must implement.
8347          * @private
8348          */
8349         getRestItemClass: function () {
8350             throw new Error("getRestItemClass(): Not implemented in subtype.");
8351         },
8352 
8353         /**
8354          * Gets the REST type of the individual items that make of the collection.
8355          * For example, a Dialogs collection object will hold a list of Dialog items.
8356          * @throws Error because subtype must implement.
8357          * @private
8358          */
8359         getRestItemType: function () {
8360             throw new Error("getRestItemType(): Not implemented in subtype.");
8361         },
8362 
8363         /**
8364          * The base REST URL in which items this object contains can be referenced.
8365          * @return {String}
8366          *     The REST URI for items this object contains.
8367          * @private
8368          */
8369         getRestItemBaseUrl: function () {
8370             var
8371             restUrl = "/finesse/api";
8372 
8373             //Append the REST type.
8374             restUrl += "/" + this.getRestItemType();
8375 
8376             return restUrl;
8377         },
8378 
8379          /*
8380          * Creates a new object from the given data
8381          * @param data - data object
8382          * @private
8383          */
8384         _objectCreator: function (data) {
8385             var objectId = this._extractId(data),
8386             newRestObj = this._collection[objectId],
8387             _this = this;
8388 
8389             //Prevent duplicate entries into collection.
8390             if (!newRestObj) {
8391                 //Create a new REST object using the subtype defined by the
8392                 //overridden method.
8393                 newRestObj = new (this.getRestItemClass())({
8394                     doNotSubscribe: this.handlesItemSubscription,
8395                     doNotRefresh: this.handlesItemRefresh,
8396                     id: objectId,
8397                     data: data,
8398                     onLoad: function (newObj) {
8399                         //Normalize and add  REST object to collection datastore.
8400                         _this._collection[objectId] = newObj;
8401                         _this._collectionAddNotifier.notifyListeners(newObj);
8402                         _this.length += 1;
8403                     }
8404                 });
8405             }
8406             else {
8407                 //If entry already exist in collection, process the new event,
8408                 //and notify all change listeners since an existing object has
8409                 //change. This could happen in the case when the Finesse server
8410                 //cycles, and sends a snapshot of the user's calls.
8411                 newRestObj._processObject(data);
8412                 newRestObj._changeNotifier.notifyListeners(newRestObj);
8413             }
8414         },
8415 
8416         /*
8417          * Deletes and object and notifies its handlers
8418          * @param data - data object
8419          * @private
8420          */
8421         _objectDeleter: function (data) {
8422             var objectId = this._extractId(data),
8423             object = this._collection[objectId];
8424             if (object) {
8425                 //Even though this is a delete, let's make sure the object we are passing has got good data
8426                 object._processObject(data);
8427                 //Notify listeners and delete from internal datastore.
8428                 this._collectionDeleteNotifier.notifyListeners(object);
8429                 delete this._collection[objectId];
8430                 this.length -= 1;
8431             }
8432         },
8433 
8434          /**
8435           * Creates an anonymous function for notifiying error listeners of a particular object
8436           * data.
8437           * @param obj - the objects whose error listeners to notify
8438           * @returns {Function}
8439           *     Callback for notifying of errors
8440           * @private
8441           */
8442         _createErrorNotifier: function (obj) {
8443             return function (err) {
8444                 obj._errorNotifier.notifyListeners(err);
8445             };
8446         },
8447 
8448          /**
8449           * Replaces the collection with a refreshed list using the passed in
8450           * data.
8451           * @param data - data object (usually this._data)
8452           * @private
8453           */
8454          _buildRefreshedCollection: function (data) {
8455             var i, dataObject, object, objectId, dataArray, newIds = [], foundFlag;
8456             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
8457                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
8458             } else {
8459                 dataArray = [];
8460             }
8461 
8462             // iterate through each item in the new data and add to or update collection
8463             for (i = 0; i < dataArray.length; i += 1) {
8464                 dataObject = {};
8465                 dataObject[this.getRestItemType()] = dataArray[i];
8466                 objectId = this._extractId(dataObject);
8467 
8468                 this._objectCreator(dataObject);
8469                 newIds.push(objectId);
8470 
8471                 // resubscribe if the object requires an explicit subscription
8472                 object = this._collection[objectId];
8473                 if (this.handlesItemRefresh && object.explicitSubscription) {
8474                     object._subscribeNode({
8475                         error: this._createErrorNotifier(object)
8476                     });
8477                 }
8478             }
8479 
8480             // now clean up items (if any) that were removed
8481             for (objectId in this._collection) {
8482                 if (this._collection.hasOwnProperty(objectId)) {
8483                     foundFlag = false;
8484                     for (i = newIds.length - 1; i >= 0; i -= 1) {
8485                         if (newIds[i] === objectId) {
8486                             foundFlag = true;
8487                             break;
8488                         }
8489                     }
8490                     // did not find in updated list, so delete it
8491                     if (!foundFlag) {
8492                         this._objectDeleter({'data': this._collection[objectId]._data});
8493                     }
8494                 }
8495             }
8496         },
8497 
8498          /**
8499           * The actual refresh operation, refactored out so we don't have to repeat code
8500           * @private
8501           */
8502         _RESTRefresh: function () {
8503             var _this = this;
8504             this._doGET({
8505                 success: function(rsp) {
8506                     if (_this._processResponse(rsp)) {
8507                         _this._buildRefreshedCollection(_this._data);
8508                     } else {
8509                         _this._errorNotifier.notifyListeners(_this);
8510                     }
8511                 },
8512                 error: function(rsp) {
8513                     _this._errorNotifier.notifyListeners(rsp);
8514                 }
8515             });            
8516         },
8517 
8518         /**
8519          * Force an update on this object. Since an asynchronous GET is performed,
8520          * it is necessary to have an onChange handler registered in order to be
8521          * notified when the response of this returns.
8522          * @returns {finesse.restservices.RestBaseCollection}
8523          *     This RestBaseCollection object to allow cascading
8524          */
8525          refresh: function() {
8526             var _this = this, isLoaded = this._loaded;
8527 
8528             // resubscribe if the collection requires an explicit subscription
8529             if (this.explicitSubscription) {
8530                 this._subscribeNode({
8531                     success: function () {
8532                         _this._RESTRefresh();
8533                     },
8534                     error: function (err) {
8535                         _this._errorNotifier.notifyListeners(err);
8536                     }
8537                 });
8538             } else {
8539                 this._RESTRefresh();
8540             }
8541 
8542             return this; // Allow cascading
8543          },
8544 
8545         /**
8546          * @private
8547          * The _addHandlerCb and _deleteHandlerCb require that data be passed in the
8548          * format of an array of {(Object Type): object} objects. For example, a
8549          * queues object would return [{Queue: queue1}, {Queue: queue2}, ...].
8550          * @param skipOuterObject If {true} is passed in for this param, then the "data"
8551          *                           property is returned instead of an object with the
8552          *                           data appended.
8553          * @return {Array}
8554          */
8555         extractCollectionData: function (skipOuterObject) {
8556             var restObjs,
8557             obj,
8558             result = [],
8559             _this = this;
8560             
8561             if (this._data)
8562             {
8563                 restObjs = this._data[this.getRestItemType()];
8564     
8565                 if (restObjs)
8566                 {
8567                     // check if there are multiple objects to pass
8568                     if (!$.isArray(restObjs))
8569                     {
8570                         restObjs = [restObjs];
8571                     }
8572     
8573                     // if so, create an object for each and add to result array
8574                     $.each(restObjs, function (id, object) {
8575                         if (skipOuterObject === true)
8576                         {
8577                             obj = object;
8578                         }
8579                         else
8580                         {
8581                             obj = {};
8582                             obj[_this.getRestItemType()] = object;
8583                         }
8584                         result.push(obj);
8585                     });
8586                 }
8587                 
8588             }
8589             
8590             return result;
8591         },
8592 
8593         /**
8594          * For Finesse, collections are handled uniquely on a POST and
8595          * doesn't necessary follow REST conventions. A POST on a collection
8596          * doesn't mean that the collection has been created, it means that an
8597          * item has been added to the collection. This function will generate
8598          * a closure which will handle this logic appropriately.
8599          * @param {Object} scope
8600          *     The scope of where the callback should be invoked.
8601          * @private
8602          */
8603         _addHandlerCb: function (scope) {
8604             return function (restItem) {
8605                 var data = restItem.extractCollectionData();               
8606 
8607                 $.each(data, function (id, object) {
8608                     scope._objectCreator(object);
8609                 });
8610             };
8611         },
8612 
8613         /**
8614          * For Finesse, collections are handled uniquely on a DELETE and
8615          * doesn't necessary follow REST conventions. A DELETE on a collection
8616          * doesn't mean that the collection has been deleted, it means that an
8617          * item has been deleted from the collection. This function will generate
8618          * a closure which will handle this logic appropriately.
8619          * @param {Object} scope
8620          *     The scope of where the callback should be invoked.
8621          * @private
8622          */
8623         _deleteHandlerCb: function (scope) {
8624             return function (restItem) {
8625                 var data = restItem.extractCollectionData();
8626 
8627                 $.each(data, function (id, obj) {
8628                     scope._objectDeleter(obj);
8629                 });
8630             };
8631         },
8632 
8633         /**
8634          * Utility method to process the update notification for Rest Items
8635          * that are children of the collection whose events are published to
8636          * the collection's node.
8637          * @param {Object} update
8638          *     The payload of an update notification.
8639          * @returns {Boolean}
8640          *     True if the update was successfully processed (the update object
8641          *     passed the schema validation) and updated the internal data cache,
8642          *     false otherwise.
8643          * @private
8644          */
8645         _processRestItemUpdate: function (update) {
8646             var object, objectId, updateObj = update.object.Update;
8647 
8648             //Extract the ID from the source if the Update was an error.
8649             if (updateObj.data.apiErrors) {
8650                 objectId = Utilities.getId(updateObj.source);
8651             }
8652             //Otherwise extract from the data object itself.
8653             else {
8654                 objectId = this._extractId(updateObj.data);
8655             }
8656 
8657             object = this._collection[objectId];
8658             if (object) {
8659                 if (object._processUpdate(update)) {
8660                     switch (updateObj.event) {
8661                     case "POST":
8662                         object._addNotifier.notifyListeners(object);
8663                         break;
8664                     case "PUT":
8665                         object._changeNotifier.notifyListeners(object);
8666                         break;
8667                     case "DELETE":
8668                         object._deleteNotifier.notifyListeners(object);
8669                         break;
8670                     }
8671                 }
8672             }
8673         },
8674 
8675         /**
8676          * SUBCLASS IMPLEMENTATION (override):
8677          * For collections, this callback has the additional responsibility of passing events
8678          * of collection item updates to the item objects themselves. The collection needs to
8679          * do this because item updates are published to the collection's node.
8680          * @returns {Function}
8681          *     The callback to be invoked when an update event is received
8682          * @private
8683          */
8684         _createPubsubCallback: function () {
8685             var _this = this;
8686             return function (update) {
8687                 //If the source of the update is our REST URL, this means the collection itself is modified
8688                 if (update.object.Update.source === _this.getRestUrl()) {
8689                     _this._updateEventHandler(_this, update);
8690                 } else {
8691                     //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
8692                     //rest item update of one of our children that was published on our node (OpenAjax topic)
8693                     _this._processRestItemUpdate(update);
8694                 }
8695             };
8696         },
8697 
8698         /**
8699          * @class
8700          * This is the base collection object. 
8701          *
8702          * @constructs
8703          * @augments finesse.restservices.RestBase
8704          * @see finesse.restservices.Contacts
8705          * @see finesse.restservices.Dialogs
8706          * @see finesse.restservices.PhoneBooks
8707          * @see finesse.restservices.Queues
8708          * @see finesse.restservices.WorkflowActions
8709          * @see finesse.restservices.Workflows
8710          * @see finesse.restservices.WrapUpReasons
8711          */
8712         _fakeConstuctor: function () {
8713             /* This is here to hide the real init constructor from the public docs */
8714         },
8715         
8716        /**
8717          * @private
8718          * @param {Object} options
8719          *     An object with the following properties:<ul>
8720          *         <li><b>id:</b> The id of the object being constructed</li>
8721          *         <li><b>onCollectionAdd(this): (optional)</b> when an object is added to this collection</li>
8722          *         <li><b>onCollectionDelete(this): (optional)</b> when an object is removed from this collection</li>
8723          *         <li><b>onLoad(this): (optional)</b> when the collection is successfully loaded from the server</li>
8724          *         <li><b>onChange(this): (optional)</b> when an update notification of the collection is received. 
8725          *         This does not include adding and deleting members of the collection</li>
8726          *         <li><b>onAdd(this): (optional)</b> when a notification that the collection is created is received</li>
8727          *         <li><b>onDelete(this): (optional)</b> when a notification that the collection is deleted is received</li>
8728          *         <li><b>onError(rsp): (optional)</b> if loading of the collection fails, invoked with the error response object:<ul>
8729          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8730          *             <li><b>content:</b> {String} Raw string of response</li>
8731          *             <li><b>object:</b> {Object} Parsed object of response</li>
8732          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8733          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8734          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8735          *             </ul></li>
8736          *         </ul></li>
8737          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8738          **/
8739         init: function (options) {
8740 
8741             options = options || {};
8742             options.id = "";
8743 
8744             //Make internal datastore collection to hold a list of objects.
8745             this._collection = {};
8746             this.length = 0;
8747 
8748             //Collections will have additional callbacks that will be invoked when
8749             //an item has been added/deleted.
8750             this._collectionAddNotifier = new Notifier();
8751             this._collectionDeleteNotifier = new Notifier();
8752 
8753             //Initialize the base class.
8754             this._super(options);
8755 
8756             this.addHandler('collectionAdd', options.onCollectionAdd);
8757             this.addHandler('collectionDelete', options.onCollectionDelete);
8758 
8759             //For Finesse, collections are handled uniquely on a POST/DELETE and
8760             //doesn't necessary follow REST conventions. A POST on a collection
8761             //doesn't mean that the collection has been created, it means that an
8762             //item has been added to the collection. A DELETE means that an item has
8763             //been removed from the collection. Due to this, we are attaching
8764             //special callbacks to the add/delete that will handle this logic.
8765             this.addHandler("add", this._addHandlerCb(this));
8766             this.addHandler("delete", this._deleteHandlerCb(this));
8767         },
8768 
8769         /**
8770          * Returns the collection.
8771          * @returns {Object}
8772          *     The collection as an object
8773          */
8774         getCollection: function () {
8775             //TODO: is this safe? or should we instead return protected functions such as .each(function)?
8776             return this._collection;
8777         },
8778 
8779         /**
8780          * Utility method to build the internal collection data structure (object) based on provided data
8781          * @param {Object} data
8782          *     The data to build the internal collection from
8783          * @private
8784          */
8785         _buildCollection: function (data) {
8786             var i, object, objectId, dataArray;
8787             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
8788                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
8789                 for (i = 0; i < dataArray.length; i += 1) {
8790 
8791                     object = {};
8792                     object[this.getRestItemType()] = dataArray[i];
8793                     objectId = this._extractId(object);
8794                     this._collection[objectId] = new (this.getRestItemClass())({
8795                         doNotSubscribe: this.handlesItemSubscription,
8796                         doNotRefresh: this.handlesItemRefresh,
8797                         id: objectId,
8798                         data: object
8799                     });
8800                     this.length += 1;
8801                 }
8802             }
8803         },
8804 
8805         /**
8806          * Called to know whether to include an item in the _collection and _data. Return false to keep it, true to filter out (discard) it.
8807          * Override this in subclasses if you need only object with certain attribute values.
8808          * @param  {Object} item Item to test.
8809          * @return {Boolean} False to keep, true to filter out (discard);
8810          */
8811         _filterOutItem: function (item) {
8812             return false;
8813         },
8814     
8815         /**
8816          * Validate and store the object into the internal data store.
8817          * SUBCLASS IMPLEMENTATION (override):
8818          * Performs collection specific logic to _buildCollection internally based on provided data
8819          * @param {Object} object
8820          *     The JavaScript object that should match of schema of this REST object.
8821          * @returns {Boolean}
8822          *     True if the object was validated and stored successfully.
8823          * @private
8824          */
8825         _processObject: function (object) {
8826             var i,
8827                 restItemType = this.getRestItemType(),
8828                 items;
8829             if (this._validate(object)) {
8830                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
8831     
8832                 // If a subclass has overriden _filterOutItem then we'll need to run through the items and remove them
8833                 if (this._data)
8834                 {
8835                     items = this._data[restItemType];
8836     
8837                     if (typeof(items) !== "undefined")
8838                     {
8839                         if (typeof(items.length) === "undefined")
8840                         {
8841                             // Single object
8842                             if (this._filterOutItem(items))
8843                             {
8844                                 this._data[restItemType] = items = [];
8845                             }
8846                             
8847                         }
8848                         else
8849                         {
8850                             // filter out objects
8851                             for (i = items.length - 1; i !== -1; i = i - 1)
8852                             {
8853                                 if (this._filterOutItem(items[i]))
8854                                 {
8855                                     items.splice(i, 1);
8856                                 }
8857                             }
8858                         }
8859                     }
8860                 }
8861     
8862                 // If loaded for the first time, call the load notifiers.
8863                 if (!this._loaded) {
8864                     this._buildCollection(this._data);
8865                     this._loaded = true;
8866                     this._loadNotifier.notifyListeners(this);
8867                 }
8868                 
8869                 return true;
8870                 
8871             }
8872             return false;
8873         },
8874 
8875         /**
8876          * Retrieves a reference to a particular notifierType.
8877          * @param {String} notifierType
8878          *      Specifies the notifier to retrieve (load, change, error, add, delete)
8879          * @return {Notifier} The notifier object.
8880          */
8881         _getNotifierReference: function (notifierType) {
8882             var notifierReference;
8883 
8884             try {
8885                 //Use the base method to get references for load/change/error.
8886                 notifierReference = this._super(notifierType);
8887             } catch (err) {
8888                 //Check for add/delete
8889                 if (notifierType === "collectionAdd") {
8890                     notifierReference = this._collectionAddNotifier;
8891                 } else if (notifierType === "collectionDelete") {
8892                     notifierReference = this._collectionDeleteNotifier;
8893                 } else {
8894                     //Rethrow exception from base class.
8895                     throw err;
8896                 }
8897             }
8898             return notifierReference;
8899         }
8900     });
8901     
8902     window.finesse = window.finesse || {};
8903     window.finesse.restservices = window.finesse.restservices || {};
8904     window.finesse.restservices.RestCollectionBase = RestCollectionBase;
8905     
8906     return RestCollectionBase;
8907 });
8908 
8909 /**
8910  * JavaScript representation of the Finesse Dialog object.
8911  *
8912  * @requires finesse.clientservices.ClientServices
8913  * @requires Class
8914  * @requires finesse.FinesseBase
8915  * @requires finesse.restservices.RestBase
8916  */
8917 
8918 /** @private */
8919 define('restservices/DialogBase',[
8920         'restservices/RestBase',
8921         'utilities/Utilities'
8922     ],
8923     function (RestBase, Utilities) {
8924         var DialogBase = RestBase.extend(/** @lends finesse.restservices.DialogBase.prototype */{
8925 
8926             /**
8927              * @class
8928              * A DialogBase is an attempted connection between or among multiple participants,
8929              * for example, a regular phone call, a chat, or an email.
8930              *
8931              * This object is typically extended into individual
8932              * REST Objects (like Dialog, MediaDialog, etc...), and shouldn't be used directly.
8933              *
8934              * @augments finesse.restservices.RestBase
8935              * @constructs
8936              */
8937             _fakeConstuctor: function () {
8938                 /* This is here to hide the real init constructor from the public docs */
8939             },
8940 
8941             /**
8942              * @private
8943              *
8944              * @param {Object} options
8945              *     An object with the following properties:<ul>
8946              *         <li><b>id:</b> The id of the object being constructed</li>
8947              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8948              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8949              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8950              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8951              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8952              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8953              *             <li><b>content:</b> {String} Raw string of response</li>
8954              *             <li><b>object:</b> {Object} Parsed object of response</li>
8955              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8956              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8957              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8958              *             </ul></li>
8959              *         </ul></li>
8960              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8961              **/
8962             init: function (options) {
8963                 this._super(options);
8964             },
8965 
8966             /**
8967              * @private
8968              * Gets the REST class for the current object - this is the Dialog class.
8969              * @returns {Object} The Dialog class.
8970              */
8971             getRestClass: function () {
8972                 throw new Error("getRestClass(): Not implemented in subtype.");
8973             },
8974 
8975             /**
8976              * @private
8977              * The constant for agent device.
8978              */
8979             _agentDeviceType: "AGENT_DEVICE",
8980 
8981             /**
8982              * @private
8983              * Gets the REST type for the current object - this is a "Dialog".
8984              * @returns {String} The Dialog string.
8985              */
8986             getRestType: function () {
8987                 return "Dialog";
8988             },
8989 
8990             /**
8991              * @private
8992              * Override default to indicate that this object doesn't support making
8993              * requests.
8994              */
8995             supportsRequests: false,
8996 
8997             /**
8998              * @private
8999              * Override default to indicate that this object doesn't support subscriptions.
9000              */
9001             supportsSubscriptions: false,
9002 
9003 
9004             /**
9005              * Getter for the media type.
9006              * @returns {String} The media type.
9007              */
9008             getMediaType: function () {
9009                 this.isLoaded();
9010                 return this.getData().mediaType;
9011             },
9012 
9013             /**
9014              * @private
9015              * Getter for the uri.
9016              * @returns {String} The uri.
9017              */
9018             getDialogUri: function () {
9019                 this.isLoaded();
9020                 return this.getData().uri;
9021             },
9022 
9023             /**
9024              * Getter for the callType.
9025              * @deprecated Use getMediaProperties().callType instead.
9026              * @returns {String} The callType.
9027              */
9028             getCallType: function () {
9029                 this.isLoaded();
9030                 return this.getData().mediaProperties.callType;
9031             },
9032 
9033 
9034             /**
9035              * Getter for the Dialog state.
9036              * @returns {String} The Dialog state.
9037              */
9038             getState: function () {
9039                 this.isLoaded();
9040                 return this.getData().state;
9041             },
9042 
9043             /**
9044              * Retrieves a list of participants within the Dialog object.
9045              * @returns {Object} Array list of participants.
9046              * Participant entity properties are as follows:<ul>
9047              *     <li>state - The state of the Participant. 
9048              *     <li>stateCause - The state cause of the Participant.
9049              *     <li>mediaAddress - The media address of the Participant.
9050              *     <li>startTime - The start Time of the Participant.
9051              *     <li>stateChangeTime - The time when participant state has changed.
9052              *     <li>actions - These are the actions that a Participant can perform</ul>
9053              */
9054             getParticipants: function () {
9055                 this.isLoaded();
9056                 var participants = this.getData().participants.Participant;
9057                 //Due to the nature of the XML->JSO converter library, a single
9058                 //element in the XML array will be considered to an object instead of
9059                 //a real array. This will handle those cases to ensure that an array is
9060                 //always returned.
9061 
9062                 return Utilities.getArray(participants);
9063             },
9064 
9065 			/**
9066 			 * Updates this dialog's call variables. This function will not do any call variable validation, caller should take care validations before calling this function.
9067 			 *
9068 			 * @param {Object} call variables to modify. Ex: {"callVariable1": "value1", "callVariable2": "value2"}
9069 			 * @param {finesse.interfaces.RequestHandlers} options
9070 			 *     An object containing the handlers for the request
9071 			 */
9072 			updateCallVariables: function (callvariablesList, options)
9073 			{
9074 				this.isLoaded();
9075 				var mediaProperties = {};
9076 				var callvariables =[];
9077 				for (var callvariable in callvariablesList) {
9078 					if(!callvariablesList.hasOwnProperty(callvariable)) continue;
9079 				   var entry = {"name":callvariable, "value": callvariablesList[callvariable] };
9080 					callvariables.push(entry);
9081 				}
9082 				var callVariableEntry = {"CallVariable": callvariables};
9083 	
9084 				mediaProperties['callvariables'] = callVariableEntry;
9085 				options = options || {};
9086 				options.content = {};
9087 				options.content[this.getRestType()] =
9088 				{
9089 					"mediaProperties": mediaProperties,
9090 					"requestedAction": DialogBase.Actions.UPDATE_CALL_DATA
9091 				};
9092 				options.method = "PUT";
9093 				this.restRequest(this.getRestUrl(), options);
9094 	
9095 				 return this;
9096 			},
9097             /**
9098              * This method retrieves the participant timer counters
9099              *
9100              * @param {String} participantExt Extension of participant.
9101              * @returns {Object} Array of Participants which contains properties :<ul>
9102              *     <li>state - The state of the Participant. 
9103              *     <li>startTime - The start Time of the Participant.
9104              *     <li>stateChangeTime - The time when participant state has changed.</ul>
9105              * 
9106              */
9107             getParticipantTimerCounters : function (participantExt) {
9108                 var part, participantTimerCounters = {}, idx, participants;
9109 
9110                 participants = this.getParticipants();
9111 
9112 
9113                 //Loop through all the participants and find the right participant (based on participantExt)
9114                 for(idx=0;idx<participants.length;idx=idx+1)
9115                 {
9116                     part = participants[idx];
9117 
9118                     if (part.mediaAddress === participantExt)
9119                     {
9120                         participantTimerCounters.startTime= part.startTime;
9121                         participantTimerCounters.stateChangeTime= part.stateChangeTime;
9122                         participantTimerCounters.state= part.state;
9123                         break;
9124                     }
9125                 }
9126 
9127                 return participantTimerCounters;
9128             },
9129 
9130 
9131             /**
9132              * Retrieves a list of media properties from the dialog object.
9133              * @returns {Object} Map of call variables; names mapped to values.
9134              * Variables may include the following:<ul>
9135              * <li>dialedNumber: The number dialed.
9136              * <li>callType: The type of call. Call types include:<ul>
9137              *     <li>ACD_IN
9138              *     <li>PREROUTE_ACD_IN
9139              *     <li>PREROUTE_DIRECT_AGENT
9140              *     <li>TRANSFER
9141              *     <li>OTHER_IN
9142              *     <li>OUT
9143              *     <li>AGENT_INSIDE
9144              *     <li>CONSULT
9145              *     <li>CONFERENCE
9146              *     <li>SUPERVISOR_MONITOR
9147              *     <li>OUTBOUND
9148              *     <li>OUTBOUND_PREVIEW</ul>
9149              * <li>DNIS: The DNIS provided. For routed calls, this is the route point.
9150              * <li>wrapUpReason: A description of the call.
9151              * <li>queueNumber: Number of the agent Skill Group the call is attributed to.
9152              * <li>queueName: Name of the agent Skill Group the call is attributed to.
9153              * <li>callKeyCallId: unique number of the call routed on a particular day.
9154              * <li>callKeyPrefix: represents the day when the call is routed.
9155              * <li>callKeySequenceNum: represents the sequence number of call.
9156              * <li>Call Variables, by name.  The name indicates whether it is a call variable or ECC variable.
9157              * Call variable names start with callVariable#, where # is 1-10. ECC variable names (both scalar and array) are prepended with "user".
9158              * ECC variable arrays include an index enclosed within square brackets located at the end of the ECC array name.
9159              * <li>The following call variables provide additional details about an Outbound Option call:<ul>
9160              *     <li>BACampaign
9161              *     <li>BAAccountNumber
9162              *     <li>BAResponse
9163              *     <li>BAStatus<ul>
9164              *         <li>PREDICTIVE_OUTBOUND: A predictive outbound call.
9165              *         <li>PROGRESSIVE_OUTBOUND: A progressive outbound call.
9166              *         <li>PREVIEW_OUTBOUND_RESERVATION: Agent is reserved for a preview outbound call.
9167              *         <li>PREVIEW_OUTBOUND: Agent is on a preview outbound call.</ul>
9168              *     <li>BADialedListID
9169              *     <li>BATimeZone
9170              *     <li>BABuddyName</ul></ul>
9171              *
9172              */
9173             getMediaProperties: function () {
9174                 var mpData, currentMediaPropertiesMap, thisMediaPropertiesJQuery;
9175 
9176                 this.isLoaded();
9177 
9178                 // We have to convert to jQuery object to do a proper compare
9179                 thisMediaPropertiesJQuery = jQuery(this.getData().mediaProperties);
9180 
9181                 if ((this._lastMediaPropertiesJQuery !== undefined)
9182                     && (this._lastMediaPropertiesMap !== undefined)
9183                     && (this._lastMediaPropertiesJQuery.is(thisMediaPropertiesJQuery))) {
9184 
9185                     return this._lastMediaPropertiesMap;
9186                 }
9187 
9188                 currentMediaPropertiesMap = {};
9189 
9190                 mpData = this.getData().mediaProperties;
9191 
9192                 if (mpData) {
9193                     if (mpData.callvariables && mpData.callvariables.CallVariable) {
9194                         if (mpData.callvariables.CallVariable.length === undefined) {
9195                             mpData.callvariables.CallVariable = [mpData.callvariables.CallVariable];
9196                         }
9197                         jQuery.each(mpData.callvariables.CallVariable, function (i, callVariable) {
9198                             currentMediaPropertiesMap[callVariable.name] = callVariable.value;
9199                         });
9200                     }
9201 
9202                     jQuery.each(mpData, function (key, value) {
9203                         if (key !== 'callvariables') {
9204                             currentMediaPropertiesMap[key] = value;
9205                         }
9206                     });
9207                 }
9208 
9209                 this._lastMediaPropertiesMap = currentMediaPropertiesMap;
9210                 this._lastMediaPropertiesJQuery = thisMediaPropertiesJQuery;
9211 
9212                 return this._lastMediaPropertiesMap;
9213             },
9214 
9215 
9216 
9217             /**
9218              * @private
9219              * Invoke a request to the server given a content body and handlers.
9220              *
9221              * @param {Object} contentBody
9222              *     A JS object containing the body of the action request.
9223              * @param {finesse.interfaces.RequestHandlers} handlers
9224              *     An object containing the handlers for the request
9225              */
9226             _makeRequest: function (contentBody, handlers) {
9227                 // Protect against null dereferencing of options allowing its
9228                 // (nonexistent) keys to be read as undefined
9229                 handlers = handlers || {};
9230 
9231                 this.restRequest(this.getRestUrl(), {
9232                     method: 'PUT',
9233                     success: handlers.success,
9234                     error: handlers.error,
9235                     content: contentBody
9236                 });
9237             }
9238 
9239 		});
9240 		
9241 		DialogBase.Actions = /** @lends finesse.restservices.DialogBase.Actions.prototype */ {
9242 			
9243 			UPDATE_CALL_DATA: "UPDATE_CALL_DATA",
9244             /**
9245              * @class Set of action constants for a Dialog.  
9246              * @constructs
9247              */
9248             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
9249         };
9250 
9251         window.finesse = window.finesse || {};
9252         window.finesse.restservices = window.finesse.restservices || {};
9253         window.finesse.restservices.DialogBase = DialogBase;
9254 
9255 
9256         return DialogBase;
9257     });
9258 
9259 /**
9260  * JavaScript representation of the Finesse Dialog object.
9261  *
9262  * @requires finesse.clientservices.ClientServices
9263  * @requires Class
9264  * @requires finesse.FinesseBase
9265  * @requires finesse.restservices.RestBase
9266  */
9267 
9268 /** @private */
9269 define('restservices/Dialog',[
9270     'restservices/DialogBase',
9271     'utilities/Utilities'
9272 ],
9273 function (DialogBase, Utilities) {
9274     var Dialog = DialogBase.extend(/** @lends finesse.restservices.Dialog.prototype */{
9275 
9276         /**
9277          * @class
9278          * A Dialog is an attempted connection between or among multiple participants,
9279          * for example, a regular phone call, a conference, or a silent monitor session.
9280          * 
9281          * @augments finesse.restservices.DialogBase
9282          * @constructs
9283          */
9284         _fakeConstuctor: function () {
9285             /* This is here to hide the real init constructor from the public docs */
9286         },
9287         
9288         /**
9289          * @private
9290          *
9291          * @param {Object} options
9292          *     An object with the following properties:<ul>
9293          *         <li><b>id:</b> The id of the object being constructed</li>
9294          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9295          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9296          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9297          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9298          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9299          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9300          *             <li><b>content:</b> {String} Raw string of response</li>
9301          *             <li><b>object:</b> {Object} Parsed object of response</li>
9302          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9303          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9304          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9305          *             </ul></li>
9306          *         </ul></li>
9307          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9308          **/
9309         init: function (options) {
9310             this._super(options);
9311         },
9312 
9313         /**
9314          * @private
9315          * Gets the REST class for the current object - this is the Dialog class.
9316          * @returns {Object} The Dialog class.
9317          */
9318         getRestClass: function () {
9319             return Dialog;
9320         },
9321 
9322         /**
9323          * The requestId reaper timeout in ms
9324          */
9325         REQUESTID_REAPER_TIMEOUT: 5000,
9326 
9327         /**
9328          * Getter for the from address.
9329          * @returns {String} The from address.
9330          */
9331         getFromAddress: function () {
9332             this.isLoaded();
9333             return this.getData().fromAddress;
9334         },
9335 
9336         /**
9337          * Getter for the to address.
9338          * @returns {String} The to address.
9339          */
9340         getToAddress: function () {
9341             this.isLoaded();
9342             return this.getData().toAddress;
9343         },
9344 
9345         /**
9346          * Getter for the callback number without prefix.
9347          * This is required to schedule a callback if there is any dialer prefix added for direct_preview outbound calls
9348          * @returns {String} The callback number.undefined if callbackNumber is not available
9349          */
9350         getCallbackNumber: function () {
9351             this.isLoaded();
9352             return this.getData().callbackNumber;
9353         },
9354         
9355         /**
9356          * Getter for the secondaryId of a dialog.
9357          * A CONSULT call has two call legs (primary leg and a consult leg). 
9358          * As the CONSULT call is completed (either with TRANSFER or CONFERENCE), call legs would be merged. 
9359          * The surviving call's Dialog will contain the dropped call's Dialog Id in secondaryId field.
9360          * For CCE deployments, DIRECT_TRANSFER also have the secondaryId populated as mentioned above.  
9361          * @returns {String} The id of the secondary dialog.
9362          * @since   11.6(1)-ES1 onwards
9363          */
9364         getSecondaryId: function () {
9365             this.isLoaded();
9366             return this.getData().secondaryId;
9367         },
9368         
9369        /**
9370          * gets the participant timer counters 
9371          *
9372          * @param {String} participantExt Extension of participant.
9373          * @returns {Object} Array of Participants which contains properties :<ul>
9374          *     <li>state - The state of the Participant. 
9375          *     <li>startTime - The start Time of the Participant.
9376          *     <li>stateChangeTime - The time when participant state has changed.</ul>
9377          */
9378         getParticipantTimerCounters : function (participantExt) {
9379           var part, participantTimerCounters = {}, idx, participants;
9380           
9381           participants = this.getParticipants();
9382 
9383 
9384           //Loop through all the participants and find the right participant (based on participantExt)
9385           for(idx=0;idx<participants.length;idx=idx+1)
9386           {
9387             part = participants[idx];
9388             
9389             if (part.mediaAddress === participantExt)
9390             {
9391                 participantTimerCounters.startTime= part.startTime;
9392                 participantTimerCounters.stateChangeTime= part.stateChangeTime;
9393                 participantTimerCounters.state= part.state;
9394                 break;
9395             }
9396           }
9397           
9398           return participantTimerCounters;
9399         },
9400         
9401         /**
9402          * Determines the droppable participants.  A droppable participant is a participant that is an agent extension.   
9403          * (It is not a CTI Route Point, IVR Port, or the caller)
9404          * 
9405          * @param {String} filterExtension used to remove a single extension from the list
9406          * @returns {Object} Array of Participants that can be dropped.
9407          * Participant entity properties are as follows:<ul>
9408          *     <li>state - The state of the Participant. 
9409          *     <li>stateCause - The state cause of the Participant.
9410          *     <li>mediaAddress - The media address of the Participant.
9411          *     <li>startTime - The start Time of the Participant.
9412          *     <li>stateChangeTime - The time when participant state has changed.
9413          *     <li>actions - These are the actions that a Participant can perform</ul>
9414          */
9415         getDroppableParticipants: function (filterExtension) {
9416           this.isLoaded();
9417           var droppableParticipants = [], participants, index, idx, filterExtensionToRemove = "", callStateOk, part;
9418 
9419           participants = this.getParticipants();
9420 
9421           if (filterExtension)
9422           {
9423             filterExtensionToRemove = filterExtension;
9424           }
9425 
9426           //Loop through all the participants to remove non-agents & remove filterExtension
9427           //We could have removed filterExtension using splice, but we have to iterate through
9428           //the list anyway.
9429           for(idx=0;idx<participants.length;idx=idx+1)
9430           {
9431             part = participants[idx];
9432 
9433             //Skip the filterExtension
9434             if (part.mediaAddress !== filterExtensionToRemove)
9435             {
9436                 callStateOk = this._isParticipantStateDroppable(part);
9437 
9438                 //Remove non-agents & make sure callstate 
9439                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
9440                 {
9441                   droppableParticipants.push(part);
9442                 }
9443             }
9444         }
9445 
9446         return Utilities.getArray(droppableParticipants);
9447         },
9448 
9449         _isParticipantStateDroppable : function (part)
9450         {
9451           var isParticipantStateDroppable = false;
9452           if (part.state === Dialog.ParticipantStates.ACTIVE || part.state === Dialog.ParticipantStates.ACCEPTED || part.state === Dialog.ParticipantStates.HELD)
9453           {
9454             isParticipantStateDroppable = true;
9455           }
9456           
9457           return isParticipantStateDroppable;
9458         },
9459         
9460         /**
9461          * Is the participant droppable
9462          *
9463          * @param {String} participantExt Extension of participant.
9464          * @returns {Boolean} True is droppable.
9465          */
9466         isParticipantDroppable : function (participantExt) {
9467           var droppableParticipants = null, isDroppable = false, idx, part, callStateOk;
9468           
9469           droppableParticipants = this.getDroppableParticipants();
9470           
9471           if (droppableParticipants) 
9472           {
9473             for(idx=0;idx<droppableParticipants.length;idx=idx+1)
9474             {
9475               part = droppableParticipants[idx];
9476              
9477               if (part.mediaAddress === participantExt)
9478               {
9479                 callStateOk = this._isParticipantStateDroppable(part);
9480 
9481                 //Remove non-agents & make sure callstate 
9482                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
9483                 {
9484                   isDroppable = true;
9485                   break;
9486                 }
9487               }
9488             }
9489           }
9490           
9491           return isDroppable;
9492         },
9493 
9494         /**
9495          * Retrieves information about the currently scheduled callback, if any.
9496          * @returns {Object} If no callback has been set, will return undefined. If 
9497          * a callback has been set, it will return a map with one or more of the 
9498          * following entries, depending on what values have been set. 
9499          *    callbackTime   - the callback time, if it has been set.
9500          *    callbackNumber - the callback number, if it has been set.
9501          */
9502         getCallbackInfo: function() {
9503             this.isLoaded();
9504             return this.getData().scheduledCallbackInfo;
9505         },
9506 
9507         /**
9508          * Invoke a consult call out to a destination.
9509          *
9510          * @param {String} mediaAddress
9511          *     The media address of the user performing the consult call.
9512          * @param {String} toAddress
9513          *     The destination address of the consult call.
9514          * @param {finesse.interfaces.RequestHandlers} handlers
9515          *     An object containing the handlers for the request
9516          */
9517         makeConsultCall: function (mediaAddress, toAddress, handlers) {
9518             this.isLoaded();
9519             var contentBody = {};
9520             contentBody[this.getRestType()] = {
9521                 "targetMediaAddress": mediaAddress,
9522                 "toAddress": toAddress,
9523                 "requestedAction": Dialog.Actions.CONSULT_CALL
9524             };
9525             this._makeRequest(contentBody, handlers);
9526             return this; // Allow cascading
9527         },
9528         
9529         /**
9530          * Invoke a single step transfer request.
9531          *
9532          * @param {String} mediaAddress
9533          *     The media address of the user performing the single step transfer.
9534          * @param {String} toAddress
9535          *     The destination address of the single step transfer.
9536          * @param {finesse.interfaces.RequestHandlers} handlers
9537          *     An object containing the handlers for the request
9538          */
9539         initiateDirectTransfer: function (mediaAddress, toAddress, handlers) {
9540             this.isLoaded();
9541             var contentBody = {};
9542             contentBody[this.getRestType()] = {
9543                 "targetMediaAddress": mediaAddress,
9544                 "toAddress": toAddress,
9545                 "requestedAction": Dialog.Actions.TRANSFER_SST
9546             };
9547             this._makeRequest(contentBody, handlers);
9548             return this; // Allow cascading
9549         },
9550 
9551         /**
9552          * Update this dialog's wrap-up reason.
9553          *
9554          * @param {String} wrapUpReason
9555          *     The new wrap-up reason for this dialog
9556          * @param {finesse.interfaces.RequestHandlers} handlers
9557          *     An object containing the handlers for the request
9558          */
9559         updateWrapUpReason: function (wrapUpItems, options)
9560         {
9561 			this.isLoaded();
9562 			var mediaProperties = {};
9563 			if (window.finesse.container.Config.deploymentType === 'UCCX') {
9564 				mediaProperties = {
9565 					"wrapUpItems": {wrapUpItem: wrapUpItems}
9566 				} ;
9567 			} else {
9568 				mediaProperties = {
9569 					"wrapUpReason": wrapUpItems
9570 				 };
9571 			}
9572 
9573             options = options || {};
9574             options.content = {};
9575             options.content[this.getRestType()] =
9576             {
9577                 "mediaProperties": mediaProperties,
9578                 "requestedAction": Dialog.Actions.UPDATE_CALL_DATA
9579             };
9580             options.method = "PUT";
9581             this.restRequest(this.getRestUrl(), options);
9582 
9583             return this;
9584         },
9585 
9586         /**
9587          * Invoke a request to server based on the action given.
9588          * @param {String} mediaAddress
9589          *     The media address of the user performing the action.
9590          * @param {finesse.restservices.Dialog.Actions} action
9591          *     The action string indicating the action to invoke on dialog.
9592          * @param {finesse.interfaces.RequestHandlers} handlers
9593          *     An object containing the handlers for the request
9594          */
9595         requestAction: function (mediaAddress, action, handlers) {
9596             this.isLoaded();
9597             var contentBody = {};
9598             contentBody[this.getRestType()] = {
9599                 "targetMediaAddress": mediaAddress,
9600                 "requestedAction": action
9601             };
9602             this._makeRequest(contentBody, handlers);
9603             return this; // Allow cascading
9604         },
9605         
9606         /**
9607          * Wrapper around "requestAction" to request PARTICIPANT_DROP action.
9608          *
9609          * @param targetMediaAddress is the address to drop
9610          * @param {finesse.interfaces.RequestHandlers} handlers
9611          *     An object containing the handlers for the request
9612          */
9613         dropParticipant: function (targetMediaAddress, handlers) {
9614             this.requestAction(targetMediaAddress, Dialog.Actions.PARTICIPANT_DROP, handlers);
9615         },
9616         
9617         /**
9618          * Invoke a request to server to send DTMF digit tones.
9619          * @param {String} mediaAddress
9620          * @param {finesse.interfaces.RequestHandlers} handlers
9621          *     An object containing the handlers for the request
9622          * @param {String} digit
9623          *     The digit which causes invocation of an action on the dialog.
9624          */
9625         sendDTMFRequest: function (mediaAddress, handlers, digit) {
9626             this.isLoaded();
9627             var contentBody = {};
9628             contentBody[this.getRestType()] = {
9629                 "targetMediaAddress": mediaAddress,
9630                 "requestedAction": "SEND_DTMF",
9631                 "actionParams": {
9632                     "ActionParam": {
9633                         "name": "dtmfString",
9634                         "value": digit
9635                     }
9636                 }
9637             };
9638             this._makeRequest(contentBody, handlers);
9639             return this; // Allow cascading
9640         },
9641 
9642         /**
9643          * Invoke a request to server to set the time for a callback.
9644          * @param {String} mediaAddress
9645          * @param {String} callbackTime 
9646          *     The requested time for the callback, in YYYY-MM-DDTHH:MM format
9647          *     (ex: 2013-12-24T23:59)
9648          * @param {finesse.interfaces.RequestHandlers} handlers
9649          *     An object containing the handlers for the request
9650          */
9651         updateCallbackTime: function (mediaAddress, callbackTime, handlers) {
9652             this.isLoaded();
9653             var contentBody = {};
9654             contentBody[this.getRestType()] = {
9655                 "targetMediaAddress": mediaAddress,
9656                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
9657                 "actionParams": {
9658                     "ActionParam": {
9659                         "name": "callbackTime",
9660                         "value": callbackTime
9661                     }
9662                 }
9663             };
9664             this._makeRequest(contentBody, handlers);
9665             return this; // Allow cascading
9666         },
9667 
9668         /**
9669          * Invoke a request to server to set the number for a callback.
9670          * @param {String} mediaAddress
9671          * @param {String} callbackNumber
9672          *     The requested number to call for the callback
9673          * @param {finesse.interfaces.RequestHandlers} handlers
9674          *     An object containing the handlers for the request
9675          */
9676         updateCallbackNumber: function (mediaAddress, callbackNumber, handlers) {
9677             this.isLoaded();
9678             var contentBody = {};
9679             contentBody[this.getRestType()] = {
9680                 "targetMediaAddress": mediaAddress,
9681                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
9682                 "actionParams": {
9683                     "ActionParam": {
9684                         "name": "callbackNumber",
9685                         "value": callbackNumber
9686                     }
9687                 }
9688             };
9689             this._makeRequest(contentBody, handlers);
9690             return this; // Allow cascading
9691         },
9692 
9693         /**
9694          * Invoke a request to server to cancel a callback.
9695          * @param {String} mediaAddress
9696          * @param {finesse.interfaces.RequestHandlers} handlers
9697          *     An object containing the handlers for the request
9698          */
9699         cancelCallback: function (mediaAddress, handlers) {
9700             this.isLoaded();
9701             var contentBody = {};
9702             contentBody[this.getRestType()] = {
9703                 "targetMediaAddress": mediaAddress,
9704                 "requestedAction": Dialog.Actions.CANCEL_SCHEDULED_CALLBACK
9705             };
9706             this._makeRequest(contentBody, handlers);
9707             return this; // Allow cascading
9708         },
9709 
9710         /**
9711          * Invoke a request to server to reclassify the call type.
9712          * @param {String} mediaAddress
9713          *     The media address of the user performing the consult call.
9714          * @param {String} classification
9715          *     The classification to assign to the call. Valid values are "VOICE", "FAX",
9716          *     "ANS_MACHINE", "INVALID", "BUSY" (CCX only), and "DO_NOT_CALL".
9717          * @param {finesse.interfaces.RequestHandlers} handlers
9718          *     An object containing the handlers for the request
9719          */
9720         reclassifyCall: function (mediaAddress, classification, handlers) {
9721             this.isLoaded();
9722             var contentBody = {};
9723             contentBody[this.getRestType()] = {
9724                 "targetMediaAddress": mediaAddress,
9725                 "requestedAction": Dialog.Actions.RECLASSIFY,
9726                 "actionParams": {
9727                     "ActionParam": {
9728                         "name": "outboundClassification",
9729                         "value": classification
9730                     }
9731                 }
9732             };
9733             this._makeRequest(contentBody, handlers);
9734             return this; // Allow cascading
9735         },
9736 
9737         /**
9738          * Utility method to create a closure containing the requestId and the Dialogs object so 
9739          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
9740          * @param  {String} requestId The requestId of the event
9741          * @return {Function}           The function to be executed by setTimeout
9742          */
9743         _createRequestIdReaper: function (requestId) {
9744             var that = this;
9745             return function () {
9746                 that._logger.log("Dialog: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
9747                 delete that._pendingCallbacks[requestId];
9748             };
9749         },
9750 
9751         /**
9752          * Overriding implementation of the one in RestBase.js
9753          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
9754          * @param  {String} requestId The requestId of the event
9755          */
9756         _postProcessUpdateStrategy: function (requestId) {
9757             this._logger.log("Dialog: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
9758             var callbacksObj = this._pendingCallbacks[requestId];
9759             if (callbacksObj && !callbacksObj.used) {
9760                 this._logger.log("Dialog: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
9761                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
9762                 callbacksObj.used = true;
9763             }            
9764         }
9765 
9766     });
9767 
9768     Dialog.Actions = /** @lends finesse.restservices.Dialog.Actions.prototype */ {
9769             /**
9770              * Drops the Participant from the Dialog.
9771              */
9772             DROP: "DROP",
9773             /**
9774              * Answers a Dialog.
9775              */
9776             ANSWER: "ANSWER",
9777             /**
9778              * Holds the Dialog.
9779              */
9780             HOLD: "HOLD",
9781             /**
9782              * Barges into a Call Dialog.
9783              */
9784             BARGE_CALL: "BARGE_CALL",
9785             /**
9786              * Allow as Supervisor to Drop a Participant from the Dialog.
9787              */
9788             PARTICIPANT_DROP: "PARTICIPANT_DROP",
9789             /**
9790              * Makes a new Call Dialog.
9791              */
9792             MAKE_CALL: "MAKE_CALL",
9793             /**
9794              * Retrieves a Dialog that is on Hold.
9795              */
9796             RETRIEVE: "RETRIEVE",
9797             /**
9798              * Sets the time or number for a callback. Can be
9799              * either a new callback, or updating an existing one.
9800              */
9801             UPDATE_SCHEDULED_CALLBACK: "UPDATE_SCHEDULED_CALLBACK",
9802             /**
9803              * Cancels a callback.
9804              */
9805             CANCEL_SCHEDULED_CALLBACK: "CANCEL_SCHEDULED_CALLBACK",
9806             /**
9807              * Initiates a Consult Call.
9808              */
9809             CONSULT_CALL: "CONSULT_CALL",
9810             /**
9811              * Initiates a Transfer of a Dialog.
9812              */
9813             TRANSFER: "TRANSFER",
9814             /**
9815              * Initiates a Single-Step Transfer of a Dialog.
9816              */
9817             TRANSFER_SST: "TRANSFER_SST",
9818             /**
9819              * Initiates a Conference of a Dialog.
9820              */
9821             CONFERENCE: "CONFERENCE",
9822             /**
9823              * Changes classification for a call
9824              */
9825             RECLASSIFY: "RECLASSIFY", 
9826             /**
9827              * Updates data on a Call Dialog.
9828              */
9829             UPDATE_CALL_DATA: "UPDATE_CALL_DATA",
9830             /**
9831              * Initiates a Recording on a Call Dialog.
9832              */
9833             START_RECORDING : "START_RECORDING",
9834             /**
9835              * Sends DTMF (dialed digits) to a Call Dialog.
9836              */
9837             DTMF : "SEND_DTMF",            
9838             /**
9839              * Accepts a Dialog that is being Previewed.
9840              */
9841             ACCEPT: "ACCEPT",
9842             /**
9843              * Rejects a Dialog.
9844              */
9845             REJECT: "REJECT",
9846             /**
9847              * Closes a Dialog.
9848              */
9849             CLOSE : "CLOSE",
9850             /**
9851              * @class Set of action constants for a Dialog.  These should be used for
9852              * {@link finesse.restservices.Dialog#requestAction}.
9853              * @constructs
9854              */
9855             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
9856         };
9857 
9858     Dialog.States = /** @lends finesse.restservices.Dialog.States.prototype */ {
9859        /**
9860          * Indicates that the call is ringing at a device.
9861          */
9862         ALERTING: "ALERTING",
9863         /**
9864          * Indicates that the phone is off the hook at a device.
9865          */
9866         INITIATING: "INITIATING",
9867         /**
9868          * Indicates that the dialog has a least one active participant.
9869          */
9870         ACTIVE: "ACTIVE",
9871         /**
9872          * Indicates that the dialog has no active participants.
9873          */
9874         DROPPED: "DROPPED",
9875         /**
9876          * Indicates that the phone is dialing at the device.
9877          */
9878         INITIATED: "INITIATED",
9879         /**
9880          * Indicates that the dialog has failed.
9881          * @see Dialog.ReasonStates
9882          */
9883         FAILED: "FAILED",
9884         /**
9885          * Indicates that the user has accepted an OUTBOUND_PREVIEW dialog.
9886          */
9887         ACCEPTED: "ACCEPTED",
9888         /**
9889          * @class Possible Dialog State constants.
9890          * The State flow of a typical in-bound Dialog is as follows: INITIATING, INITIATED, ALERTING, ACTIVE, DROPPED.
9891          * @constructs
9892          */
9893         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
9894     };
9895 
9896     Dialog.ParticipantStates = /** @lends finesse.restservices.Dialog.ParticipantStates.prototype */ {
9897         /**
9898           * Indicates that an incoming call is ringing on the device.
9899           */
9900          ALERTING: "ALERTING",
9901          /**
9902           * Indicates that an outgoing call, not yet active, exists on the device.
9903           */
9904          INITIATING: "INITIATING",
9905          /**
9906           * Indicates that the participant is active on the call.
9907           */
9908          ACTIVE: "ACTIVE",
9909          /**
9910           * Indicates that the participant has dropped from the call.
9911           */
9912          DROPPED: "DROPPED",
9913          /**
9914           * Indicates that the participant has held their connection to the call.
9915           */
9916          HELD: "HELD",
9917          /**
9918           * Indicates that the phone is dialing at a device.
9919           */
9920          INITIATED: "INITIATED",
9921          /**
9922           * Indicates that the call failed.
9923           * @see Dialog.ReasonStates
9924           */
9925          FAILED: "FAILED",
9926          /**
9927           * Indicates that the participant is not in active state on the call, but is wrapping up after the participant has dropped from the call.
9928           */
9929          WRAP_UP: "WRAP_UP",
9930          /**
9931           * Indicates that the participant has accepted the dialog.  This state is applicable to OUTBOUND_PREVIEW dialogs.
9932           */
9933          ACCEPTED: "ACCEPTED",
9934          /**
9935           * @class Possible Dialog Participant State constants.
9936           * @constructs
9937           */
9938          _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
9939      };
9940 
9941     Dialog.ReasonStates = /** @lends finesse.restservices.Dialog.ReasonStates.prototype */ {
9942        /**
9943         * Dialog was Busy.  This will typically be for a Failed Dialog.
9944         */
9945         BUSY: "BUSY",
9946         /**
9947          * Dialog reached a Bad Destination.  This will typically be for a Failed Dialog.
9948          */
9949         BAD_DESTINATION: "BAD_DESTINATION",
9950         /**
9951          * All Other Reasons.  This will typically be for a Failed Dialog.
9952          */
9953         OTHER: "OTHER",
9954         /**
9955          * The Device Resource for the Dialog was not available.
9956          */
9957         DEVICE_RESOURCE_NOT_AVAILABLE : "DEVICE_RESOURCE_NOT_AVAILABLE",
9958         /**
9959          * @class Possible dialog state reasons code constants.
9960              * @constructs
9961              */
9962             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
9963     };
9964 
9965     window.finesse = window.finesse || {};
9966     window.finesse.restservices = window.finesse.restservices || {};
9967     window.finesse.restservices.Dialog = Dialog;
9968     
9969     
9970     return Dialog;
9971 });
9972 
9973 /**
9974  * JavaScript representation of the Finesse Dialogs collection
9975  * object which contains a list of Dialog objects.
9976  *
9977  * @requires finesse.clientservices.ClientServices
9978  * @requires Class
9979  * @requires finesse.FinesseBase
9980  * @requires finesse.restservices.RestBase
9981  * @requires finesse.restservices.Dialog
9982  */
9983 /** @private */
9984 define('restservices/Dialogs',[
9985     'restservices/RestCollectionBase',
9986     'restservices/Dialog'
9987 ],
9988 function (RestCollectionBase, Dialog) {
9989     var Dialogs = RestCollectionBase.extend(/** @lends finesse.restservices.Dialogs.prototype */{
9990     	
9991     	/**
9992     	 * if includeNonVoice is true then voice and non-voice both the dialogs will be loaded
9993     	 */
9994     	includeNonVoice: false,
9995         /**
9996          * @class
9997          * JavaScript representation of a Dialogs collection object. Also exposes
9998          * methods to operate on the object against the server.
9999          * @augments finesse.restservices.RestCollectionBase
10000          * @constructs
10001          * @see finesse.restservices.Dialog
10002          * @example
10003          *  _dialogs = _user.getDialogs( {
10004          *      onCollectionAdd : _handleDialogAdd,
10005          *      onCollectionDelete : _handleDialogDelete,
10006          *      onLoad : _handleDialogsLoaded
10007          *  });
10008          *  
10009          * _dialogCollection = _dialogs.getCollection();
10010          * for (var dialogId in _dialogCollection) {
10011          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
10012          *         _dialog = _dialogCollection[dialogId];
10013          *         etc...
10014          *     }
10015          * }
10016          */
10017         _fakeConstuctor: function () {
10018             /* This is here to hide the real init constructor from the public docs */
10019         },
10020         
10021         /**
10022          * @private
10023          * @param {Object} options
10024          *     An object with the following properties:<ul>
10025          *         <li><b>id:</b> The id of the object being constructed</li>
10026          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10027          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10028          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10029          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10030          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10031          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10032          *             <li><b>content:</b> {String} Raw string of response</li>
10033          *             <li><b>object:</b> {Object} Parsed object of response</li>
10034          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10035          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10036          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10037          *             </ul></li>
10038          *         </ul></li>
10039          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10040          **/
10041         init: function (options) {
10042         	this.includeNonVoice = options.includeNonVoice;
10043             this._super(options);
10044         },
10045 
10046         getRestUrlAdditionalParameters: function () {
10047         	//if includeNonVoice is true then include query parameters for voice and non-voice both
10048         	return this.includeNonVoice ? "type=voice&type=non-voice&" : "";
10049         },
10050         /**
10051          * @private
10052          * Gets the REST class for the current object - this is the Dialogs class.
10053          */
10054         getRestClass: function () {
10055             return Dialogs;
10056         },
10057 
10058         /**
10059          * @private
10060          * Gets the REST class for the objects that make up the collection. - this
10061          * is the Dialog class.
10062          */
10063         getRestItemClass: function () {
10064             return Dialog;
10065         },
10066 
10067         /**
10068          * @private
10069          * Gets the REST type for the current object - this is a "Dialogs".
10070          */
10071         getRestType: function () {
10072             return "Dialogs";
10073         },
10074 
10075         /**
10076          * @private
10077          * Gets the REST type for the objects that make up the collection - this is "Dialogs".
10078          */
10079         getRestItemType: function () {
10080             return "Dialog";
10081         },
10082 
10083         /**
10084          * @private
10085          * Override default to indicates that the collection doesn't support making
10086          * requests.
10087          */
10088         supportsRequests: true,
10089 
10090         /**
10091          * @private
10092          * Override default to indicates that the collection subscribes to its objects.
10093          */
10094         supportsRestItemSubscriptions: true,
10095 
10096         /**
10097          * The requestId reaper timeout in ms
10098          */
10099         REQUESTID_REAPER_TIMEOUT: 5000,
10100 
10101         /**
10102          * @private
10103          * Create a new Dialog in this collection
10104          *
10105          * @param {String} toAddress
10106          *     The to address of the new Dialog
10107          * @param {String} fromAddress
10108          *     The from address of the new Dialog
10109          * @param {finesse.interfaces.RequestHandlers} handlers
10110          *     An object containing the (optional) handlers for the request.
10111          * @return {finesse.restservices.Dialogs}
10112          *     This Dialogs object, to allow cascading.
10113          */
10114         createNewCallDialog: function (toAddress, fromAddress, handlers)
10115         {
10116             var contentBody = {};
10117             contentBody[this.getRestItemType()] = {
10118                 "requestedAction": "MAKE_CALL",
10119                 "toAddress": toAddress,
10120                 "fromAddress": fromAddress
10121             };
10122 
10123             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10124             handlers = handlers || {};
10125 
10126             this.restRequest(this.getRestUrl(), {
10127                 method: 'POST',
10128                 success: handlers.success,
10129                 error: handlers.error,
10130                 content: contentBody
10131             });
10132             return this; // Allow cascading
10133         },
10134 
10135         /**
10136          * @private
10137          * Create a new Dialog in this collection as a result of a requested action
10138          *
10139          * @param {String} toAddress
10140          *     The to address of the new Dialog
10141          * @param {String} fromAddress
10142          *     The from address of the new Dialog
10143          * @param {finesse.restservices.Dialog.Actions} actionType
10144          *     The associated action to request for creating this new dialog
10145          * @param {finesse.interfaces.RequestHandlers} handlers
10146          *     An object containing the (optional) handlers for the request.
10147          * @return {finesse.restservices.Dialogs}
10148          *     This Dialogs object, to allow cascading.
10149          */
10150         createNewSuperviseCallDialog: function (toAddress, fromAddress, actionType, handlers)
10151         {
10152             var contentBody = {};
10153             this._isLoaded = true;
10154 
10155             contentBody[this.getRestItemType()] = {
10156                 "requestedAction": actionType,
10157                 "toAddress": toAddress,
10158                 "fromAddress": fromAddress
10159             };
10160 
10161             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10162             handlers = handlers || {};
10163 
10164             this.restRequest(this.getRestUrl(), {
10165                 method: 'POST',
10166                 success: handlers.success,
10167                 error: handlers.error,
10168                 content: contentBody
10169             });
10170             return this; // Allow cascading
10171         },
10172         
10173         /**
10174          * @private
10175          * Create a new Dialog in this collection as a result of a requested action
10176          * @param {String} fromAddress
10177          *     The from address of the new Dialog
10178          * @param {String} toAddress
10179          *     The to address of the new Dialog
10180          * @param {finesse.restservices.Dialog.Actions} actionType
10181          *     The associated action to request for creating this new dialog
10182          * @param {String} dialogUri
10183          *     The associated uri of SUPERVISOR_MONITOR call
10184          * @param {finesse.interfaces.RequestHandlers} handlers
10185          *     An object containing the (optional) handlers for the request.
10186          * @return {finesse.restservices.Dialogs}
10187          *     This Dialogs object, to allow cascading.
10188          */
10189         createNewBargeCall: function (fromAddress, toAddress, actionType, dialogURI, handlers) {
10190             this.isLoaded();
10191          
10192             var contentBody = {};
10193             contentBody[this.getRestItemType()] = {
10194                 "fromAddress": fromAddress,
10195                 "toAddress": toAddress,
10196                 "requestedAction": actionType,
10197                 "associatedDialogUri": dialogURI
10198                 
10199             };
10200             // (nonexistent) keys to be read as undefined
10201             handlers = handlers || {};  
10202             this.restRequest(this.getRestUrl(), {
10203                 method: 'POST',
10204                 success: handlers.success,
10205                 error: handlers.error,
10206                 content: contentBody
10207             });
10208             return this; // Allow cascading
10209         },
10210 
10211         /**
10212          * Utility method to get the number of dialogs in this collection.
10213          * 'excludeSilentMonitor' flag is provided as an option to exclude calls with type
10214          * 'SUPERVISOR_MONITOR' from the count.
10215          * @param  {Boolean} excludeSilentMonitor If true, calls with type of 'SUPERVISOR_MONITOR' will be excluded from the count.
10216          * @return {Number} The number of dialogs in this collection.
10217          */
10218         getDialogCount: function (excludeSilentMonitor) {
10219             this.isLoaded();
10220 
10221             var dialogId, count = 0;
10222             if (excludeSilentMonitor) {
10223                 for (dialogId in this._collection) {
10224                     if (this._collection.hasOwnProperty(dialogId)) {
10225                         if (this._collection[dialogId].getCallType() !== 'SUPERVISOR_MONITOR') {
10226                             count += 1;
10227                         }
10228                     }
10229                 }
10230 
10231                 return count;
10232             } else {
10233                 return this.length;
10234             }        
10235         },
10236 
10237         /**
10238          * Utility method to create a closure containing the requestId and the Dialogs object so 
10239          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
10240          * @param  {String} requestId The requestId of the event
10241          * @return {Function}           The function to be executed by setTimeout
10242          */
10243         _createRequestIdReaper: function (requestId) {
10244             var that = this;
10245             return function () {
10246                 that._logger.log("Dialogs: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
10247                 delete that._pendingCallbacks[requestId];
10248             };
10249         },
10250 
10251         /**
10252          * Overriding implementation of the one in RestBase.js
10253          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
10254          * @param  {String} requestId The requestId of the event
10255          */
10256         _postProcessUpdateStrategy: function (requestId) {
10257             this._logger.log("Dialogs: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
10258             var callbacksObj = this._pendingCallbacks[requestId];
10259             if (callbacksObj && !callbacksObj.used) {
10260                 this._logger.log("Dialogs: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
10261                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
10262                 callbacksObj.used = true;
10263             }            
10264         }
10265 
10266     });
10267     
10268     window.finesse = window.finesse || {};
10269     window.finesse.restservices = window.finesse.restservices || {};
10270     window.finesse.restservices.Dialogs = Dialogs;
10271     
10272     return Dialogs;
10273 });
10274 
10275 /**
10276  * JavaScript representation of the Finesse ClientLog object
10277  *
10278  * @requires finesse.clientservices.ClientServices
10279  * @requires Class
10280  * @requires finesse.FinesseBase
10281  * @requires finesse.restservices.RestBase
10282  */
10283 
10284 /** The following comment is to prevent jslint errors about 
10285  * using variables before they are defined.
10286  */
10287 /** @private */
10288 /*global finesse*/
10289 
10290 define('restservices/ClientLog',["restservices/RestBase"], function (RestBase) {
10291     
10292     var ClientLog = RestBase.extend(/** @lends finesse.restservices.ClientLog.prototype */{    
10293         /**
10294          * @private
10295          * Returns whether this object supports transport logs
10296          */
10297         doNotLog : true,
10298         
10299         explicitSubscription : true,
10300         
10301         /**
10302          * @class
10303          * @private
10304          * JavaScript representation of a ClientLog object. Also exposes methods to operate
10305          * on the object against the server.
10306          *
10307          * @param {Object} options
10308          *     An object with the following properties:<ul>
10309          *         <li><b>id:</b> The id of the object being constructed</li>
10310          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10311          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10312          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10313          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10314          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10315          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10316          *             <li><b>content:</b> {String} Raw string of response</li>
10317          *             <li><b>object:</b> {Object} Parsed object of response</li>
10318          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10319          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10320          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10321          *             </ul></li>
10322          *         </ul></li>
10323          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10324          * @constructs
10325          * @augments finesse.restservices.RestBase
10326          **/
10327         init: function (options) {
10328             this._super({
10329                 id: "",
10330                 data: {clientLog : null},
10331                 onAdd: options.onAdd,
10332                 onChange: options.onChange,
10333                 onLoad: options.onLoad,
10334                 onError: options.onError,
10335                 onDelete: options.onDelete,
10336                 parentObj: options.parentObj
10337                 });
10338         },
10339 
10340         /**
10341          * @private
10342          * Gets the REST class for the current object - this is the ClientLog object.
10343          */
10344         getRestClass: function () {
10345             return ClientLog;
10346         },
10347 
10348         /**
10349          * @private
10350          * Gets the REST type for the current object - this is a "ClientLog".
10351          */
10352         getRestType: function ()
10353         {
10354             return "ClientLog";
10355         },
10356         
10357         /**
10358          * @private
10359          * Gets the node path for the current object
10360          * @returns {String} The node path
10361          */
10362         getXMPPNodePath: function () {
10363             return this.getRestUrl();
10364         },
10365 
10366         /**
10367          * @private
10368          * Utility method to fetch this object from the server, however we
10369          * override it for ClientLog to not do anything because GET is not supported
10370          * for ClientLog object.
10371          */
10372         _doGET: function (handlers) {
10373             return;
10374         },
10375            
10376         /**
10377          * @private
10378          * Invoke a request to the server given a content body and handlers.
10379          *
10380          * @param {Object} contentBody
10381          *     A JS object containing the body of the action request.
10382          * @param {Object} handlers
10383          *     An object containing the following (optional) handlers for the request:<ul>
10384          *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
10385          *         response object as its only parameter:<ul>
10386          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10387          *             <li><b>content:</b> {String} Raw string of response</li>
10388          *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
10389          *         <li>A error callback function for an unsuccessful request to be invoked with the
10390          *         error response object as its only parameter:<ul>
10391          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10392          *             <li><b>content:</b> {String} Raw string of response</li>
10393          *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
10394          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10395          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10396          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10397          *             </ul></li>
10398          *         </ul>
10399          */
10400 		sendLogs: function (contentBody, handlers) {
10401             // Protect against null dereferencing of options allowing its
10402             // (nonexistent) keys to be read as undefined
10403             handlers = handlers || {};
10404 
10405 			if (handlers && handlers.isZip) {
10406                 var postdata = new FormData();
10407 				postdata.append("file", contentBody);
10408 			    this.restRequest(this.getRestUrl().replace('ClientLog', 'CompressedClientLog'), {
10409                 method: 'POST',
10410                 success: handlers.success,
10411                 error: handlers.error,
10412                 content: postdata,
10413                 contentType: 'multipart'
10414             });
10415 			} else {
10416 			    this.restRequest(this.getRestUrl(), {
10417                 method: 'POST',
10418                 //success: handlers.success,
10419                 error: handlers.error,
10420                 content: contentBody
10421             });
10422 			}
10423 
10424         }
10425     });
10426     
10427     window.finesse = window.finesse || {};
10428     window.finesse.restservices = window.finesse.restservices || {};
10429     window.finesse.restservices.ClientLog = ClientLog;
10430     
10431     return ClientLog;
10432 });
10433 
10434 /**
10435  * JavaScript representation of the Finesse Queue object
10436  * @requires finesse.clientservices.ClientServices
10437  * @requires Class
10438  * @requires finesse.FinesseBase
10439  * @requires finesse.restservices.RestBase
10440  */
10441 
10442 /** @private */
10443 define('restservices/Queue',[
10444     'restservices/RestBase',
10445     'utilities/Utilities'
10446 ],
10447 function (RestBase, Utilities) {
10448     var Queue = RestBase.extend(/** @lends finesse.restservices.Queue.prototype */{
10449 
10450         /**
10451          * @class
10452          * The Queue object represents a queue with uri, queue name, and statistics for that queue.
10453          * 
10454          * @augments finesse.restservices.RestBase
10455          * @constructs
10456          */
10457         _fakeConstuctor: function () {
10458             /* This is here to hide the real init constructor from the public docs */
10459         },
10460         
10461 		/**
10462 		 * @private
10463 		 * JavaScript representation of a Queue object. Also exposes methods to operate
10464 		 * on the object against the server.
10465 		 *
10466 		 * @constructor
10467 		 * @param {String} id
10468 		 *     Not required...
10469 		 * @param {Object} callbacks
10470 		 *     An object containing callbacks for instantiation and runtime
10471 		 * @param {Function} callbacks.onLoad(this)
10472 		 *     Callback to invoke upon successful instantiation
10473 		 * @param {Function} callbacks.onLoadError(rsp)
10474 		 *     Callback to invoke on instantiation REST request error
10475 		 *     as passed by finesse.clientservices.ClientServices.ajax()
10476 		 *     {
10477 		 *         status: {Number} The HTTP status code returned
10478 		 *         content: {String} Raw string of response
10479 		 *         object: {Object} Parsed object of response
10480 		 *         error: {Object} Wrapped exception that was caught
10481 		 *         error.errorType: {String} Type of error that was caught
10482 		 *         error.errorMessage: {String} Message associated with error
10483 		 *     }
10484 		 * @param {Function} callbacks.onChange(this)
10485 		 *     Callback to invoke upon successful update
10486 		 * @param {Function} callbacks.onError(rsp)
10487 		 *     Callback to invoke on update error (refresh or event)
10488 		 *     as passed by finesse.clientservices.ClientServices.ajax()
10489 		 *     {
10490 		 *         status: {Number} The HTTP status code returned
10491 		 *         content: {String} Raw string of response
10492 		 *         object: {Object} Parsed object of response
10493 		 *         error: {Object} Wrapped exception that was caught
10494 		 *         error.errorType: {String} Type of error that was caught
10495 		 *         error.errorMessage: {String} Message associated with error
10496 		 *     }
10497 		 *  
10498 		 */
10499         init: function (id, callbacks, restObj) {
10500             this._super(id, callbacks, restObj);
10501         },
10502 
10503         /**
10504          * @private
10505          * Gets the REST class for the current object - this is the Queue object.
10506          * @returns {Object} The Queue class.
10507          */
10508         getRestClass: function () {
10509             return Queue;
10510         },
10511 
10512         /**
10513          * @private
10514          * Gets the REST type for the current object - this is a "Queue".
10515          * @returns {String} The Queue string.
10516          */
10517         getRestType: function () {
10518             return "Queue";
10519         },
10520 
10521         /**
10522          * @private
10523          * Returns whether this object supports subscriptions
10524          * @returns {Boolean}
10525          */
10526         supportsSubscriptions: function () {
10527             return true;
10528         },
10529         
10530         /**
10531          * @private
10532          * Specifies whether this object's subscriptions need to be explicitly requested
10533          */
10534         explicitSubscription: true,
10535         
10536         /**
10537          * @private
10538          * Gets the node path for the current object - this is the team Users node
10539          * @returns {String} The node path
10540          */
10541         getXMPPNodePath: function () {
10542             return this.getRestUrl();
10543         },
10544         
10545         /**
10546          * Getter for the queue id
10547          * @returns {String}
10548          *     The id of the Queue
10549          */
10550         getId: function () {
10551             this.isLoaded();
10552             return this._id;
10553         },
10554         
10555         /**
10556          * Getter for the queue name
10557          * @returns {String}
10558          *      The name of the Queue
10559          */
10560         getName: function () {
10561             this.isLoaded();
10562             return this.getData().name;
10563         },
10564         
10565         /**
10566          * Getter for the queue statistics.
10567          * Supported statistics include:<br>
10568          *  - agentsBusyOther<br>
10569          *  - agentsLoggedOn<br>
10570          *  - agentsNotReady<br>
10571          *  - agentsReady<br>
10572          *  - agentsTalkingInbound<br>
10573          *  - agentsTalkingInternal<br>
10574          *  - agentsTalkingOutbound<br>
10575          *  - agentsWrapUpNotReady<br>
10576          *  - agentsWrapUpReady<br>
10577          *  - callsInQueue<br>
10578          *  - startTimeOfLongestCallInQueue<br>
10579          *  <br>
10580          *  These statistics can be accessed via dot notation:<br>
10581          *  i.e.: getStatistics().callsInQueue
10582          * @returns {Object}
10583          *      The Object with different statistics as properties.
10584          */
10585         getStatistics: function () {
10586             this.isLoaded();
10587             return this.getData().statistics;       
10588         },
10589 
10590         /**
10591          * Parses a uriString to retrieve the id portion
10592          * @param {String} uriString
10593          * @return {String} id
10594          */
10595         _parseIdFromUriString : function (uriString) {
10596             return Utilities.getId(uriString);
10597         }
10598 
10599     });
10600 	
10601 	window.finesse = window.finesse || {};
10602     window.finesse.restservices = window.finesse.restservices || {};
10603     window.finesse.restservices.Queue = Queue;
10604     
10605     return Queue;
10606 });
10607 
10608 /**
10609  * JavaScript representation of the Finesse Queues collection
10610  * object which contains a list of Queue objects.
10611  * @requires finesse.clientservices.ClientServices
10612  * @requires Class
10613  * @requires finesse.FinesseBase
10614  * @requires finesse.restservices.RestBase
10615  * @requires finesse.restservices.RestCollectionBase
10616  */
10617 
10618 /**
10619  * @class
10620  * JavaScript representation of a Queues collection object.
10621  *
10622  * @constructor
10623  * @borrows finesse.restservices.RestCollectionBase as finesse.restservices.Queues
10624  */
10625 
10626 /** @private */
10627 define('restservices/Queues',[
10628     'restservices/RestCollectionBase',
10629     'restservices/Queue'
10630 ],
10631 function (RestCollectionBase, Queue) {
10632     var Queues = RestCollectionBase.extend(/** @lends finesse.restservices.Queues.prototype */{
10633 
10634         /**
10635          * @class
10636          * JavaScript representation of a Queues collection object. 
10637          * @augments finesse.restservices.RestCollectionBase
10638          * @constructs
10639          * @see finesse.restservices.Queue
10640          * @example
10641          *  _queues = _user.getQueues( {
10642          *      onCollectionAdd : _handleQueueAdd,
10643          *      onCollectionDelete : _handleQueueDelete,
10644          *      onLoad : _handleQueuesLoaded
10645          *  });
10646          *  
10647          * _queueCollection = _queues.getCollection();
10648          * for (var queueId in _queueCollection) {
10649          *     if (_queueCollection.hasOwnProperty(queueId)) {
10650          *         _queue = _queueCollection[queueId];
10651          *         etc...
10652          *     }
10653          * }
10654          */
10655         _fakeConstuctor: function () {
10656             /* This is here to hide the real init constructor from the public docs */
10657         },
10658 	    
10659          /**
10660          * @private
10661          * JavaScript representation of a Queues object. Also exposes
10662          * methods to operate on the object against the server.
10663          *
10664          * @param {Object} options
10665          *     An object with the following properties:<ul>
10666          *         <li><b>id:</b> The id of the object being constructed</li>
10667          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10668          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10669          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10670          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10671          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10672          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10673          *             <li><b>content:</b> {String} Raw string of response</li>
10674          *             <li><b>object:</b> {Object} Parsed object of response</li>
10675          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10676          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10677          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10678          *             </ul></li>
10679          *         </ul></li>
10680          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10681          **/
10682         init: function (options) {
10683             this._super(options);           
10684         },
10685 
10686         /**
10687          * @private
10688          * Gets xmpp node path.
10689          * @returns {String} The node path
10690          */
10691         getXMPPNodePath: function () {
10692             return this.getRestUrl();
10693         },
10694 
10695         /**
10696          * @private
10697          * Gets the REST class for the current object - this is the Queues class.
10698          * @returns {Object} The Queues class.
10699          */
10700         getRestClass: function () {
10701             return Queues;
10702         },
10703 
10704         /**
10705          * @private
10706          * Gets the REST class for the objects that make up the collection. - this
10707          * is the Queue class.
10708          * @returns {finesse.restservices.Queue}
10709          * The Queue class
10710          * @see finesse.restservices.Queue
10711          */
10712         getRestItemClass: function () {
10713             return Queue;
10714         },
10715 
10716         /**
10717          * @private
10718          * Gets the REST type for the current object - this is a "Queues".
10719          * @returns {String} The Queues String
10720          */
10721         getRestType: function () {
10722             return "Queues";
10723         },
10724         
10725         /**
10726          * @private
10727          * Gets the REST type for the objects that make up the collection - this is "Queue".
10728          * @returns {String} The Queue String
10729          */
10730         getRestItemType: function () {
10731             return "Queue";
10732         },
10733 
10734         explicitSubscription: true,
10735         
10736         handlesItemRefresh: true
10737     });
10738     
10739     window.finesse = window.finesse || {};
10740     window.finesse.restservices = window.finesse.restservices || {};
10741     window.finesse.restservices.Queues = Queues;
10742     
10743     return Queues;
10744 });
10745 
10746 /**
10747  * JavaScript representation of the Finesse WrapUpReason object.
10748  *
10749  * @requires finesse.clientservices.ClientServices
10750  * @requires Class
10751  * @requires finesse.FinesseBase
10752  * @requires finesse.restservices.RestBase
10753  */
10754 
10755 /** @private */
10756 define('restservices/WrapUpReason',['restservices/RestBase'], function (RestBase) {
10757 
10758     var WrapUpReason = RestBase.extend(/** @lends finesse.restservices.WrapUpReason.prototype */{
10759 
10760         /**
10761          * @class
10762          * A WrapUpReason is a code and description identifying a particular reason that a
10763          * User is in WORK (WrapUp) mode.
10764          * 
10765          * @augments finesse.restservices.RestBase
10766          * @see finesse.restservices.User
10767          * @see finesse.restservices.User.States#WORK
10768          * @constructs
10769          */
10770         _fakeConstuctor: function () {
10771             /* This is here to hide the real init constructor from the public docs */
10772         },
10773         
10774         /** 
10775          * @private
10776          * JavaScript representation of a WrapUpReason object. Also exposes
10777          * methods to operate on the object against the server.
10778          *
10779          * @param {Object} options
10780          *     An object with the following properties:<ul>
10781          *         <li><b>id:</b> The id of the object being constructed</li>
10782          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10783          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10784          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10785          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10786          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10787          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10788          *             <li><b>content:</b> {String} Raw string of response</li>
10789          *             <li><b>object:</b> {Object} Parsed object of response</li>
10790          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10791          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10792          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10793          *             </ul></li>
10794          *         </ul></li>
10795          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10796          **/
10797         init: function (options) {
10798             this._super(options);
10799         },
10800 
10801         /**
10802          * @private
10803          * Gets the REST class for the current object - this is the WrapUpReason class.
10804          * @returns {Object} The WrapUpReason class.
10805          */
10806         getRestClass: function () {
10807             return WrapUpReason;
10808         },
10809 
10810         /**
10811          * @private
10812          * Gets the REST type for the current object - this is a "WrapUpReason".
10813          * @returns {String} The WrapUpReason string.
10814          */
10815         getRestType: function () {
10816             return "WrapUpReason";
10817         },
10818 
10819         /**
10820          * @private
10821          * Gets the REST type for the current object - this is a "WrapUpReasons".
10822          * @returns {String} The WrapUpReasons string.
10823          */
10824         getParentRestType: function () {
10825             return "WrapUpReasons";
10826         },
10827 
10828         /**
10829          * @private
10830          * Override default to indicate that this object doesn't support making
10831          * requests.
10832          */
10833         supportsRequests: false,
10834 
10835         /**
10836          * @private
10837          * Override default to indicate that this object doesn't support subscriptions.
10838          */
10839         supportsSubscriptions: false,
10840 
10841         /**
10842          * Getter for the label.
10843          * @returns {String} The label.
10844          */
10845         getLabel: function () {
10846             this.isLoaded();
10847             return this.getData().label;
10848         },
10849 
10850         /**
10851          * @private
10852          * Getter for the forAll flag.
10853          * @returns {Boolean} True if global.
10854          */
10855         getForAll: function () {
10856             this.isLoaded();
10857             return this.getData().forAll;
10858         },
10859 
10860         /**
10861          * @private
10862          * Getter for the Uri value.
10863          * @returns {String} The Uri.
10864          */
10865         getUri: function () {
10866             this.isLoaded();
10867             return this.getData().uri;
10868         }
10869     });
10870 
10871     window.finesse = window.finesse || {};
10872     window.finesse.restservices = window.finesse.restservices || {};
10873     window.finesse.restservices.WrapUpReason = WrapUpReason;
10874         
10875     return WrapUpReason;
10876 });
10877 
10878 /**
10879 * JavaScript representation of the Finesse WrapUpReasons collection
10880 * object which contains a list of WrapUpReason objects.
10881  *
10882  * @requires finesse.clientservices.ClientServices
10883  * @requires Class
10884  * @requires finesse.FinesseBase
10885  * @requires finesse.restservices.RestBase
10886  * @requires finesse.restservices.Dialog
10887  * @requires finesse.restservices.RestCollectionBase
10888  */
10889 
10890 /** @private */
10891 define('restservices/WrapUpReasons',[
10892     'restservices/RestCollectionBase',
10893     'restservices/WrapUpReason'
10894 ],
10895 function (RestCollectionBase, WrapUpReason) {
10896 
10897     var WrapUpReasons = RestCollectionBase.extend(/** @lends finesse.restservices.WrapUpReasons.prototype */{
10898     	teamId: null,
10899         /**
10900          * @class
10901          * JavaScript representation of a WrapUpReasons collection object. 
10902          * @augments finesse.restservices.RestCollectionBase
10903          * @constructs
10904          * @see finesse.restservices.WrapUpReason
10905          * @example
10906          *  _wrapUpReasons = _user.getWrapUpReasons ( {
10907          *      onCollectionAdd : _handleWrapUpReasonAdd,
10908          *      onCollectionDelete : _handleWrapUpReasonDelete,
10909          *      onLoad : _handleWrapUpReasonsLoaded
10910          *  });
10911          *  
10912          * _wrapUpReasonCollection = _wrapUpReasons.getCollection();
10913          * for (var wrapUpReasonId in _wrapUpReasonCollection) {
10914          *     if (_wrapUpReasonCollection.hasOwnProperty(wrapUpReasonId)) {
10915          *         _wrapUpReason = _wrapUpReasonCollection[wrapUpReasonId];
10916          *         etc...
10917          *     }
10918          * }
10919         */
10920         _fakeConstuctor: function () {
10921             /* This is here to hide the real init constructor from the public docs */
10922         },
10923         
10924         /** 
10925          * @private
10926          * JavaScript representation of a WrapUpReasons collection object. Also exposes
10927          * methods to operate on the object against the server.
10928          *
10929          * @param {Object} options
10930          *     An object with the following properties:<ul>
10931          *         <li><b>id:</b> The id of the object being constructed</li>
10932          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10933          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10934          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10935          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10936          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10937          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10938          *             <li><b>content:</b> {String} Raw string of response</li>
10939          *             <li><b>object:</b> {Object} Parsed object of response</li>
10940          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10941          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10942          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10943          *             </ul></li>
10944          *         </ul></li>
10945          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10946          **/
10947         init: function (options) {
10948         	if(options && options.teamId){
10949             	this.teamId = options.teamId;
10950             }
10951         	
10952             this._super(options);
10953         },
10954 
10955         getRestUrl: function () {
10956         	if(this.teamId){
10957         		return this.getBaseRestUrl() + "/TeamResource/" + this.teamId + "/"+ this.getRestType();
10958         	}
10959         	return this._super();
10960         },
10961         
10962         /**
10963          * @private
10964          * Gets the REST class for the current object - this is the WrapUpReasons class.
10965          * @returns {Object}
10966          * The WrapUpReasons constructor.
10967          */
10968         getRestClass: function () {
10969             return WrapUpReasons;
10970         },
10971 
10972         /**
10973          * @private
10974          * Gets the REST class for the objects that make up the collection. - this
10975          * is the WrapUpReason class.
10976          * @returns {finesse.restservices.WrapUpReason}
10977          * The WrapUpReason class
10978          * @see finesse.restservices.WrapUpReason
10979          */
10980         getRestItemClass: function () {
10981             return WrapUpReason;
10982         },
10983 
10984         /**
10985          * @private
10986          * Gets the REST type for the current object - this is a "WrapUpReasons".
10987          * @returns {String} The WrapUpReasons String
10988          */
10989         getRestType: function () {
10990             return "WrapUpReasons";
10991         },
10992         
10993         /**
10994          * @private
10995          * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
10996          * @returns {String} The WrapUpReason String
10997          */
10998         getRestItemType: function () {
10999             return "WrapUpReason";
11000         },
11001 
11002         /**
11003          * @private
11004          * Override default to indicates that the collection supports making
11005          * requests.
11006          */
11007         supportsRequests: true,
11008 
11009         /**
11010          * @private
11011          * Override default to indicate that this object doesn't support subscriptions.
11012          */
11013         supportsRestItemSubscriptions: false,
11014 
11015         /**
11016          * @private
11017          * Retrieve the Wrap-Up Reason Codes. This call will re-query the server and refresh the collection.
11018          *
11019          * @returns {finesse.restservices.WrapUpReasons}
11020          *     This ReadyReasonCodes object to allow cascading.
11021          */
11022         get: function () {
11023             // set loaded to false so it will rebuild the collection after the get
11024             this._loaded = false;
11025             // reset collection
11026             this._collection = {};
11027             // perform get
11028             this._synchronize();
11029             return this;
11030         }
11031         
11032     });
11033  
11034     window.finesse = window.finesse || {};
11035     window.finesse.restservices = window.finesse.restservices || {};
11036     window.finesse.restservices.WrapUpReasons = WrapUpReasons;
11037        
11038     return WrapUpReasons;
11039 });
11040 
11041 /**
11042  * JavaScript representation of the Finesse Contact object.
11043  * @requires finesse.clientservices.ClientServices
11044  * @requires Class
11045  * @requires finesse.FinesseBase
11046  * @requires finesse.restservices.RestBase
11047  */
11048 /** @private */
11049 define('restservices/Contact',['restservices/RestBase'], function (RestBase) {
11050 
11051     var Contact = RestBase.extend(/** @lends finesse.restservices.Contact.prototype */{
11052 
11053         /**
11054          * @class
11055          * A Contact is a single entry in a PhoneBook, consisting of a First and Last Name,
11056          * a Phone Number, and a Description.
11057          * 
11058          * @augments finesse.restservices.RestBase
11059          * @see finesse.restservices.PhoneBook
11060          * @constructs
11061          */
11062         _fakeConstuctor: function () {
11063             /* This is here to hide the real init constructor from the public docs */
11064         },
11065         
11066         /**
11067          * @private
11068          * @param {Object} options
11069          *     An object with the following properties:<ul>
11070          *         <li><b>id:</b> The id of the object being constructed</li>
11071          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11072          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11073          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11074          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11075          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11076          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11077          *             <li><b>content:</b> {String} Raw string of response</li>
11078          *             <li><b>object:</b> {Object} Parsed object of response</li>
11079          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11080          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11081          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11082          *             </ul></li>
11083          *         </ul></li>
11084          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11085          **/
11086         init: function (options) {
11087             this._super(options);
11088         },
11089 
11090         /**
11091          * @private
11092          * Gets the REST class for the current object - this is the Contact class.
11093          * @returns {Object} The Contact class.
11094          */
11095         getRestClass: function () {
11096             return Contact;
11097         },
11098 
11099         /**
11100          * @private
11101          * Gets the REST type for the current object - this is a "Contact".
11102          * @returns {String} The Contact string.
11103          */
11104         getRestType: function () {
11105             return "Contact";
11106         },
11107 
11108         /**
11109          * @private
11110          * Override default to indicate that this object doesn't support making
11111          * requests.
11112          */
11113         supportsRequests: false,
11114 
11115         /**
11116          * @private
11117          * Override default to indicate that this object doesn't support subscriptions.
11118          */
11119         supportsSubscriptions: false,
11120 
11121         /**
11122          * Method to get the firstName of the contact.
11123          * @returns {String} The firstName of the contact.
11124          * @example	 
11125          * restContact.getFirstName();
11126          * 
11127          */
11128         getFirstName: function () {
11129             this.isLoaded();
11130             return this.getData().firstName;
11131         },
11132 
11133         /**
11134          * Method to get the lastName of the contact.
11135          * @returns {String} The lastName of the contact.
11136          * @example	 
11137          * restContact.getLastName();
11138          */
11139         getLastName: function () {
11140             this.isLoaded();
11141             return this.getData().lastName;
11142         },
11143 
11144         /**
11145          * Method to get the phone numnber of the contact.
11146          * @returns {String} The phoneNumber of the contact.
11147          * @example	 
11148          * restContact.getPhoneNumber();
11149          */
11150         getPhoneNumber: function () {
11151             this.isLoaded();
11152             return this.getData().phoneNumber;
11153         },
11154 
11155         /**
11156          * Method to get the description of the contact.
11157          * @returns {String} The description of the contact.
11158          * @example	 
11159          * restContact.getDescription();
11160          */
11161         getDescription: function () {
11162             this.isLoaded();
11163             return this.getData().description;
11164         },
11165 
11166         /** @private */
11167         createPutSuccessHandler: function(contact, contentBody, successHandler){
11168             return function (rsp) {
11169                 // Update internal structure based on response. Here we
11170                 // inject the contentBody from the PUT request into the
11171                 // rsp.object element to mimic a GET as a way to take
11172                 // advantage of the existing _processResponse method.
11173                 rsp.object = contentBody;
11174                 contact._processResponse(rsp);
11175 
11176                 //Remove the injected Contact object before cascading response
11177                 rsp.object = {};
11178                 
11179                 //cascade response back to consumer's response handler
11180                 successHandler(rsp);
11181             };
11182         },
11183 
11184         /** @private */
11185         createPostSuccessHandler: function (contact, contentBody, successHandler) {
11186             return function (rsp) {
11187                 rsp.object = contentBody;
11188                 contact._processResponse(rsp);
11189 
11190                 //Remove the injected Contact object before cascading response
11191                 rsp.object = {};
11192 
11193                 //cascade response back to consumer's response handler
11194                 successHandler(rsp);
11195             };
11196         },
11197 
11198         /**
11199          * Add
11200          * @private
11201          */
11202         add: function (newValues, handlers) {
11203             // this.isLoaded();
11204             var contentBody = {};
11205 
11206             contentBody[this.getRestType()] = {
11207                 "firstName": newValues.firstName,
11208                 "lastName": newValues.lastName,
11209                 "phoneNumber": newValues.phoneNumber,
11210                 "description": newValues.description
11211             };
11212 
11213             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11214             handlers = handlers || {};
11215 
11216             this.restRequest(this.getRestUrl(), {
11217                 method: 'POST',
11218                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
11219                 error: handlers.error,
11220                 content: contentBody
11221             });
11222 
11223             return this; // Allow cascading
11224         },
11225 
11226         /**
11227          * Update
11228          * @private
11229          */
11230         update: function (newValues, handlers) {
11231             this.isLoaded();
11232             var contentBody = {};
11233 
11234             contentBody[this.getRestType()] = {
11235                 "uri": this.getId(),
11236                 "firstName": newValues.firstName,
11237                 "lastName": newValues.lastName,
11238                 "phoneNumber": newValues.phoneNumber,
11239                 "description": newValues.description
11240             };
11241 
11242             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11243             handlers = handlers || {};
11244 
11245             this.restRequest(this.getRestUrl(), {
11246                 method: 'PUT',
11247                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
11248                 error: handlers.error,
11249                 content: contentBody
11250             });
11251 
11252             return this; // Allow cascading
11253         },
11254 
11255 
11256         /**
11257          * Delete
11258          * @private
11259          */
11260         "delete": function ( handlers) {
11261             this.isLoaded();
11262 
11263             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11264             handlers = handlers || {};
11265 
11266             this.restRequest(this.getRestUrl(), {
11267                 method: 'DELETE',
11268                 success: this.createPutSuccessHandler(this, {}, handlers.success),
11269                 error: handlers.error,
11270                 content: undefined
11271             });
11272 
11273             return this; // Allow cascading
11274         }
11275     });
11276 
11277     window.finesse = window.finesse || {};
11278     window.finesse.restservices = window.finesse.restservices || {};
11279     window.finesse.restservices.Contact = Contact;
11280     
11281     return Contact;
11282 });
11283 
11284 /**
11285 * JavaScript representation of the Finesse Contacts collection
11286 * object which contains a list of Contact objects.
11287  *
11288  * @requires finesse.clientservices.ClientServices
11289  * @requires Class
11290  * @requires finesse.FinesseBase
11291  * @requires finesse.restservices.RestBase
11292  * @requires finesse.restservices.Dialog
11293  * @requires finesse.restservices.RestCollectionBase
11294  */
11295 /** @private */
11296 define('restservices/Contacts',[
11297     'restservices/RestCollectionBase',
11298     'restservices/Contact'
11299 ],
11300 function (RestCollectionBase, Contact) {
11301     var Contacts = RestCollectionBase.extend(/** @lends finesse.restservices.Contacts.prototype */{
11302         
11303         /**
11304          * @class
11305          * JavaScript representation of a Contacts collection object. Also exposes
11306          * methods to operate on the object against the server.
11307          * @augments finesse.restservices.RestCollectionBase
11308          * @constructs
11309          * @see finesse.restservices.Contact
11310          * @see finesse.restservices.PhoneBook
11311          * @example
11312          *  _contacts = _phonebook.getContacts( {
11313          *      onCollectionAdd : _handleContactAdd,
11314          *      onCollectionDelete : _handleContactDelete,
11315          *      onLoad : _handleContactsLoaded
11316          *  });
11317          *  
11318          * _contactCollection = _contacts.getCollection();
11319          * for (var contactId in _contactCollection) {
11320          *     if (_contactCollection.hasOwnProperty(contactId)) {
11321          *         _contact = _contactCollection[contactId];
11322          *         etc...
11323          *     }
11324          * }
11325          */
11326         _fakeConstuctor: function () {
11327             /* This is here to hide the real init constructor from the public docs */
11328         },
11329         
11330         /** 
11331          * @private
11332          * JavaScript representation of a Contacts 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          **/
11353         init: function (options) {
11354             this._super(options);           
11355         },
11356 
11357         /**
11358          * @private
11359          * Gets the REST class for the current object - this is the Contacts class.
11360          */
11361         getRestClass: function () {
11362             return Contacts;
11363         },
11364 
11365         /**
11366          * @private
11367          * Gets the REST class for the objects that make up the collection. - this
11368          * is the Contact class.
11369          */
11370         getRestItemClass: function () {
11371             return Contact;
11372         },
11373 
11374         /**
11375          * @private
11376          * Gets the REST type for the current object - this is a "Contacts".
11377          */
11378         getRestType: function () {
11379             return "Contacts";
11380         },
11381         
11382         /**
11383          * @private
11384          * Gets the REST type for the objects that make up the collection - this is "Contacts".
11385          */
11386         getRestItemType: function () {
11387             return "Contact";
11388         },
11389 
11390         /**
11391          * @private
11392          * Override default to indicates that the collection supports making
11393          * requests.
11394          */
11395         supportsRequests: true,
11396 
11397         /**
11398          * @private
11399          * Override default to indicates that the collection subscribes to its objects.
11400          */
11401         supportsRestItemSubscriptions: false,
11402         
11403         /**
11404          * @private
11405          * Retrieve the Contacts.  This call will re-query the server and refresh the collection.
11406          *
11407          * @returns {finesse.restservices.Contacts}
11408          *     This Contacts object, to allow cascading.
11409          */
11410         get: function () {
11411             // set loaded to false so it will rebuild the collection after the get
11412             this._loaded = false;
11413             // reset collection
11414             this._collection = {};
11415             // perform get
11416             this._synchronize();
11417             return this;
11418         }
11419         
11420     });
11421     
11422     window.finesse = window.finesse || {};
11423     window.finesse.restservices = window.finesse.restservices || {};
11424     window.finesse.restservices.Contacts = Contacts;
11425     
11426     
11427     return Contacts;
11428 });
11429 
11430 /**
11431  * JavaScript representation of the Finesse PhoneBook object.
11432  *
11433  * @requires finesse.clientservices.ClientServices
11434  * @requires Class
11435  * @requires finesse.FinesseBase
11436  * @requires finesse.restservices.RestBase
11437  */
11438 
11439 /** @private */
11440 define('restservices/PhoneBook',[
11441     'restservices/RestBase',
11442     'restservices/Contacts'
11443 ],
11444 function (RestBase, Contacts) {
11445     var PhoneBook = RestBase.extend(/** @lends finesse.restservices.PhoneBook.prototype */{
11446 
11447         _contacts: null,
11448 
11449         /**
11450          * @class
11451          * A PhoneBook is a list of Contacts available to a User for quick dial.
11452          * 
11453          * @augments finesse.restservices.RestBase
11454          * @see finesse.restservices.Contacts
11455          * @constructs
11456          */
11457         _fakeConstuctor: function () {
11458             /* This is here to hide the real init constructor from the public docs */
11459         },
11460         
11461         /** 
11462          * @private
11463          * JavaScript representation of a PhoneBook object. Also exposes
11464          * methods to operate on the object against the server.
11465          *
11466          * @param {Object} options
11467          *     An object with the following properties:<ul>
11468          *         <li><b>id:</b> The id of the object being constructed</li>
11469          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11470          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11471          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11472          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11473          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11474          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11475          *             <li><b>content:</b> {String} Raw string of response</li>
11476          *             <li><b>object:</b> {Object} Parsed object of response</li>
11477          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11478          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11479          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11480          *             </ul></li>
11481          *         </ul></li>
11482          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11483          **/
11484         init: function (options) {
11485             this._super(options);
11486         },
11487 
11488         /**
11489          * @private
11490          * Gets the REST class for the current object - this is the PhoneBook class.
11491          * @returns {Object} The PhoneBook class.
11492          */
11493         getRestClass: function () {
11494             return PhoneBook;
11495         },
11496 
11497         /**
11498          * @private
11499          * Gets the REST type for the current object - this is a "PhoneBook".
11500          * @returns {String} The PhoneBook string.
11501          */
11502         getRestType: function () {
11503             return "PhoneBook";
11504         },
11505 
11506         /**
11507          * @private
11508          * Override default to indicate that this object doesn't support making
11509          * requests.
11510          */
11511         supportsRequests: false,
11512 
11513         /**
11514          * @private
11515          * Override default to indicate that this object doesn't support subscriptions.
11516          */
11517         supportsSubscriptions: false,
11518 
11519         /**
11520          * Getter for the name of the Phone Book.
11521          * @returns {String} The name.
11522          */
11523         getName: function () {
11524             this.isLoaded();
11525             return this.getData().name;
11526         },
11527 
11528         /**
11529          * Getter for the type flag.
11530          * @returns {String} The type.
11531          */
11532         getType: function () {
11533             this.isLoaded();
11534             return this.getData().type;
11535         },
11536 
11537         /**
11538          * @private
11539          * Getter for the Uri value.
11540          * @returns {String} The Uri.
11541          */
11542         getUri: function () {
11543             this.isLoaded();
11544             return this.getData().uri;
11545         },
11546 
11547         /**
11548          * Getter for a Contacts collection object that is associated with PhoneBook.
11549          * @param {finesse.interfaces.RequestHandlers} handlers
11550          *     An object containing the handlers for the request
11551          * @returns {finesse.restservices.Contacts}
11552          *     A Contacts collection object.
11553          */
11554         getContacts: function (callbacks) {
11555             var options = callbacks || {};
11556             options.parentObj = this;
11557             this.isLoaded();
11558 
11559             if (this._contacts === null) {
11560                 this._contacts = new Contacts(options);
11561             }
11562 
11563             return this._contacts;
11564         },
11565 
11566         /**
11567          * Getter for <contacts> node within PhoneBook - sometimes it's just a URI, sometimes it is a Contacts collection
11568          * @returns {String} uri to contacts
11569          *          or {finesse.restservices.Contacts} collection
11570          */
11571         getEmbeddedContacts: function(){
11572             this.isLoaded();
11573             return this.getData().contacts;
11574         },
11575 
11576         /** @private */
11577         createPutSuccessHandler: function(phonebook, contentBody, successHandler){
11578             return function (rsp) {
11579                 // Update internal structure based on response. Here we
11580                 // inject the contentBody from the PUT request into the
11581                 // rsp.object element to mimic a GET as a way to take
11582                 // advantage of the existing _processResponse method.
11583                 rsp.object = contentBody;
11584                 phonebook._processResponse(rsp);
11585 
11586                 //Remove the injected PhoneBook object before cascading response
11587                 rsp.object = {};
11588                 
11589                 //cascade response back to consumer's response handler
11590                 successHandler(rsp);
11591             };
11592         },
11593 
11594         /** @private */
11595         createPostSuccessHandler: function (phonebook, contentBody, successHandler) {
11596             return function (rsp) {
11597                 rsp.object = contentBody;
11598                 phonebook._processResponse(rsp);
11599 
11600                 //Remove the injected PhoneBook object before cascading response
11601                 rsp.object = {};
11602 
11603                 //cascade response back to consumer's response handler
11604                 successHandler(rsp);
11605             };
11606         },
11607 
11608         /**
11609          * @private
11610          * Add a PhoneBook.
11611          * @param {Object} newValues
11612          * @param {String} newValues.name Name of PhoneBook
11613          * @param {String} newValues.type Type of PhoneBook
11614          * @param {finesse.interfaces.RequestHandlers} handlers
11615          *     An object containing the handlers for the request
11616          * @returns {finesse.restservices.PhoneBook}
11617          *     This PhoneBook object, to allow cascading
11618          */
11619         add: function (newValues, handlers) {
11620             // this.isLoaded();
11621             var contentBody = {};
11622 
11623             contentBody[this.getRestType()] = {
11624                 "name": newValues.name,
11625                 "type": newValues.type
11626             };
11627 
11628             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11629             handlers = handlers || {};
11630 
11631             this.restRequest(this.getRestUrl(), {
11632                 method: 'POST',
11633                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
11634                 error: handlers.error,
11635                 content: contentBody
11636             });
11637 
11638             return this; // Allow cascading
11639         },
11640 
11641         /**
11642          * @private
11643          * Update a PhoneBook.
11644          * @param {Object} newValues
11645          * @param {String} newValues.name Name of PhoneBook
11646          * @param {String} newValues.type Type of PhoneBook
11647          * @param {finesse.interfaces.RequestHandlers} handlers
11648          *     An object containing the handlers for the request
11649          * @returns {finesse.restservices.PhoneBook}
11650          *     This PhoneBook object, to allow cascading
11651          */
11652         update: function (newValues, handlers) {
11653             this.isLoaded();
11654             var contentBody = {};
11655 
11656             contentBody[this.getRestType()] = {
11657                 "uri": this.getId(),
11658                 "name": newValues.name,
11659                 "type": newValues.type
11660             };
11661 
11662             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11663             handlers = handlers || {};
11664 
11665             this.restRequest(this.getRestUrl(), {
11666                 method: 'PUT',
11667                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
11668                 error: handlers.error,
11669                 content: contentBody
11670             });
11671 
11672             return this; // Allow cascading
11673         },
11674 
11675 
11676         /**
11677          * Delete a PhoneBook.
11678          * @param {finesse.interfaces.RequestHandlers} handlers
11679          *     An object containing the handlers for the request
11680          * @returns {finesse.restservices.PhoneBook}
11681          *     This PhoneBook object, to allow cascading
11682          */
11683         "delete": function ( handlers) {
11684             this.isLoaded();
11685 
11686             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11687             handlers = handlers || {};
11688 
11689             this.restRequest(this.getRestUrl(), {
11690                 method: 'DELETE',
11691                 success: this.createPutSuccessHandler(this, {}, handlers.success),
11692                 error: handlers.error,
11693                 content: undefined
11694             });
11695 
11696             return this; // Allow cascading
11697         }
11698 
11699 
11700 
11701     });
11702     
11703     window.finesse = window.finesse || {};
11704     window.finesse.restservices = window.finesse.restservices || {};
11705     window.finesse.restservices.PhoneBook = PhoneBook;
11706     
11707     return PhoneBook;
11708 });
11709 
11710 /**
11711 * JavaScript representation of the Finesse PhoneBooks collection
11712 * object which contains a list of PhoneBook objects.
11713  *
11714  * @requires finesse.clientservices.ClientServices
11715  * @requires Class
11716  * @requires finesse.FinesseBase
11717  * @requires finesse.restservices.RestBase
11718  * @requires finesse.restservices.Dialog
11719  * @requires finesse.restservices.RestCollectionBase
11720  */
11721 /** @private */
11722 define('restservices/PhoneBooks',[
11723     'restservices/RestCollectionBase',
11724     'restservices/PhoneBook'
11725 ],
11726 function (RestCollectionBase, PhoneBook) {
11727     var PhoneBooks = RestCollectionBase.extend(/** @lends finesse.restservices.PhoneBooks.prototype */{
11728         
11729     	teamId: null,
11730         /**
11731          * @class
11732          * JavaScript representation of a PhoneBooks collection object. 
11733          * @augments finesse.restservices.RestCollectionBase
11734          * @constructs
11735          * @see finesse.restservices.PhoneBook
11736          * @see finesse.restservices.Contacts
11737          * @see finesse.restservices.Contact
11738          * @example
11739          *  _phoneBooks = _user.getPhoneBooks( {
11740          *      onCollectionAdd : _handlePhoneBookAdd,
11741          *      onCollectionDelete : _handlePhoneBookDelete,
11742          *      onLoad : _handlePhoneBooksLoaded
11743          *  });
11744          *  
11745          * _phoneBookCollection = _phoneBooks.getCollection();
11746          * for (var phoneBookId in _phoneBookCollection) {
11747          *     if (_phoneBookCollection.hasOwnProperty(phoneBookId)) {
11748          *         _phoneBook = _phoneBookCollection[phoneBookId];
11749          *         etc...
11750          *     }
11751          * }
11752         */
11753         _fakeConstuctor: function () {
11754             /* This is here to hide the real init constructor from the public docs */
11755         },
11756         
11757        /**
11758          * @private
11759          *
11760          * @param {Object} options
11761          *     An object with the following properties:<ul>
11762          *         <li><b>id:</b> The id of the object being constructed</li>
11763          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11764          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11765          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11766          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11767          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11768          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11769          *             <li><b>content:</b> {String} Raw string of response</li>
11770          *             <li><b>object:</b> {Object} Parsed object of response</li>
11771          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11772          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11773          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11774          *             </ul></li>
11775          *         </ul></li>
11776          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11777          **/
11778         init: function (options) {
11779             // Keep the REST response for PhoneBooks to check for 206 Partial Content.
11780             this.keepRestResponse = true;
11781             // Add in the Range header which is required for PhoneBooks API.
11782             this.extraHeaders = { "Range": "objects=1-6000" };
11783             if(options && options.teamId){
11784             	this.teamId = options.teamId;
11785             }
11786             this._super(options);
11787         },
11788         
11789         getRestUrl: function () {
11790         	if(this.teamId){
11791         		return this.getBaseRestUrl() + "/TeamResource/" + this.teamId + "/"+ this.getRestType();
11792         	}
11793         	return this._super();
11794         },
11795 
11796         /**
11797          * @private
11798          * Gets the REST class for the current object - this is the PhoneBooks class.
11799          * @returns {Object} The PhoneBooks class.
11800          */
11801         getRestClass: function () {
11802             return PhoneBooks;
11803         },
11804 
11805         /**
11806          * @private
11807          * Gets the REST class for the objects that make up the collection. - this is the PhoneBook class.
11808          * @returns {Object} The PhoneBook class
11809          */
11810         getRestItemClass: function () {
11811             return PhoneBook;
11812         },
11813 
11814         /**
11815          * @private
11816          * Gets the REST type for the current object - this is a "PhoneBooks".
11817          * @returns {String} The PhoneBooks string.
11818          */
11819         getRestType: function () {
11820             return "PhoneBooks";
11821         },
11822         
11823         /**
11824          * @private
11825          * Gets the REST type for the objects that make up the collection - this is "PhoneBooks".
11826          * @returns {String} The PhoneBook string.
11827          */
11828         getRestItemType: function () {
11829             return "PhoneBook";
11830         },
11831 
11832         /**
11833          * @private
11834          * Override default to indicates that the collection supports making
11835          * requests.
11836          */
11837         supportsRequests: true,
11838 
11839         /**
11840          * @private
11841          * Override default to indicates that the collection subscribes to its objects.
11842          */
11843         supportsRestItemSubscriptions: false,
11844         
11845         /**
11846          * @private
11847          * Retrieve the PhoneBooks.  This call will re-query the server and refresh the collection.
11848          *
11849          * @returns {finesse.restservices.PhoneBooks}
11850          *     This PhoneBooks object, to allow cascading.
11851          */
11852         get: function () {
11853             // set loaded to false so it will rebuild the collection after the get
11854             this._loaded = false;
11855             // reset collection
11856             this._collection = {};
11857             // perform get
11858             this._synchronize();
11859             return this;
11860         }
11861         
11862     });
11863     
11864     window.finesse = window.finesse || {};
11865     window.finesse.restservices = window.finesse.restservices || {};
11866     window.finesse.restservices.PhoneBooks = PhoneBooks;
11867     
11868     return PhoneBooks;
11869 });
11870 
11871 /**
11872  * JavaScript representation of the Finesse WorkflowAction object.
11873  *
11874  * @requires finesse.clientservices.ClientServices
11875  * @requires Class
11876  * @requires finesse.FinesseBase
11877  * @requires finesse.restservices.RestBase
11878  */
11879 
11880 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
11881 /*global define,finesse */
11882 
11883 /** @private */
11884 define('restservices/WorkflowAction',['restservices/RestBase'], function (RestBase) {
11885 
11886     var WorkflowAction = RestBase.extend({
11887 
11888         _contacts: null,
11889 
11890         actionTypes: [
11891             {
11892                 name: 'BROWSER_POP',
11893                 params: [
11894                     {
11895                         name: 'windowName',
11896                         type: 'text'
11897                     },
11898                     {
11899                         name: 'path',
11900                         type: 'systemVariableSingleLineEditor'
11901                     }
11902                 ]
11903             },
11904             {
11905                 name: 'HTTP_REQUEST',
11906                 params: [
11907                     {
11908                         name: 'method',
11909                         type: 'dropdown',
11910                         values: ['POST', 'PUT']
11911                     },
11912                     {
11913                         name: 'location',
11914                         type: 'dropdown',
11915                         values: ['FINESSE', 'OTHER']
11916                     },
11917                     {
11918                         name: 'contentType',
11919                         type: 'text'
11920                     },
11921                     {
11922                         name: 'path',
11923                         type: 'systemVariableSingleLineEditor'
11924                     },
11925                     {
11926                         name: 'body',
11927                         type: 'systemVariableMultiLineEditor'
11928                     }
11929                 ]
11930             }            
11931             // more action type definitions here
11932         ],
11933 
11934         /**
11935          * @class
11936          * A WorkflowAction is an action (e.g. Browser Pop, Rest Request) defined in a
11937          * Workflow and triggered by a system event (Call Received, Call Ended, etc.).
11938          * 
11939          * @augments finesse.restservices.RestBase
11940          * @see finesse.restservices.Workflow
11941          * @constructs
11942          */
11943         _fakeConstuctor: function () {
11944             /* This is here to hide the real init constructor from the public docs */
11945         },
11946         
11947         /**
11948          * @private
11949          * JavaScript representation of a WorkflowAction object. Also exposes
11950          * methods to operate on the object against the server.
11951          *
11952          * @param {Object} options
11953          *     An object with the following properties:<ul>
11954          *         <li><b>id:</b> The id of the object being constructed</li>
11955          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11956          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11957          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11958          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11959          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11960          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11961          *             <li><b>content:</b> {String} Raw string of response</li>
11962          *             <li><b>object:</b> {Object} Parsed object of response</li>
11963          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11964          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11965          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11966          *             </ul></li>
11967          *         </ul></li>
11968          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11969          **/
11970         init: function (options) {
11971             this._super(options);
11972         },
11973 
11974         /**
11975          * @private
11976          * Gets the REST class for the current object - this is the WorkflowAction class.
11977          * @returns {Object} The WorkflowAction class.
11978          */
11979         getRestClass: function () {
11980             return finesse.restservices.WorkflowAction;
11981         },
11982 
11983         /**
11984          * @private
11985          * Gets the REST type for the current object - this is a "WorkflowAction".
11986          * @returns {String} The WorkflowAction string.
11987          */
11988         getRestType: function () {
11989             return "WorkflowAction";
11990         },
11991 
11992         /**
11993          * @private
11994          * Override default to indicate that this object doesn't support making
11995          * requests.
11996          */
11997         supportsRequests: false,
11998 
11999         /**
12000          * @private
12001          * Override default to indicate that this object doesn't support subscriptions.
12002          */
12003         supportsSubscriptions: false,
12004 
12005         /**
12006          * Getter for the name.
12007          * @returns {String} The name.
12008          */
12009         getName: function () {
12010             this.isLoaded();
12011             return this.getData().name;
12012         },
12013 
12014         /**
12015          * Getter for the type flag.
12016          * @returns {String} The type.
12017          */
12018         getType: function () {
12019             this.isLoaded();
12020             return this.getData().type;
12021         },
12022 
12023         /**
12024          * @private
12025          * Getter for the Uri value.
12026          * @returns {String} The Uri.
12027          */
12028         getUri: function () {
12029             this.isLoaded();
12030             return this.getData().uri;
12031         },
12032 
12033         /**
12034          * @private
12035          * Getter for the handledBy value.
12036          * @returns {String} handledBy.
12037          */
12038         getHandledBy: function () {
12039             this.isLoaded();
12040             return this.getData().handledBy;
12041         },
12042 
12043         /**
12044          * Getter for the parameters.
12045          * @returns {Object} key = param name, value = param value
12046          */
12047         getParams: function () {
12048             var map = {},
12049                 params = this.getData().params.Param,
12050                 i,
12051                 param;
12052 
12053             for(i=0; i<params.length; i+=1){
12054                 param = params[i];
12055                 map[param.name] = param.value || "";
12056             }
12057 
12058             return map;
12059         },
12060 
12061         /**
12062          * Getter for the ActionVariables
12063          * @returns {Object} key = action variable name, value = Object{name, type, node, testValue}
12064          */
12065         getActionVariables: function() {
12066             var map = {},
12067                 actionVariablesParent = this.getData().actionVariables,
12068                 actionVariables,
12069                 i,
12070                 actionVariable;
12071 
12072             if (actionVariablesParent === null ||  typeof(actionVariablesParent) === "undefined" || actionVariablesParent.length === 0){
12073                 return map;
12074             }
12075             actionVariables = actionVariablesParent.ActionVariable;
12076 
12077             if(actionVariables.length > 0){
12078                 for(i=0; i<actionVariables.length; i+=1){
12079                     actionVariable = actionVariables[i];
12080                     // escape nulls to empty string
12081                     actionVariable.name = actionVariable.name || "";
12082                     actionVariable.type = actionVariable.type || "";
12083                     actionVariable.node = actionVariable.node || "";
12084                     actionVariable.testValue = actionVariable.testValue || "";
12085                     map[actionVariable.name] = actionVariable;
12086                 }
12087             } else {
12088                 map[actionVariables.name] = actionVariables;
12089             }
12090 
12091             return map;
12092         },
12093 
12094         /** @private */
12095         createPutSuccessHandler: function(action, contentBody, successHandler){
12096             return function (rsp) {
12097                 // Update internal structure based on response. Here we
12098                 // inject the contentBody from the PUT request into the
12099                 // rsp.object element to mimic a GET as a way to take
12100                 // advantage of the existing _processResponse method.
12101                 rsp.object = contentBody;
12102                 action._processResponse(rsp);
12103 
12104                 //Remove the injected WorkflowAction object before cascading response
12105                 rsp.object = {};
12106                 
12107                 //cascade response back to consumer's response handler
12108                 successHandler(rsp);
12109             };
12110         },
12111 
12112         /** @private */
12113         createPostSuccessHandler: function (action, contentBody, successHandler) {
12114             return function (rsp) {
12115                 rsp.object = contentBody;
12116                 action._processResponse(rsp);
12117 
12118                 //Remove the injected WorkflowAction object before cascading response
12119                 rsp.object = {};
12120 
12121                 //cascade response back to consumer's response handler
12122                 successHandler(rsp);
12123             };
12124         },
12125 
12126         /**
12127          * @private
12128          * Build params array out of all the values coming into add or update methods
12129          * paramMap is a map of params.. we need to translate it into an array of Param objects
12130          * where path and windowName are params for the BROWSER_POP type
12131          */
12132         buildParamsForRest: function(paramMap){
12133             var params = {"Param": []},
12134                 i;
12135             for(i in paramMap){
12136                 if(paramMap.hasOwnProperty(i)){
12137                     params.Param.push({name: i, value: paramMap[i]});
12138                 }
12139             }
12140             return params;
12141         },
12142 
12143         /**
12144          * @private
12145          * Build actionVariables array out of all the values coming into add or update methods
12146          * actionVariableMap is a map of actionVariables.. we need to translate it into an array of ActionVariable objects
12147          * where path and windowName are params for the BROWSER_POP type
12148          */
12149         buildActionVariablesForRest: function(actionVariableMap){
12150             var actionVariables = {"ActionVariable": []},
12151                 i,
12152                 actionVariable;
12153             for(i in actionVariableMap){
12154                 if(actionVariableMap.hasOwnProperty(i)){
12155                     // {name: "callVariable1", type: "SYSTEM", node: "", testValue: "<blink>"}
12156                     actionVariable = {
12157                         "name": actionVariableMap[i].name,
12158                         "type": actionVariableMap[i].type,
12159                         "node": actionVariableMap[i].node,
12160                         "testValue": actionVariableMap[i].testValue
12161                     };
12162                     actionVariables.ActionVariable.push(actionVariable);
12163                 }
12164             }
12165             return actionVariables;
12166         },
12167 
12168         /**
12169          * Add
12170          */
12171         add: function (newValues, handlers) {
12172             var contentBody = {};
12173 
12174             contentBody[this.getRestType()] = {
12175                 "name": newValues.name,
12176                 "type": newValues.type,
12177                 "handledBy": newValues.handledBy,
12178                 "params": this.buildParamsForRest(newValues.params),
12179                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
12180             };
12181 
12182             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12183             handlers = handlers || {};
12184 
12185             this.restRequest(this.getRestUrl(), {
12186                 method: 'POST',
12187                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
12188                 error: handlers.error,
12189                 content: contentBody
12190             });
12191 
12192             return this; // Allow cascading
12193         },
12194 
12195         /**
12196          * @private
12197          * Update
12198          */
12199         update: function (newValues, handlers) {
12200             this.isLoaded();
12201             var contentBody = {};
12202             
12203             contentBody[this.getRestType()] = {
12204                 "uri": this.getId(),
12205                 "name": newValues.name,
12206                 "type": newValues.type,
12207                 "handledBy": newValues.handledBy,
12208                 "params": this.buildParamsForRest(newValues.params),
12209                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
12210             };
12211 
12212             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12213             handlers = handlers || {};
12214 
12215             this.restRequest(this.getRestUrl(), {
12216                 method: 'PUT',
12217                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
12218                 error: handlers.error,
12219                 content: contentBody
12220             });
12221 
12222             return this; // Allow cascading
12223         },
12224 
12225 
12226         /**
12227          * @private
12228          * Delete
12229          */
12230         "delete": function ( handlers) {
12231             this.isLoaded();
12232 
12233             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12234             handlers = handlers || {};
12235 
12236             this.restRequest(this.getRestUrl(), {
12237                 method: 'DELETE',
12238                 success: this.createPutSuccessHandler(this, {}, handlers.success),
12239                 error: handlers.error,
12240                 content: undefined
12241             });
12242 
12243             return this; // Allow cascading
12244         }
12245 
12246 
12247 
12248     });
12249 
12250     window.finesse = window.finesse || {};
12251     window.finesse.restservices = window.finesse.restservices || {};
12252     window.finesse.restservices.WorkflowAction = WorkflowAction;
12253     
12254     return WorkflowAction;
12255 });
12256 
12257 /**
12258 * JavaScript representation of the Finesse WorkflowActions collection
12259 * object which contains a list of WorkflowAction objects.
12260  *
12261  * @requires finesse.clientservices.ClientServices
12262  * @requires Class
12263  * @requires finesse.FinesseBase
12264  * @requires finesse.restservices.RestBase
12265  * @requires finesse.restservices.Dialog
12266  * @requires finesse.restservices.RestCollectionBase
12267  */
12268 
12269 /** @private */
12270 define('restservices/WorkflowActions',[
12271     'restservices/RestCollectionBase',
12272     'restservices/RestBase',
12273     'restservices/WorkflowAction'
12274 ],
12275 function (RestCollectionBase, RestBase, WorkflowAction) {
12276 
12277     var WorkflowActions = RestCollectionBase.extend({
12278         
12279         /**
12280          * @class
12281          * JavaScript representation of a WorkflowActions collection object. 
12282          * @augments finesse.restservices.RestCollectionBase
12283          * @constructs
12284          * @see finesse.restservices.WorkflowAction
12285          * @see finesse.restservices.Workflow
12286          * @see finesse.restservices.Workflows
12287          * @example
12288          *  _workflowActions = _user.getWorkflowActions( {
12289          *      onCollectionAdd : _handleWorkflowActionAdd,
12290          *      onCollectionDelete : _handleWorkflowActionDelete,
12291          *      onLoad : _handleWorkflowActionsLoaded
12292          *  });
12293         */
12294         _fakeConstuctor: function () {
12295             /* This is here to hide the real init constructor from the public docs */
12296         },
12297         
12298         /**
12299          * @private
12300          * JavaScript representation of a WorkflowActions collection object. Also exposes
12301          * methods to operate on the object against the server.
12302          *
12303          * @param {Object} options
12304          *     An object with the following properties:<ul>
12305          *         <li><b>id:</b> The id of the object being constructed</li>
12306          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12307          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12308          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12309          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12310          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12311          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12312          *             <li><b>content:</b> {String} Raw string of response</li>
12313          *             <li><b>object:</b> {Object} Parsed object of response</li>
12314          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12315          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12316          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12317          *             </ul></li>
12318          *         </ul></li>
12319          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12320          **/
12321         init: function (options) {
12322             this._super(options);           
12323         },
12324 
12325         /**
12326          * @private
12327          * Gets the REST class for the current object - this is the WorkflowActions class.
12328          */
12329         getRestClass: function () {
12330             return WorkflowActions;
12331         },
12332 
12333         /**
12334          * @private
12335          * Gets the REST class for the objects that make up the collection. - this
12336          * is the WorkflowAction class.
12337          */
12338         getRestItemClass: function () {
12339             return WorkflowAction;
12340         },
12341 
12342         /**
12343          * @private
12344          * Gets the REST type for the current object - this is a "WorkflowActions".
12345          */
12346         getRestType: function () {
12347             return "WorkflowActions";
12348         },
12349         
12350         /**
12351          * @private
12352          * Gets the REST type for the objects that make up the collection - this is "WorkflowActions".
12353          */
12354         getRestItemType: function () {
12355             return "WorkflowAction";
12356         },
12357 
12358         /**
12359          * @private
12360          * Override default to indicates that the collection supports making
12361          * requests.
12362          */
12363         supportsRequests: true,
12364 
12365         /**
12366          * @private
12367          * Override default to indicates that the collection subscribes to its objects.
12368          */
12369         supportsRestItemSubscriptions: false,
12370         
12371         /**
12372          * @private
12373          * Retrieve the WorkflowActions.
12374          *
12375          * @returns {finesse.restservices.WorkflowActions}
12376          *     This WorkflowActions object to allow cascading.
12377          */
12378         get: function () {
12379             // set loaded to false so it will rebuild the collection after the get
12380             this._loaded = false;
12381             // reset collection
12382             this._collection = {};
12383             // perform get
12384             this._synchronize();
12385             return this;
12386         }
12387     });
12388 
12389     window.finesse = window.finesse || {};
12390     window.finesse.restservices = window.finesse.restservices || {};
12391     window.finesse.restservices.WorkflowActions = WorkflowActions;
12392         
12393     return WorkflowActions;
12394 });
12395 
12396 /**
12397  * JavaScript representation of the Finesse Workflow object.
12398  *
12399  * @requires finesse.clientservices.ClientServices
12400  * @requires Class
12401  * @requires finesse.FinesseBase
12402  * @requires finesse.restservices.RestBase
12403  */
12404 
12405 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
12406 /*global define,finesse */
12407 
12408 /** @private */
12409 define('restservices/Workflow',[
12410     'restservices/RestBase',
12411     'restservices/WorkflowActions'
12412 ],
12413 function (RestBase, WorkflowActions) {
12414 
12415     var Workflow = RestBase.extend({
12416 
12417         /**
12418          * @class
12419          * JavaScript representation of a Workflow object. Also exposes
12420          * methods to operate on the object against the server.
12421          *
12422          * @param {Object} options
12423          *     An object with the following properties:<ul>
12424          *         <li><b>id:</b> The id of the object being constructed</li>
12425          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12426          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12427          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12428          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12429          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12430          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12431          *             <li><b>content:</b> {String} Raw string of response</li>
12432          *             <li><b>object:</b> {Object} Parsed object of response</li>
12433          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12434          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12435          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12436          *             </ul></li>
12437          *         </ul></li>
12438          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12439          * @constructs
12440          **/
12441         init: function (options) {
12442             this._super(options);
12443         },
12444 
12445         /**
12446          * @private
12447          * Gets the REST class for the current object - this is the Workflow class.
12448          * @returns {Object} The Workflow class.
12449          */
12450         getRestClass: function () {
12451             return Workflow;
12452         },
12453 
12454         /**
12455          * @private
12456          * Gets the REST type for the current object - this is a "Workflow".
12457          * @returns {String} The Workflow string.
12458          */
12459         getRestType: function () {
12460             return "Workflow";
12461         },
12462 
12463         /**
12464          * @private
12465          * Override default to indicate that this object doesn't support making
12466          * requests.
12467          */
12468         supportsRequests: false,
12469 
12470         /**
12471          * @private
12472          * Override default to indicate that this object doesn't support subscriptions.
12473          */
12474         supportsSubscriptions: false,
12475 
12476         /**
12477          * @private
12478          * Getter for the Uri value.
12479          * @returns {String} The Uri.
12480          */
12481         getUri: function () {
12482             this.isLoaded();
12483             return this.getData().uri;
12484         },
12485 
12486         /**
12487          * Getter for the name.
12488          * @returns {String} The name.
12489          */
12490         getName: function () {
12491             this.isLoaded();
12492             return this.getData().name;
12493         },
12494 
12495         /**
12496          * Getter for the description.
12497          * @returns {String} The description.
12498          */
12499         getDescription: function () {
12500             this.isLoaded();
12501             return this.getData().description;
12502         },
12503         
12504         /**
12505          * Getter for the media.
12506          * @returns {String} The media.
12507          */
12508         getMedia: function () {
12509             this.isLoaded();
12510             return this.getData().media;
12511         },
12512 
12513         /**
12514          * Getter for the trigger set.
12515          * @returns {String} The trigger set.
12516          */
12517         getTriggerSet: function () {
12518             this.isLoaded();
12519             return this.getData().TriggerSet;
12520         },
12521 
12522         /**
12523          * Getter for the condition set.
12524          * @returns {String} The condition set.
12525          */
12526         getConditionSet: function () {
12527             this.isLoaded();
12528             return this.getData().ConditionSet;
12529         },
12530         
12531         /**
12532          * Getter for the assigned workflowActions.
12533          * @returns {String} The workflowActions object.
12534          */
12535         getWorkflowActions: function () {
12536             this.isLoaded();
12537             var workflowActions = this.getData().workflowActions;
12538             if (workflowActions === null) {
12539                 workflowActions = "";
12540             }
12541             return workflowActions;
12542         },
12543 
12544         createPutSuccessHandler: function (workflow, contentBody, successHandler) {
12545             return function (rsp) {
12546                 // Update internal structure based on response. Here we
12547                 // inject the contentBody from the PUT request into the
12548                 // rsp.object element to mimic a GET as a way to take
12549                 // advantage of the existing _processResponse method.
12550                 rsp.object = contentBody;
12551                 workflow._processResponse(rsp);
12552 
12553                 //Remove the injected Workflow object before cascading response
12554                 rsp.object = {};
12555 
12556                 //cascade response back to consumer's response handler
12557                 successHandler(rsp);
12558             };
12559         },
12560 
12561         createPostSuccessHandler: function (workflow, contentBody, successHandler) {
12562             return function (rsp) {
12563                 rsp.object = contentBody;
12564                 workflow._processResponse(rsp);
12565 
12566                 //Remove the injected Workflow object before cascading response
12567                 rsp.object = {};
12568 
12569                 //cascade response back to consumer's response handler
12570                 successHandler(rsp);
12571             };
12572         },
12573 
12574         /**
12575          * @private
12576          * Add
12577          */
12578         add: function (newValues, handlers) {
12579             // this.isLoaded();
12580             var contentBody = {};
12581 
12582             contentBody[this.getRestType()] = {
12583             	"media": newValues.media,
12584                 "name": newValues.name,
12585                 "description": newValues.description,
12586                 "TriggerSet" : newValues.TriggerSet,
12587                 "ConditionSet" : newValues.ConditionSet,
12588                 "workflowActions" : newValues.workflowActions
12589             };
12590 
12591             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12592             handlers = handlers || {};
12593 
12594             this.restRequest(this.getRestUrl(), {
12595                 method: 'POST',
12596                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
12597                 error: handlers.error,
12598                 content: contentBody
12599             });
12600 
12601             return this; // Allow cascading
12602         },
12603 
12604         /**
12605          * @private
12606          * Update
12607          */
12608         update: function (newValues, handlers) {
12609             this.isLoaded();
12610             var contentBody = {};
12611 
12612             contentBody[this.getRestType()] = {
12613                 "uri": this.getId(),
12614                 "media": this.getMedia(),
12615                 "name": newValues.name,
12616                 "description": newValues.description,
12617                 "TriggerSet" : newValues.TriggerSet,
12618                 "ConditionSet" : newValues.ConditionSet,
12619                 "workflowActions" : newValues.workflowActions
12620             };
12621 
12622             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12623             handlers = handlers || {};
12624 
12625             this.restRequest(this.getRestUrl(), {
12626                 method: 'PUT',
12627                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
12628                 error: handlers.error,
12629                 content: contentBody
12630             });
12631 
12632             return this; // Allow cascading
12633         },
12634 
12635 
12636         /**
12637          * @private
12638          * Delete
12639          */
12640         "delete": function (handlers) {
12641             this.isLoaded();
12642 
12643             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12644             handlers = handlers || {};
12645 
12646             this.restRequest(this.getRestUrl(), {
12647                 method: 'DELETE',
12648                 success: this.createPutSuccessHandler(this, {}, handlers.success),
12649                 error: handlers.error,
12650                 content: undefined
12651             });
12652 
12653             return this; // Allow cascading
12654         }
12655 
12656 
12657 
12658     });
12659 
12660     window.finesse = window.finesse || {};
12661     window.finesse.restservices = window.finesse.restservices || {};
12662     window.finesse.restservices.Workflow = Workflow;
12663 
12664     return Workflow;
12665 });
12666 
12667 /**
12668 * JavaScript representation of the Finesse workflows collection
12669 * object which contains a list of workflow objects.
12670  *
12671  * @requires finesse.clientservices.ClientServices
12672  * @requires Class
12673  * @requires finesse.FinesseBase
12674  * @requires finesse.restservices.RestBase
12675  * @requires finesse.restservices.Dialog
12676  * @requires finesse.restservices.RestCollectionBase
12677  */
12678 
12679 /** @private */
12680 define('restservices/Workflows',[
12681     'restservices/RestCollectionBase',
12682     'restservices/RestBase',
12683     'restservices/Workflow'
12684 ],
12685 function (RestCollectionBase, RestBase, Workflow) {
12686 
12687     var Workflows = RestCollectionBase.extend({
12688 
12689         /**
12690          * @class
12691          * JavaScript representation of a workflows collection object. Also exposes
12692          * methods to operate on the object against the server.
12693          *
12694          * @param {Object} options
12695          *     An object with the following properties:<ul>
12696          *         <li><b>id:</b> The id of the object being constructed</li>
12697          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12698          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12699          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12700          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12701          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12702          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12703          *             <li><b>content:</b> {String} Raw string of response</li>
12704          *             <li><b>object:</b> {Object} Parsed object of response</li>
12705          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12706          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12707          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12708          *             </ul></li>
12709          *         </ul></li>
12710          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12711          *  @constructs
12712          **/
12713         init: function (options) {
12714         	if(options && options.teamId){
12715             	this.teamId = options.teamId;
12716             }
12717         	
12718             this._super(options);
12719         },
12720         
12721         getRestUrl: function () {
12722         	if(this.teamId){
12723         		return this.getBaseRestUrl() + "/TeamResource/" + this.teamId + "/"+ this.getRestType();
12724         	}
12725         	return this._super();
12726         },
12727 
12728         /**
12729          * @private
12730          * Gets the REST class for the current object - this is the workflows class.
12731          */
12732         getRestClass: function () {
12733             return Workflows;
12734         },
12735 
12736         /**
12737          * @private
12738          * Gets the REST class for the objects that make up the collection. - this
12739          * is the workflow class.
12740          */
12741         getRestItemClass: function () {
12742             return Workflow;
12743         },
12744 
12745         /**
12746          * @private
12747          * Gets the REST type for the current object - this is a "workflows".
12748          */
12749         getRestType: function () {
12750             return "Workflows";
12751         },
12752 
12753         /**
12754          * @private
12755          * Gets the REST type for the objects that make up the collection - this is "workflows".
12756          */
12757         getRestItemType: function () {
12758             return "Workflow";
12759         },
12760 
12761         /**
12762          * @private
12763          * Override default to indicates that the collection supports making requests.
12764          */
12765         supportsRequests: true,
12766 
12767         /**
12768          * @private
12769          * Override default to indicates that the collection does not subscribe to its objects.
12770          */
12771         supportsRestItemSubscriptions: false,
12772 
12773         /**
12774          * @private
12775          * Retrieve the workflows. This call will re-query the server and refresh the collection.
12776          *
12777          * @returns {finesse.restservices.workflows}
12778          *     This workflows object to allow cascading.
12779          */
12780         get: function () {
12781             // set loaded to false so it will rebuild the collection after the get
12782             this._loaded = false;
12783             // reset collection
12784             this._collection = {};
12785             // perform get
12786             this._synchronize();
12787             return this;
12788         }
12789     });
12790 
12791     window.finesse = window.finesse || {};
12792     window.finesse.restservices = window.finesse.restservices || {};
12793     window.finesse.restservices.Workflows = Workflows;
12794         
12795     return Workflows;
12796 });
12797 
12798 /**
12799  * JavaScript representation of the Finesse MediaPropertiesLayout object for the Admin webapp.
12800  * @requires finesse.clientservices.ClientServices
12801  * @requires Class
12802  * @requires finesse.FinesseBase
12803  * @requires finesse.restservices.RestBase
12804  */
12805 
12806 /** The following comment is to prevent jslint errors about 
12807  * using variables before they are defined.
12808  */
12809 /*global finesse*/
12810 
12811 /**
12812  * @class
12813  * JavaScript representation of a MediaPropertiesLayout object for the Admin webapp. Also exposes
12814  * methods to operate on the object against the server.
12815  *
12816  * @constructor
12817  * @param {String} id
12818  *     Not required...
12819  * @param {Object} callbacks
12820  *     An object containing callbacks for instantiation and runtime
12821  * @param {Function} callbacks.onLoad(this)
12822  *     Callback to invoke upon successful instantiation, passes in MediaPropertiesLayout object
12823  * @param {Function} callbacks.onLoadError(rsp)
12824  *     Callback to invoke on instantiation REST request error
12825  *     as passed by finesse.clientservices.ClientServices.ajax()
12826  *     {
12827  *         status: {Number} The HTTP status code returned
12828  *         content: {String} Raw string of response
12829  *         object: {Object} Parsed object of response
12830  *         error: {Object} Wrapped exception that was caught
12831  *         error.errorType: {String} Type of error that was caught
12832  *         error.errorMessage: {String} Message associated with error
12833  *     }
12834  * @param {Function} callbacks.onChange(this)
12835  *     Callback to invoke upon successful update, passes in MediaPropertiesLayout object
12836  * @param {Function} callbacks.onError(rsp)
12837  *     Callback to invoke on update error (refresh or event)
12838  *     as passed by finesse.clientservices.ClientServices.ajax()
12839  *     {
12840  *         status: {Number} The HTTP status code returned
12841  *         content: {String} Raw string of response
12842  *         object: {Object} Parsed object of response
12843  *         error: {Object} Wrapped exception that was caught
12844  *         error.errorType: {String} Type of error that was caught
12845  *         error.errorMessage: {String} Message associated with error
12846  *     }
12847  */
12848 
12849 /** @private */
12850 define('restservices/MediaPropertiesLayout',['restservices/RestBase'], function (RestBase) {
12851     var MediaPropertiesLayout = RestBase.extend(/** @lends finesse.restservices.MediaPropertiesLayout.prototype */{
12852 
12853         /**
12854          * @class
12855          * The MediaPropertiesLayout handles which call variables are associated with Dialogs.
12856          * 
12857          * @augments finesse.restservices.RestBase
12858          * @see finesse.restservices.Dialog#getMediaProperties
12859          * @see finesse.restservices.User#getMediaPropertiesLayout
12860          * @constructs
12861          */
12862         _fakeConstuctor: function () {
12863             /* This is here to hide the real init constructor from the public docs */
12864         },
12865         
12866         /**
12867          * @private
12868          * JavaScript representation of a MediaPropertiesLayout object. Also exposes
12869          * methods to operate on the object against the server.
12870          *
12871          * @param {Object} options
12872          *     An object with the following properties:<ul>
12873          *         <li><b>id:</b> The id of the object being constructed</li>
12874          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12875          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12876          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12877          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12878          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12879          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12880          *             <li><b>content:</b> {String} Raw string of response</li>
12881          *             <li><b>object:</b> {Object} Parsed object of response</li>
12882          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12883          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12884          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12885          *             </ul></li>
12886          *         </ul></li>
12887          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12888          **/
12889         init: function (options) {
12890             this._super(options);
12891         },
12892 
12893         /**
12894          * @private
12895          * Gets the REST class for the current object - this is the MediaPropertiesLayout object.
12896          * @returns {Object} The MediaPropertiesLayout constructor.
12897          */
12898         getRestClass: function () {
12899             return MediaPropertiesLayout;
12900         },
12901 
12902         /**
12903          * @private
12904          * Gets the REST type for the current object - this is a "MediaPropertiesLayout".
12905          * @returns {String} The MediaPropertiesLayout string
12906          */
12907         getRestType: function () {
12908             return "MediaPropertiesLayout";
12909         },
12910 
12911         /**
12912          * @private
12913          * Returns whether this object supports subscriptions
12914          */
12915         supportsSubscriptions: false,
12916 
12917         /**
12918          * Getter for the name.
12919          * @returns {String} The name.
12920          */
12921         getName: function () {
12922             this.isLoaded();
12923             return this._data.name;
12924         },
12925 
12926         /**
12927          * Getter for the description.
12928          * @returns {String} The description.
12929          */
12930         getDescription: function () {
12931             this.isLoaded();
12932             return this._data.description || "";
12933         },
12934 
12935         /**
12936          * Getter for the layout type (should be DEFAULT or CUSTOM).
12937          * @returns {String} The layout type.
12938          */
12939         getType: function () {
12940             this.isLoaded();
12941             return this._data.type || "";
12942         },
12943 
12944         /**
12945          * Retrieve the media properties layout. This call will re-query the server and refresh the layout object.
12946          * @returns {finesse.restservices.MediaPropertiesLayout}
12947          *     This MediaPropertiesLayout object to allow cascading
12948          */
12949         get: function () {
12950             this._synchronize();
12951 
12952             return this; //Allow cascading
12953         },
12954 
12955         /**
12956          * Gets the data for this object.
12957          * 
12958          * Performs safe conversion from raw API data to ensure that the returned layout object
12959          * always has a header with correct entry fields, and exactly two columns with lists of entries.
12960          *
12961          * @returns {finesse.restservices.MediaPropertiesLayout.Object} Data in columns (unless only one defined).
12962          */
12963         getData: function () {
12964 
12965             var layout = this._data, result, _addColumnData;
12966 
12967             result = this.getEmptyData();
12968             result.name = layout.name;
12969             result.description = layout.description;
12970             result.type = layout.type;
12971 
12972             /**
12973              * @private
12974              */
12975             _addColumnData = function (entryData, colIndex) {
12976 
12977                 if (!entryData) {
12978                     //If there's no entry data at all, rewrite entryData to be an empty collection of entries
12979                     entryData = {};
12980                 } else if (entryData.mediaProperty) {
12981                     //If entryData contains the keys for a single entry rather than being a collection of entries,
12982                     //rewrite it to be a collection containing a single entry
12983                     entryData = { "": entryData };
12984                 }
12985 
12986                 //Add each of the entries in the list to the column
12987                 jQuery.each(entryData, function (i, entryData) {
12988 
12989                     //If the entry has no displayName specified, explicitly set it to the empty string
12990                     if (!entryData.displayName) {
12991                         entryData.displayName = "";
12992                     }
12993 
12994                     result.columns[colIndex].push(entryData);
12995 
12996                 });
12997 
12998             };
12999 
13000             //The header should only contain a single entry
13001             if (layout.header && layout.header.entry) {
13002 
13003                 //If the entry has no displayName specified, explicitly set it to the empty string
13004                 if (!layout.header.entry.displayName) {
13005                     layout.header.entry.displayName = "";
13006                 }
13007 
13008                 result.header = layout.header.entry;
13009 
13010             } else {
13011 
13012                 throw "MediaPropertiesLayout.getData() - Header does not contain an entry";
13013 
13014             }
13015 
13016             //If the column object contains an entry object that wasn't part of a list of entries,
13017             //it must be a single right-hand entry object (left-hand entry object would be part of a list.)
13018             //Force the entry object to be the 2nd element in an otherwise-empty list.
13019             if (layout.column && layout.column.entry) {
13020                 layout.column = [
13021                     null,
13022                     { "entry": layout.column.entry }
13023                 ];
13024             }
13025 
13026             if (layout.column && layout.column.length > 0 && layout.column.length <= 2) {
13027 
13028                 //Render left column entries
13029                 if (layout.column[0] && layout.column[0].entry) {
13030                     _addColumnData(layout.column[0].entry, 0);
13031                 }
13032 
13033                 //Render right column entries
13034                 if (layout.column[1] && layout.column[1].entry) {
13035                     _addColumnData(layout.column[1].entry, 1);
13036                 }
13037 
13038             }
13039 
13040             return result;
13041 
13042         },
13043 
13044         /**
13045          * @private
13046          * Empty/template version of getData().
13047          *
13048          * Used by getData(), and by callers of getData() in error cases.
13049          * @returns Empty/template version of getData()
13050          */
13051         getEmptyData: function () {
13052 
13053             return {
13054                 header : {
13055                     displayName: null,
13056                     mediaProperty: null
13057                 },
13058                 columns : [[], []]
13059             };
13060 
13061         },
13062 
13063         /**
13064          * Update the layout of this MediaPropertiesLayout
13065          * @param {Object} layout
13066          *      The object representation of the layout you are setting
13067          * @param {finesse.interfaces.RequestHandlers} handlers
13068          *      An object containing the handlers for the request
13069          * @returns {finesse.restservices.MediaPropertiesLayout}
13070          *      This MediaPropertiesLayout object to allow cascading
13071          * @private
13072          */
13073         update: function (newLayoutObject, handlers) {
13074             var contentBody = {};
13075 
13076             // Make sure type is kept the same
13077             newLayoutObject.type = this.getType();
13078 
13079             contentBody[this.getRestType()] = newLayoutObject;
13080 
13081             //Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
13082             handlers = handlers || {};
13083 
13084             this.restRequest(this.getRestUrl(), {
13085                 method: 'PUT',
13086                 success: handlers.success,
13087                 error: handlers.error,
13088                 content: contentBody
13089             });
13090 
13091             return this; // Allow cascading
13092         },
13093 
13094         /**
13095          * Create a new MediaPropertiesLayout object with the layout passed in
13096          * @param {Object} layout
13097          *      The object representation of the layout you are creating
13098          * @param {finesse.interfaces.RequestHandlers} handlers
13099          *      An object containing the handlers for the request
13100          * @returns {finesse.restservices.MediaPropertiesLayout}
13101          *      This MediaPropertiesLayout object to allow cascading
13102          * @private
13103          */
13104         add: function (layout, handlers) {
13105             var contentBody = {};
13106 
13107             contentBody[this.getRestType()] = layout;
13108 
13109             handlers = handlers || {};
13110 
13111             this.restRequest(this.getRestUrl(), {
13112                 method: 'POST',
13113                 success: handlers.success,
13114                 error: handlers.error,
13115                 content: contentBody
13116             });
13117 
13118             return this; // Allow cascading
13119         },
13120 
13121         /**
13122          * Delete this MediaPropertiesLayout
13123          * @param {finesse.interfaces.RequestHandlers} handlers
13124          *      An object containing the handlers for the request
13125          * @returns {finesse.restservices.MediaPropertiesLayout}
13126          *      This MediaPropertiesLayout object to allow cascading
13127          * @private
13128          */
13129         "delete": function (handlers) {
13130             handlers = handlers || {};
13131 
13132             this.restRequest(this.getRestUrl(), {
13133                 method: 'DELETE',
13134                 success: handlers.success,
13135                 error: handlers.error,
13136                 content: undefined
13137             });
13138 
13139             return this; // Allow cascading
13140         }
13141 
13142     });
13143     
13144     MediaPropertiesLayout.Object = /** @lends finesse.restservices.MediaPropertiesLayout.Object.prototype */ {
13145         /**
13146          * @class Format of MediaPropertiesLayout Object.<br>
13147          * Object { <ul>
13148          *      <li>header : { <ul>
13149          *          <li>dispayName {String} 
13150          *          <li>mediaProperty {String}</ul>}
13151          *      <li>columns : { <ul>
13152          *          <li>[ [] , [] ]
13153          *          </ul>
13154          *      where column arrays consists of the same Object format as header.<br>
13155          *          }</ul>
13156          *      }<br>         
13157          * @constructs
13158          */
13159         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
13160         
13161     };
13162 
13163 	window.finesse = window.finesse || {};
13164     window.finesse.restservices = window.finesse.restservices || {};
13165     window.finesse.restservices.MediaPropertiesLayout = MediaPropertiesLayout;
13166     
13167     return MediaPropertiesLayout;
13168 });
13169 
13170 /**
13171  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
13172  *
13173  * @requires MediaPropertiesLayout
13174  * @requires ClientServices
13175  * @requires finesse.FinesseBase
13176  * @requires finesse.restservices.RestBase
13177  */
13178 
13179 /** The following comment is to prevent jslint errors about 
13180  * using variables before they are defined.
13181  */
13182 /*global finesse*/
13183 
13184 /** @private */
13185 define('restservices/UserMediaPropertiesLayout',['restservices/MediaPropertiesLayout'], function (MediaPropertiesLayout) {
13186      var UserMediaPropertiesLayout = MediaPropertiesLayout.extend(/** @lends finesse.restservices.UserMediaPropertiesLayout.prototype */{
13187 
13188 		/**
13189 		 * @class
13190 		 * JavaScript representation of a UserMediaPropertiesLayout collection object. Also exposes
13191 		 * methods to operate on the object against the server.
13192 		 * 
13193 		 * @param {Object} options
13194 		 * An object with the following properties:<ul>
13195 		 *        <li><b>id:</b> The id of the object being constructed</li>
13196 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13197 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13198 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13199 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13200 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13201 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
13202 		 *            <li><b>content:</b> {String} Raw string of response</li>
13203 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
13204 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13205 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
13206 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
13207 		 *            </ul></li>
13208 		 *        </ul></li>
13209 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
13210 		 * @constructs
13211 		**/
13212 		init: function (options) {
13213 		    this._super(options);
13214 		},
13215 		
13216 		/**
13217 		 * @private
13218 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayout class.
13219 		 */
13220 		getRestClass: function () {
13221 		    return UserMediaPropertiesLayout;
13222 		},
13223 
13224         /**
13225          * Overrides the parent class.  Returns the url for the UserMediaPropertiesLayout resource
13226          */
13227         getRestUrl: function () {
13228             return ("/finesse/api/User/" + this.getId() + "/" + this.getRestType());
13229         },
13230 
13231         /**
13232          * @private
13233          * Override to throw an error because we cannot do an update on the User's
13234          * MediaPropertiesLayout node
13235          */
13236         update: function (layout, handlers) {
13237             throw new Error("update(): Cannot update layout for User's MediaPropertiesLayout");
13238         },
13239 
13240         /**
13241          * @private
13242          * Override to throw an error because we cannot create a new layout on the User's
13243          * MediaPropertiesLayout node
13244          */
13245         add: function (layout, handlers) {
13246             throw new Error("add(): Cannot create a new layout for User's MediaPropertiesLayout");
13247         },
13248 
13249         /**
13250          * @private
13251          * Override to throw an error because we cannot delete the layout on the User's
13252          * MediaPropertiesLayout node
13253          */
13254         "delete": function (layout, handlers) {
13255             throw new Error("delete(): Cannot delete the layout for User's MediaPropertiesLayout");
13256         }
13257 
13258     });
13259 	
13260 	window.finesse = window.finesse || {};
13261     window.finesse.restservices = window.finesse.restservices || {};
13262     window.finesse.restservices.UserMediaPropertiesLayout = UserMediaPropertiesLayout;
13263     
13264     return UserMediaPropertiesLayout;
13265 });
13266 
13267 /**
13268 * JavaScript representation of the Finesse mediaPropertiesLayouts collection
13269 * object which contains a list of mediaPropertiesLayout objects.
13270  *
13271  * @requires finesse.clientservices.ClientServices
13272  * @requires Class
13273  * @requires finesse.FinesseBase
13274  * @requires finesse.restservices.RestBase
13275  * @requires finesse.restservices.Dialog
13276  * @requires finesse.restservices.RestCollectionBase
13277  */
13278 
13279 /** @private */
13280 define('restservices/MediaPropertiesLayouts',[
13281     'restservices/RestCollectionBase',
13282     'restservices/RestBase',
13283     'restservices/MediaPropertiesLayout'
13284 ],
13285 function (RestCollectionBase, RestBase, MediaPropertiesLayout) {
13286 
13287     var MediaPropertiesLayouts = RestCollectionBase.extend({
13288 
13289         /**
13290          * @class
13291          * JavaScript representation of a mediaPropertiesLayouts collection object. Also exposes
13292          * methods to operate on the object against the server.
13293          *
13294          * @param {Object} options
13295          *     An object with the following properties:<ul>
13296          *         <li><b>id:</b> The id of the object being constructed</li>
13297          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13298          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13299          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13300          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13301          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13302          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13303          *             <li><b>content:</b> {String} Raw string of response</li>
13304          *             <li><b>object:</b> {Object} Parsed object of response</li>
13305          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13306          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13307          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13308          *             </ul></li>
13309          *         </ul></li>
13310          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13311          *  @constructs
13312          **/
13313         init: function (options) {
13314         	if(options && options.teamId){
13315             	this.teamId = options.teamId;
13316             }
13317             this._super(options);
13318         },
13319 
13320         getRestUrl: function () {
13321         	if(this.teamId){
13322         		return this.getBaseRestUrl() + "/TeamResource/" + this.teamId + "/"+ this.getRestType();
13323         	}
13324         	return this._super();
13325         },
13326         
13327         /**
13328          * @private
13329          * Gets the REST class for the current object - this is the mediaPropertiesLayouts class.
13330          */
13331         getRestClass: function () {
13332             return MediaPropertiesLayouts;
13333         },
13334 
13335         /**
13336          * @private
13337          * Gets the REST class for the objects that make up the collection. - this
13338          * is the mediaPropertiesLayout class.
13339          */
13340         getRestItemClass: function () {
13341             return MediaPropertiesLayout;
13342         },
13343 
13344         /**
13345          * @private
13346          * Gets the REST type for the current object - this is a "mediaPropertiesLayouts".
13347          */
13348         getRestType: function () {
13349             return "MediaPropertiesLayouts";
13350         },
13351 
13352         /**
13353          * @private
13354          * Gets the REST type for the objects that make up the collection - this is "mediaPropertiesLayouts".
13355          */
13356         getRestItemType: function () {
13357             return "MediaPropertiesLayout";
13358         },
13359 
13360         /**
13361          * @private
13362          * Override default to indicates that the collection supports making requests.
13363          */
13364         supportsRequests: true,
13365 
13366         /**
13367          * @private
13368          * Override default to indicates that the collection does not subscribe to its objects.
13369          */
13370         supportsRestItemSubscriptions: false,
13371 
13372         /**
13373          * @private
13374          * Retrieve the MediaPropertiesLayouts. This call will re-query the server and refresh the collection.
13375          *
13376          * @returns {finesse.restservices.MediaPropertiesLayouts}
13377          *     This MediaPropertiesLayouts object to allow cascading.
13378          */
13379         get: function () {
13380             // set loaded to false so it will rebuild the collection after the get
13381             this._loaded = false;
13382             // reset collection
13383             this._collection = {};
13384             // perform get
13385             this._synchronize();
13386             return this;
13387         }
13388     });
13389 
13390     window.finesse = window.finesse || {};
13391     window.finesse.restservices = window.finesse.restservices || {};
13392     window.finesse.restservices.MediaPropertiesLayouts = MediaPropertiesLayouts;
13393         
13394     return MediaPropertiesLayouts;
13395 });
13396 
13397 /**
13398  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
13399  *
13400  * @requires MediaPropertiesLayout
13401  * @requires ClientServices
13402  * @requires finesse.FinesseBase
13403  * @requires finesse.restservices.RestBase
13404  */
13405 
13406 /** The following comment is to prevent jslint errors about 
13407  * using variables before they are defined.
13408  */
13409 /*global finesse*/
13410 
13411 /** @private */
13412 define('restservices/UserMediaPropertiesLayouts',[
13413 	'restservices/MediaPropertiesLayouts',
13414 	'restservices/UserMediaPropertiesLayout'
13415 ],
13416 function (MediaPropertiesLayouts, UserMediaPropertiesLayout) {
13417      var UserMediaPropertiesLayouts = MediaPropertiesLayouts.extend(/** @lends finesse.restservices.UserMediaPropertiesLayouts.prototype */{
13418 
13419 		/**
13420 		 * @class
13421 		 * JavaScript representation of a UserMediaPropertiesLayouts collection object. Also exposes
13422 		 * methods to operate on the object against the server.
13423 		 * 
13424 		 * @param {Object} options
13425 		 * An object with the following properties:<ul>
13426 		 *        <li><b>id:</b> The id of the object being constructed</li>
13427 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13428 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13429 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13430 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13431 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13432 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
13433 		 *            <li><b>content:</b> {String} Raw string of response</li>
13434 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
13435 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13436 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
13437 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
13438 		 *            </ul></li>
13439 		 *        </ul></li>
13440 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
13441 		 * @constructs
13442 		**/
13443 		init: function (options) {
13444 		    this._super(options);
13445 		},
13446 		
13447 		/**
13448 		 * @private
13449 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayouts class.
13450 		 */
13451 		getRestClass: function () {
13452 		    return UserMediaPropertiesLayouts;
13453 		},
13454 
13455         /**
13456          * @private
13457          * Gets the REST class for the objects that make up the collection. - this
13458          * is the UserMediaPropertiesLayout class.
13459          */
13460 		getRestItemClass: function() {
13461 			return UserMediaPropertiesLayout;
13462 		}
13463     });
13464 	
13465 	window.finesse = window.finesse || {};
13466     window.finesse.restservices = window.finesse.restservices || {};
13467     window.finesse.restservices.UserMediaPropertiesLayouts = UserMediaPropertiesLayouts;
13468     
13469     return UserMediaPropertiesLayouts;
13470 });
13471 
13472 /**
13473  * JavaScript representation of the Finesse Dialog object for non-voice media.
13474  *
13475  * @requires finesse.restservices.DialogBase
13476  */
13477 
13478 /** @private */
13479 define('restservices/MediaDialog',[
13480         'restservices/DialogBase'
13481     ],
13482     function (DialogBase) {
13483         var MediaDialog = DialogBase.extend(/** @lends finesse.restservices.MediaDialog.prototype */{
13484 
13485             /**
13486              * @private
13487              *
13488              * Support requests so that applications can refresh non-voice dialogs when the media channel that the
13489              * dialog belongs to is interrupted. An event is not sent to update a dialog's actions when the media is
13490              * interrupted so a refresh is required so that the application can get an updated set of actions.
13491              */
13492             supportsRequests: true,
13493 
13494             /**
13495              * @class
13496              * A MediaDialog is an attempted connection between or among multiple participants,
13497              * for example, a chat or email.
13498              *
13499              * @augments finesse.restservices.DialogBase
13500              * @constructs
13501              */
13502             _fakeConstuctor: function () {
13503                 /* This is here to hide the real init constructor from the public docs */
13504             },
13505 
13506             /**
13507              * @private
13508              *
13509              * @param {Object} options
13510              *     An object with the following properties:<ul>
13511              *         <li><b>id:</b> The id of the object being constructed</li>
13512              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13513              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13514              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13515              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13516              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13517              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13518              *             <li><b>content:</b> {String} Raw string of response</li>
13519              *             <li><b>object:</b> {Object} Parsed object of response</li>
13520              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13521              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13522              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13523              *             </ul></li>
13524              *         </ul></li>
13525              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
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 MediaDialog class.
13534              * @returns {Object} The Dialog class.
13535              */
13536             getRestClass: function () {
13537                 return MediaDialog;
13538             },
13539 
13540             /**
13541              * Transfers a Media Dialog to the target specified
13542              * @param {String} target script selector
13543              *     The script selector to transfer the dialog.
13544              * @param {finesse.interfaces.RequestHandlers} handlers
13545              *     An object containing the handlers for the request
13546              */
13547             transfer: function(target, handlers) {
13548                 this.setTaskState(MediaDialog.TaskActions.TRANSFER, handlers, target);
13549             },
13550 
13551             /**
13552              * Set the state on a Media Dialog based on the action given.
13553              * @param {finesse.restservices.MediaDialog.TaskActions} action
13554              *     The action string indicating the action to invoke on a Media dialog.
13555              * @param {finesse.interfaces.RequestHandlers} handlers
13556              *     An object containing the handlers for the request
13557              * @param {String} target
13558              *     The target to transfer the dialog.  Pass null if not transfer
13559              *
13560              * @example
13561              *     _mediaDialog.setTaskState(finesse.restservices.MediaDialog.TaskActions.ACCEPT,
13562              *                               {
13563              *                                   success: _handleAcceptSuccess,
13564              *                                   error: _handleAcceptError
13565              *                               },
13566              *                               null);
13567              */
13568             setTaskState: function (state,handlers,target) {
13569                 this.isLoaded();
13570 
13571                 var contentBody = {};
13572                 contentBody[this.getRestType()] = {
13573                     "requestedAction": state,
13574                     "target": target
13575                 };
13576                 // (nonexistent) keys to be read as undefined
13577                 handlers = handlers || {};
13578                 this.restRequest(this.getRestUrl(), {
13579                     method: 'PUT',
13580                     success: handlers.success,
13581                     error: handlers.error,
13582                     content: contentBody
13583                 });
13584                 return this; // Allow cascading
13585             }
13586 
13587         });
13588 
13589         MediaDialog.TaskActions = /** @lends finesse.restservices.MediaDialog.TaskActions.prototype */ {
13590             /**
13591              * Accept an incoming task.
13592              */
13593             ACCEPT: "ACCEPT",
13594             /**
13595              * Start work on a task.
13596              */
13597             START : "START",
13598             /**
13599              * Pause work on an active task.
13600              */
13601             PAUSE: "PAUSE",
13602             /**
13603              * Resume work on a paused task.
13604              */
13605             RESUME : "RESUME",
13606             /**
13607              * Wrap up work for a task.
13608              */
13609             WRAP_UP : "WRAP_UP",
13610             /**
13611              * Transfer task to another target.
13612              */
13613             TRANSFER : "TRANSFER",
13614             /**
13615              * End a task.
13616              */
13617             CLOSE : "CLOSE",
13618             /**
13619              * @class Set of action constants for a Media Dialog.  These should be used for
13620              * {@link finesse.restservices.MediaDialog#setTaskState}.
13621              * @constructs
13622              */
13623             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
13624         };
13625 
13626 
13627 
13628         MediaDialog.States = /** @lends finesse.restservices.MediaDialog.States.prototype */ {
13629             /**
13630              * Indicates that the task has been offered to an agent.
13631              */
13632             OFFERED: "OFFERED",
13633             /**
13634              * Indicates that the user has started work on the task.
13635              */
13636             ACTIVE: "ACTIVE",
13637             /**
13638              * Indicates that the user has paused work on the task.
13639              */
13640             PAUSED: "PAUSED",
13641             /**
13642              * Indicates that the user is wrapping up the task.
13643              */
13644             WRAPPING_UP: "WRAPPING_UP",
13645             /**
13646              * Indicates that the task was interrupted.
13647              */
13648             INTERRUPTED: "INTERRUPTED",
13649             /**
13650              * Indicates that the task has ended.
13651              */
13652             CLOSED: "CLOSED",
13653             /**
13654              * Indicates that the user has accepted the task.
13655              */
13656             ACCEPTED: "ACCEPTED",
13657             /**
13658              * Finesse has recovered a task after a failure. It does not have enough information to build a complete set
13659              * of actions for the task so it only allows the user to end the task.
13660              */
13661             UNKNOWN: "UNKNOWN",
13662             /**
13663              * @class Possible Dialog State constants.
13664              * The State flow of a typical in-bound Dialog is as follows: OFFERED, ACCEPTED, ACTIVE, CLOSED.
13665              * @constructs
13666              */
13667             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
13668         };
13669 
13670         MediaDialog.ParticipantStates = MediaDialog.States;
13671 
13672         window.finesse = window.finesse || {};
13673         window.finesse.restservices = window.finesse.restservices || {};
13674         window.finesse.restservices.MediaDialog = MediaDialog;
13675 
13676 
13677         return MediaDialog;
13678     });
13679 
13680 /* Simple JavaScript Inheritance
13681  * By John Resig http://ejohn.org/
13682  * MIT Licensed.
13683  */
13684 // Inspired by base2 and Prototype
13685 define('restservices/../../thirdparty/Class',[], function () {
13686         var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
13687         // The base Class implementation (does nothing)
13688         /** @private */
13689         Class = function(){};
13690         
13691         // Create a new Class that inherits from this class
13692         /** @private */
13693         Class.extend = function(prop) {
13694           var _super = this.prototype;
13695           
13696           // Instantiate a base class (but only create the instance,
13697           // don't run the init constructor)
13698           initializing = true;
13699           var prototype = new this();
13700           initializing = false;
13701           
13702           // Copy the properties over onto the new prototype
13703           for (var name in prop) {
13704             // Check if we're overwriting an existing function
13705             prototype[name] = typeof prop[name] == "function" && 
13706               typeof _super[name] == "function" && fnTest.test(prop[name]) ?
13707               (function(name, fn){
13708                 return function() {
13709                   var tmp = this._super;
13710                   
13711                   // Add a new ._super() method that is the same method
13712                   // but on the super-class
13713                   this._super = _super[name];
13714                   
13715                   // The method only need to be bound temporarily, so we
13716                   // remove it when we're done executing
13717                   var ret = fn.apply(this, arguments);        
13718                   this._super = tmp;
13719                   
13720                   return ret;
13721                 };
13722               })(name, prop[name]) :
13723               prop[name];
13724           }
13725           
13726           // The dummy class constructor
13727           /** @private */
13728           function Class() {
13729             // All construction is actually done in the init method
13730             if ( !initializing && this.init )
13731               this.init.apply(this, arguments);
13732           }
13733           
13734           // Populate our constructed prototype object
13735           Class.prototype = prototype;
13736           
13737           // Enforce the constructor to be what we expect
13738           Class.prototype.constructor = Class;
13739 
13740           // And make this class extendable
13741           Class.extend = arguments.callee;
13742           
13743           return Class;
13744         };
13745     return Class;
13746 });
13747 
13748 /**
13749  * Class used to establish a Media/Dialogs subscription to be shared by MediaDialogs objects for non-voice
13750  * dialog events.
13751  *
13752  * @requires Class
13753  * @requires finesse.clientservices.ClientServices
13754  * @requires finesse.clientservices.Topics
13755  */
13756 /** @private */
13757 define('restservices/MediaDialogsSubscriptionManager',[
13758         "../../thirdparty/Class",
13759         "clientservices/ClientServices",
13760         "clientservices/Topics"
13761     ],
13762     function (Class, ClientServices, Topics) {
13763         var MediaDialogsSubscriptionManager = Class.extend(/** @lends finesse.restservices.MediaDialogsSubscriptionManager.prototype */{
13764 
13765             /**
13766              * Map used to track the MediaDialogs objects managed by this object.
13767              * @private
13768              */
13769             _mediaDialogsMap: {},
13770 
13771             /**
13772              * The regex used to match the source of BOSH/XMPP events. If an event matches this source, the event will
13773              * be processed by the subscription manager.
13774              * @private
13775              */
13776             _sourceRegEx: null,
13777 
13778             /**
13779              * The subscription ID/handle for Media/Dialogs events.
13780              * @private
13781              */
13782             _subscriptionId: null,
13783 
13784             _fakeConstuctor: function ()
13785             {
13786                 /* This is here to hide the real init constructor from the public docs */
13787             },
13788 
13789             /**
13790              * Create the regex used to match the source of BOSH/XMPP events. If an event matches this source, the event
13791              * will be processed by the subscription manager.
13792              *
13793              * @param {Object} restObj
13794              *     The restObj whose REST URL will be used as the base of the regex.
13795              *
13796              * @returns {RegExp}
13797              *     The regex used to match the source of XMPP events.
13798              * @private
13799              */
13800             _makeSourceRegEx: function(restObj)
13801             {
13802                 return new RegExp("^" + restObj.getRestUrl() + "/Media/[0-9]+/Dialogs$");
13803             },
13804 
13805             /**
13806              * Return the media ID associated with the update.
13807              *
13808              * @param {Object} update
13809              *     The content of the update event.
13810              *
13811              * @returns {String}
13812              *     The media ID associated with the update.
13813              * @private
13814              */
13815             _getMediaIdFromEventUpdate: function(update)
13816             {
13817                 var parts = update.object.Update.source.split("/");
13818                 return parts[MediaDialogsSubscriptionManager.MEDIA_ID_INDEX_IN_SOURCE];
13819             },
13820 
13821             /**
13822              * Handler for update events. This handler forwards the update to the MediaDialogs object associated with
13823              * the media ID carried in the update event.
13824              *
13825              * @param {Object} update
13826              *     The content of the update event.
13827              *
13828              * @private
13829              */
13830             _updateEventHandler: function(update)
13831             {
13832                 var mediaId = this._getMediaIdFromEventUpdate(update),
13833                     mediaDialogs = this._mediaDialogsMap[mediaId];
13834 
13835                 if ( mediaDialogs )
13836                 {
13837                     mediaDialogs._updateEventHandler(mediaDialogs, update);
13838                 }
13839             },
13840 
13841             /**
13842              * Return the media ID associated with the REST update.
13843              *
13844              * @param {Object} update
13845              *     The content of the REST update.
13846              *
13847              * @returns {String}
13848              *     The media ID associated with the update.
13849              * @private
13850              */
13851             _getMediaIdFromRestUpdate: function(update)
13852             {
13853                 return update.object.Update.data.dialog.mediaProperties.mediaId;
13854             },
13855 
13856             /**
13857              * Handler for REST updates. This handler forwards the update to the MediaDialogs object associated with
13858              * the media ID carried in the REST update.
13859              *
13860              * @param {Object} update
13861              *     The content of the REST update.
13862              *
13863              * @private
13864              */
13865             _processRestItemUpdate: function(update)
13866             {
13867                 var mediaId = this._getMediaIdFromRestUpdate(update),
13868                     mediaDialogs = this._mediaDialogsMap[mediaId];
13869 
13870                 if ( mediaDialogs )
13871                 {
13872                     mediaDialogs._processRestItemUpdate(update);
13873                 }
13874             },
13875 
13876             /**
13877              * Utility method to create a callback to be given to OpenAjax to invoke when a message
13878              * is published on the topic of our REST URL (also XEP-0060 node).
13879              * This needs to be its own defined method so that subclasses can have their own implementation.
13880              * @returns {Function} callback(update)
13881              *     The callback to be invoked when an update event is received. This callback will
13882              *     process the update by notifying the MediaDialogs object associated with the media ID in the update.
13883              *
13884              * @private
13885              */
13886             _createPubsubCallback: function ()
13887             {
13888                 var _this = this;
13889                 return function (update) {
13890                     //If the source of the update is our REST URL, this means the collection itself is modified
13891                     if (update.object.Update.source.match(_this._sourceRegEx)) {
13892                         _this._updateEventHandler(update);
13893                     } else {
13894                         //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
13895                         //rest item update of one of our children that was published on our node (OpenAjax topic)
13896                         _this._processRestItemUpdate(update);
13897                     }
13898                 };
13899             },
13900 
13901             /**
13902              * Track the MediaDialogs object so that events and REST updates signalled to this subscription manager
13903              * can be forwarded to the given MediaDialogs object.
13904              * @param {finesse.restservices.MediaDialogs} mediaDialogs MediaDialogs object to be tracked by the
13905              *     subscription manager.
13906              * @private
13907              */
13908             _manage: function(mediaDialogs)
13909             {
13910                 this._mediaDialogsMap[mediaDialogs.getMedia().getMediaId()] = mediaDialogs;
13911             },
13912 
13913             /**
13914              * Stop tracking the MediaDialogs object. Events and REST updates signalled to this subscription manager
13915              * will no longer be forwarded to the given MediaDialogs object.
13916              * @param {finesse.restservices.MediaDialogs} mediaDialogs MediaDialogs object to no longer track.
13917              * @private
13918              */
13919             _unManage: function(mediaDialogs)
13920             {
13921                 var mediaId = mediaDialogs.getMedia().getMediaId();
13922                 if ( this._callbackMap[mediaId] )
13923                 {
13924                     delete this._callbackMap[mediaId];
13925                 }
13926             },
13927 
13928             /**
13929              * @class
13930              * An internal class used to establish a Media/Dialogs subscription to be shared by MediaDialogs objects
13931              * for non-voice dialog events.
13932              *
13933              * @constructor
13934              * @param {RestBase} restObj
13935              *     A RestBase object used to build the user portion of XMPP and REST paths.
13936              * @constructs
13937              * @private
13938              */
13939             init: function (restObj)
13940             {
13941                 var _this;
13942 
13943                 this._sourceRegEx = this._makeSourceRegEx(restObj);
13944             },
13945 
13946             /**
13947              * Create the BOSH/XMPP subscription used for non-voice dialog events. Additionally, store the given
13948              * MediaDialogs object so that events for the object can be forwarded to it.
13949              *
13950              * @param {finesse.restservices.MediaDialogs} mediaDialogs a MediaDialogs object to manage (forward events)
13951              * @param {Object} callbacks an object containing success and error callbacks used to signal the result of
13952              *     the subscription.
13953              * @returns {MediaDialogsSubscriptionManager}
13954              * @private
13955              */
13956             subscribe: function (mediaDialogs, callbacks)
13957             {
13958                 var topic = Topics.getTopic(mediaDialogs.getXMPPNodePath()),
13959                     _this = mediaDialogs,
13960                     handlers,
13961                     successful;
13962 
13963                 callbacks = callbacks || {};
13964 
13965                 handlers = {
13966                     /** @private */
13967                     success: function () {
13968                         // Add item to the refresh list in ClientServices to refresh if
13969                         // we recover due to our resilient connection.
13970                         ClientServices.addToRefreshList(_this);
13971                         if (typeof callbacks.success === "function") {
13972                             callbacks.success();
13973                         }
13974                     },
13975                     /** @private */
13976                     error: function (err) {
13977                         if (successful) {
13978                             _this._unManage(mediaDialogs);
13979                             ClientServices.unsubscribe(topic);
13980                         }
13981 
13982                         if (typeof callbacks.error === "function") {
13983                             callbacks.error(err);
13984                         }
13985                     }
13986                 };
13987 
13988                 this._manage(mediaDialogs);
13989                 if ( this._subscriptionId )
13990                 {
13991                     successful = true;
13992                 }
13993                 else
13994                 {
13995                     successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true);
13996                     if ( successful )
13997                     {
13998                         this._subscriptionId = "OpenAjaxOnly";
13999                     }
14000                 }
14001 
14002                 if (successful) {
14003                     handlers.success();
14004                 } else {
14005                     handlers.error();
14006                 }
14007 
14008                 return this;
14009             }
14010         });
14011 
14012         MediaDialogsSubscriptionManager.MEDIA_ID_INDEX_IN_SOURCE = 6;
14013 
14014         window.finesse = window.finesse || {};
14015         window.finesse.restservices = window.finesse.restservices || {};
14016         window.finesse.restservices.MediaDialogsSubscriptionManager = MediaDialogsSubscriptionManager;
14017 
14018         return MediaDialogsSubscriptionManager;
14019     });
14020 
14021 /**
14022  * JavaScript representation of the Finesse MediaDialogs collection
14023  * object which contains a list of Dialog objects.
14024  *
14025  * @requires finesse.clientservices.ClientServices
14026  * @requires Class
14027  * @requires finesse.FinesseBase
14028  * @requires finesse.restservices.RestBase
14029  * @requires finesse.restservices.Dialogs
14030  * @requires finesse.restservices.MediaDialogsSubscriptionManager
14031  */
14032 /** @private */
14033 define('restservices/MediaDialogs',[
14034     'restservices/RestCollectionBase',
14035     'restservices/RestBase',
14036     'restservices/Dialogs',
14037     'restservices/MediaDialog',
14038     'restservices/MediaDialogsSubscriptionManager'
14039 ],
14040 function (RestCollectionBase, RestBase, Dialogs, MediaDialog, MediaDialogsSubscriptionManager) {
14041     var MediaDialogs = Dialogs.extend(/** @lends finesse.restservices.MediaDialogs.prototype */{
14042 
14043         /**
14044          * @class
14045          * JavaScript representation of a collection of Dialogs for a specific non-voice Media.
14046          * @augments finesse.restservices.Dialogs
14047          * @constructs
14048          * @see finesse.restservices.Dialog
14049          * @example
14050          *  _MediaDialogs = _media.getMediaDialogs( {
14051          *      onCollectionAdd : _handleDialogAdd,
14052          *      onCollectionDelete : _handleDialogDelete,
14053          *      onLoad : _handleMediaDialogsLoaded
14054          *  });
14055          *  
14056          * _dialogCollection = _MediaDialogs.getCollection();
14057          * for (var dialogId in _dialogCollection) {
14058          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
14059          *         _dialog = _dialogCollection[dialogId];
14060          *         etc...
14061          *     }
14062          * }
14063          */
14064         _fakeConstuctor: function () {
14065             /* This is here to hide the real init constructor from the public docs */
14066         },
14067         
14068         /**
14069          * @private
14070          * @param {Object} options
14071          *     An object with the following properties:<ul>
14072          *         <li><b>id:</b> The id of the object being constructed</li>
14073          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14074          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14075          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14076          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14077          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14078          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14079          *             <li><b>content:</b> {String} Raw string of response</li>
14080          *             <li><b>object:</b> {Object} Parsed object of response</li>
14081          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14082          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14083          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14084          *             </ul></li>
14085          *         </ul></li>
14086          *         <li><b>parentObj:</b> The parent object</li></ul>
14087          *         <li><b>mediaObj:</b> The media object</li></ul>
14088          **/
14089         init: function (options) {
14090             this._mediaObj = options.mediaObj;
14091             this._super(options);
14092         },
14093 
14094         /**
14095          * Returns the associated Media object.
14096          * @returns {finesse.restservices.Media} the associated media object.
14097          */
14098         getMedia: function() {
14099             return this._mediaObj;
14100         },
14101 
14102         /**
14103          * @private
14104          * Gets the REST class for the objects that make up the collection. - this
14105          * is the Dialog class.
14106          */
14107         getRestItemClass: function () {
14108             return MediaDialog;
14109         },
14110 
14111         /**
14112          * @private
14113          * Gets the node path for the current object - this is the media node
14114          * @returns {String} The node path
14115          */
14116         getXMPPNodePath: function () {
14117             var
14118                 restObj = this._restObj,
14119                 nodePath = "";
14120 
14121             //Prepend the base REST object if one was provided.
14122             if (restObj instanceof RestBase) {
14123                 nodePath += restObj.getRestUrl();
14124             }
14125             //Otherwise prepend with the default webapp name.
14126             else {
14127                 nodePath += "/finesse/api";
14128             }
14129             
14130             //Append the REST type.
14131             nodePath += "/" + this.getRestType() + "/Media";
14132             return nodePath;
14133         },
14134         
14135         /**
14136          * The REST URL in which this object can be referenced.
14137          * @return {String}
14138          *     The REST URI for this object.
14139          * @private
14140          */
14141         getRestUrl: function () {
14142             var
14143             restObj = this._mediaObj,
14144             restUrl = "";
14145 
14146             //Prepend the base REST object if one was provided.
14147             if (restObj instanceof RestBase) {
14148                 restUrl += restObj.getRestUrl();
14149             }
14150             //Otherwise prepend with the default webapp name.
14151             else {
14152                 restUrl += "/finesse/api";
14153             }
14154 
14155             //Append the REST type.
14156             restUrl += "/" + this.getRestType();
14157 
14158             //Append ID if it is not undefined, null, or empty.
14159             if (this._id) {
14160                 restUrl += "/" + this._id;
14161             }
14162             return restUrl;
14163         },
14164 
14165         /**
14166          * Overridden so that MediaDialogsSubscriptionManager can be used to share events across media dialogs.
14167          *
14168          * @param {Object} callbacks
14169          *     An object containing success and error handlers for the subscription request.
14170          * @private
14171          */
14172         subscribe: function (callbacks)
14173         {
14174             if ( !MediaDialogs.subscriptionManager )
14175             {
14176                 MediaDialogs.subscriptionManager = new MediaDialogsSubscriptionManager(this._restObj);
14177             }
14178 
14179             MediaDialogs.subscriptionManager.subscribe(this, callbacks);
14180 
14181             return this;
14182         }
14183     });
14184 
14185     MediaDialogs.subscriptionManager = /** @lends finesse.restservices.MediaDialogsSubscriptionManager.prototype */ null;
14186 
14187     window.finesse = window.finesse || {};
14188     window.finesse.restservices = window.finesse.restservices || {};
14189     window.finesse.restservices.MediaDialogs = MediaDialogs;
14190     
14191     return MediaDialogs;
14192 });
14193 
14194 /**
14195  * Utility class used to recover a media object after recovering from a connection or system failure.
14196  *
14197  * @requires Class
14198  * @requires finesse.restservices.Notifier
14199  * @requires finesse.clientservices.ClientServices
14200  * @requires finesse.restservices.Media
14201  */
14202 
14203 /** @private */
14204 define('restservices/MediaOptionsHelper',['../../thirdparty/Class',
14205         '../utilities/Utilities',
14206         '../clientservices/ClientServices',
14207         '../cslogger/ClientLogger'
14208 ],
14209 function (Class, Utilities, ClientServices, ClientLogger)
14210 {
14211     /**
14212      * Utility class used to synchronize media login options after recovering from a connection or system failure. This
14213      * class will ensure that the Finesse server that the application fails over to has the same maxDialogLimit,
14214      * interruptAction, and dialogLogoutAction as the previous Finesse server.
14215      */
14216     var MediaOptionsHelper = Class.extend(/** @lends finesse.restservices.MediaOptionsHelper.prototype */
14217     {
14218         /**
14219          * @private
14220          *
14221          * The media that this helper is responsible for recovering in case of failover.
14222          */
14223         _media: null,
14224 
14225         /**
14226          * @private
14227          *
14228          * The media options that this helper will ensure are set properly across failures.
14229          */
14230         _mediaOptions: null,
14231 
14232         /**
14233          * @private
14234          *
14235          * The current state of the failover recovery.
14236          */
14237         _state: null,
14238 
14239         /**
14240          * @class
14241          *
14242          * Utility class used to synchronize media login options after recovering from a connection or system failure. This
14243          * class will ensure that the Finesse server that the application fails over to has the same maxDialogLimit,
14244          * interruptAction, and dialogLogoutAction as the previous Finesse server.
14245          *
14246          * @constructs
14247          */
14248         _fakeConstuctor: function ()
14249         {
14250             /* This is here to hide the real init constructor from the public docs */
14251         },
14252 
14253         /**
14254          * Utility method to format a message logged by an instance of this class.
14255          *
14256          * @param {string} message the message to format
14257          * @returns {string} the given message prefixed with the name of this class and the ID of the Media object
14258          *      associated with this class.
14259          * @private
14260          */
14261         _formatLogMessage: function(message)
14262         {
14263             return "MediaOptionsHelper[" + this.media.getMediaId() + "]: " + message;
14264         },
14265 
14266         /**
14267          * Utility method to log an informational message.
14268          *
14269          * Note that this method piggy-backs on the logger setup by the gadget. If the gadget does not initialize
14270          * logger, this class will not log.
14271          *
14272          * @param {string} message the message to log
14273          * @private
14274          */
14275         _log: function(message)
14276         {
14277             ClientLogger.log(this._formatLogMessage(message));
14278         },
14279 
14280         /**
14281          * Utility method to log an error message.
14282          *
14283          * Note that this method piggy-backs on the logger setup by the gadget. If the gadget does not initialize
14284          * logger, this class will not log.
14285          *
14286          * @param {string} message the message to log
14287          * @private
14288          */
14289         _error: function(message)
14290         {
14291             ClientLogger.error(this._formatLogMessage(message));
14292         },
14293 
14294         /**
14295          * @private
14296          *
14297          * Set the running state of this failover helper.
14298          *
14299          * @param {String} newState the new state of the failover helper.
14300          */
14301         _setState: function(newState)
14302         {
14303             this._state = newState;
14304             this._log("changed state to " + this._state);
14305         },
14306 
14307         /**
14308          * Check the given media object to see if the maxDialogLimit, interruptAction, and dialogLogoutAction options
14309          * need to be reset. These options need to be reset if the application specified login options and any of the
14310          * following conditions are true:<ul>
14311          *     <li>the dialogLogoutAction in the given media object does not match the action set by the application</li>
14312          *     <li>the interruptAction in the given media object does not match the action set by the application</li>
14313          *     <li>the maxDialogLimit in the given media object does not match the limit set by the application</li></ul>
14314          *
14315          * @param {Object} media the media object to evaluate
14316          * @returns {*|{}|boolean} true if a login request should be sent to correct the media options
14317          * @private
14318          */
14319         _shouldLoginToFixOptions: function(media)
14320         {
14321             return this._mediaOptions
14322                 && media.isLoggedIn()
14323                 && (media.getDialogLogoutAction() !== this._mediaOptions.dialogLogoutAction
14324                     || media.getInterruptAction() !== this._mediaOptions.interruptAction
14325                     || media.getMaxDialogLimit() !== this._mediaOptions.maxDialogLimit);
14326         },
14327 
14328         /**
14329          * @private
14330          *
14331          * Determine if the given response is an "agent already logged in" error.
14332          *
14333          * @param {Object} response the response to evaluate
14334          *
14335          * @returns {boolean} true if
14336          */
14337         _agentIsAlreadyLoggedIn: function(response)
14338         {
14339             return response
14340                 && response.object
14341                 && response.object.ApiErrors
14342                 && response.object.ApiErrors.ApiError
14343                 && response.object.ApiErrors.ApiError.ErrorMessage === "E_ARM_STAT_AGENT_ALREADY_LOGGED_IN";
14344         },
14345 
14346         /**
14347          * Determine if the given response to a media login request is successful. A response is successful under these
14348          * conditions:<ul>
14349          *     <li>the response has a 202 status</li>
14350          *     <li>the response has a 400 status and the error indicates the agent is already logged in</li>
14351          *     </ul>
14352          *
14353          * @param {Object} loginResponse the response to evaluate
14354          *
14355          * @returns {*|boolean} true if the response status is 202 or if the response status is 400 and the error states
14356          *      that the agent is already logged in.
14357          * @private
14358          */
14359         _isSuccessfulLoginResponse: function(loginResponse)
14360         {
14361             return loginResponse && ((loginResponse.status === 202) || this._agentIsAlreadyLoggedIn(loginResponse));
14362         },
14363 
14364         /**
14365          * Process a media load or change while in the connected state. This involves checking the media options to
14366          * ensure they are the same as those set by the application.
14367          *
14368          * @param {Object} media the media object that was loaded or changed.
14369          * @private
14370          */
14371         _processConnectedState: function(media)
14372         {
14373             var _this = this, processResponse;
14374 
14375             if ( this._shouldLoginToFixOptions(media) )
14376             {
14377                 processResponse = function(response)
14378                 {
14379                     _this._setState(MediaOptionsHelper.States.MONITORING_OPTIONS);
14380 
14381                     if ( !_this._isSuccessfulLoginResponse(response) )
14382                     {
14383                         _this._error("failed to reset options: " + response.status + ": " + response.content);
14384                     }
14385                 };
14386 
14387                 this._setState(MediaOptionsHelper.States.SETTING_OPTIONS);
14388 
14389                 this._log("logging in to fix options");
14390 
14391                 this.media.login({
14392                     dialogLogoutAction: _this._mediaOptions.dialogLogoutAction,
14393                     interruptAction: _this._mediaOptions.interruptAction,
14394                     maxDialogLimit: _this._mediaOptions.maxDialogLimit,
14395                     handlers: {
14396                         success: processResponse,
14397                         error: processResponse
14398                     }
14399                 });
14400             }
14401         },
14402 
14403         /**
14404          * Process a media load or change while in the resetting options state. All that is done in this state is log a
14405          * message that a reset is already in progress.
14406          *
14407          * @param {Object} media the media object that was loaded or changed.
14408          * @private
14409          */
14410         _processResettingOptionsState: function(media)
14411         {
14412             this._log("Resetting options is in progress");
14413         },
14414 
14415         /**
14416          * Initialize a helper class used to recover media objects following connectivity or component failures related
14417          * to Finesse and/or CCE services.
14418          *
14419          * Initialize the failover helper to manage the recovery of the given media object.
14420          *
14421          * @param {Object} media the media object to recover
14422          * @param {Object} mediaOptions an object containing the media options used by the application:<ul>
14423          *         <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
14424          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
14425          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
14426          */
14427         init: function (media, mediaOptions)
14428         {
14429             var _this = this, processMediaStateChange = function(media)
14430             {
14431                 switch ( _this._state )
14432                 {
14433                     case MediaOptionsHelper.States.MONITORING_OPTIONS:
14434                         _this._processConnectedState(media);
14435                         break;
14436                     case MediaOptionsHelper.States.SETTING_OPTIONS:
14437                         _this._processResettingOptionsState(media);
14438                         break;
14439                     default:
14440                         _this._error("unexpected state: " + _this.state);
14441                         break;
14442                 }
14443             };
14444 
14445             this.media = media;
14446 
14447             this._mediaOptions = mediaOptions || {};
14448 
14449             // The maxDialogLimit is a string in media events. Ensure _mediaOptions.maxDialogLimit is a string to
14450             // make sure it can be compared to the maxDialogLimit field in media events.
14451             //
14452             if ( this._mediaOptions.maxDialogLimit )
14453             {
14454                 this._mediaOptions.maxDialogLimit = this._mediaOptions.maxDialogLimit.toString();
14455             }
14456 
14457             // Add the media object to the refresh list so that ClientServices calls refresh on the media object when
14458             // the connection is reestablished. This will trigger processMediaStateChange() which will trigger a login
14459             // to restore media options if media options are different.
14460             //
14461             ClientServices.addToRefreshList(this.media);
14462 
14463             this._setState(MediaOptionsHelper.States.MONITORING_OPTIONS);
14464 
14465             this.media.addHandler('load', processMediaStateChange);
14466             this.media.addHandler('change', processMediaStateChange);
14467 
14468             this._log("initialized");
14469         }
14470     });
14471 
14472     /**
14473      * @private
14474      *
14475      * The states that a running MediaOptionsHelper executes.
14476      *
14477      * @type {{CONNECTED: string, RECOVERING: string, RECOVERY_LOGIN: string, _fakeConstructor: _fakeConstructor}}
14478      */
14479     MediaOptionsHelper.States = /** @lends finesse.restservices.MediaOptionsHelper.States.prototype */ {
14480 
14481         /**
14482          * The media is synchronized with the Finesse server. Media options are being monitored for changes.
14483          */
14484         MONITORING_OPTIONS: "MONITORING_OPTIONS",
14485 
14486         /**
14487          * A media login request has been sent to Finesse to set the correct media options.
14488          */
14489         SETTING_OPTIONS: "SETTING_OPTIONS",
14490 
14491         /**
14492          * @class Possible MediaOptionsHelper state values.
14493          * @constructs
14494          */
14495         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
14496     };
14497 
14498     window.finesse = window.finesse || {};
14499     window.finesse.restservices = window.finesse.restservices || {};
14500     window.finesse.restservices.MediaOptionsHelper = MediaOptionsHelper;
14501 
14502     return MediaOptionsHelper;
14503 });
14504 /**
14505  * JavaScript representation of the Finesse Media object
14506  *
14507  * @requires finesse.clientservices.ClientServices
14508  * @requires Class
14509  * @requires finesse.FinesseBase
14510  * @requires finesse.restservices.RestBase
14511  * @requires finesse.restservices.MediaDialogs
14512  * @requires finesse.restservices.MediaOptionsHelper
14513  */
14514 
14515 /** @private */
14516 define('restservices/Media',[
14517     'restservices/RestBase',
14518     'restservices/MediaDialogs',
14519     'restservices/MediaOptionsHelper'
14520 ],
14521 function (RestBase, MediaDialogs, MediaOptionsHelper) {
14522     var Media = RestBase.extend(/** @lends finesse.restservices.Media.prototype */{
14523 
14524         /**
14525          * @private
14526          * Media objects support GET REST requests.
14527          */
14528         supportsRequests: true,
14529 
14530         /**
14531          * @private
14532          * The list of dialogs associated with this media.
14533          */
14534         _mdialogs : null,
14535 
14536         /**
14537          * @private
14538          * The options used to log into this media.
14539          */
14540         _mediaOptions: null,
14541 
14542         /**
14543          * @private
14544          * Object used to keep the maxDialogLimit, interruptAction, and dialogLogoutAction in-synch across fail-overs.
14545          */
14546         _optionsHelper: null,
14547 
14548         /**
14549          * @class
14550          * A Media represents a non-voice channel,
14551          * for example, a chat or a email.
14552          *
14553          * @augments finesse.restservices.RestBase
14554          * @constructs
14555          */
14556         _fakeConstuctor: function () {
14557             /* This is here to hide the real init constructor from the public docs */
14558         },
14559 
14560         /**
14561          * @private
14562          *
14563          * @param {Object} options
14564          *     An object with the following properties:<ul>
14565          *         <li><b>id:</b> The id of the object being constructed</li>
14566          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14567          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14568          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14569          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14570          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14571          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14572          *             <li><b>content:</b> {String} Raw string of response</li>
14573          *             <li><b>object:</b> {Object} Parsed object of response</li>
14574          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14575          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14576          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14577          *             </ul></li>
14578          *         </ul></li>
14579          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14580          **/
14581         init: function (options) {
14582             this._super(options);
14583         },
14584 
14585         /**
14586          * @private
14587          * Utility method used to retrieve an attribute from this media object's underlying data.
14588          *
14589          * @param {String} attributeName the name of the attribute to retrieve
14590          * @returns {String} the value of the attribute or undefined if the attribute is not found
14591          */
14592         _getDataAttribute: function(attributeName) {
14593             this.isLoaded();
14594             return this.getData()[attributeName];
14595         },
14596 
14597         /**
14598          * @private
14599          * Gets the REST class for the current object - this is the Media class.
14600          * @returns {Object} The Media class.
14601          */
14602         getRestClass: function () {
14603             return Media;
14604         },
14605         
14606         /**
14607          * @private
14608          * Gets the REST type for the current object - this is a "Media".
14609          * @returns {String} The Media string.
14610          */
14611         getRestType: function () {
14612             return "Media";
14613         },
14614 
14615         /**
14616          * @private
14617          * Getter for the uri.
14618          * @returns {String} The uri.
14619          */
14620         getMediaUri: function () {
14621             return this._getDataAttribute('uri');
14622         },
14623 
14624         /**
14625          * Returns the id.
14626          * @returns {String} The id.
14627          */
14628         getId: function () {
14629             return this._getDataAttribute('id');
14630         },
14631 
14632         /**
14633          * Returns the name.
14634          * @returns {String} The name.
14635          */
14636         getName: function () {
14637             return this._getDataAttribute('name');
14638         },
14639 
14640         /**
14641          * Returns the reason code id.
14642          * @returns {String} The reason code id.
14643          */
14644         getReasonCodeId: function () {
14645             return this._getDataAttribute('reasonCodeId');
14646         },
14647 
14648         /**
14649          * Returns the reason code label.
14650          * @returns {String} The reason code label.
14651          */
14652         getReasonCodeLabel: function() {
14653             this.isLoaded();
14654             if (this.getData().reasonCode) {
14655                 return this.getData.reasonCode.label;
14656             }
14657             return "";
14658         },
14659 
14660         /**
14661          * Returns the action to be taken in the event this media is interrupted. The action will be one of the
14662          * following:<ul>
14663          *     <li><b>ACCEPT:</b> the interrupt will be accepted and the agent will not work on tasks in this media
14664          *     until the media is no longer interrupted.</li>
14665          *     <li><b>IGNORE:</b> the interrupt will be ignored and the agent is allowed to work on the task while the
14666          *     media is interrupted.</li></ul>
14667          * @returns {*|Object}
14668          */
14669         getInterruptAction: function() {
14670             return this._getDataAttribute('interruptAction');
14671         },
14672 
14673         /**
14674          * Returns the action to be taken in the event the agent logs out with dialogs associated with this media.
14675          * The action will be one of the following:<ul>
14676          *     <li><b>CLOSE:</b> the dialog will be closed.</li>
14677          *     <li><b>TRANSFER:</b> the dialog will be transferred to another agent.</li></ul>
14678          * @returns {*|Object}
14679          */
14680         getDialogLogoutAction: function() {
14681             return this._getDataAttribute('dialogLogoutAction');
14682         },
14683 
14684         /**
14685          * Returns the state of the User on this Media.
14686          * @returns {String}
14687          *     The current (or last fetched) state of the User on this Media
14688          * @see finesse.restservices.Media.States
14689          */
14690         getState: function() {
14691             return this._getDataAttribute('state');
14692         },
14693 
14694         /**
14695          * Returns the Media id
14696          * @returns {String} The Media id
14697          */
14698         getMediaId: function() {
14699             return this._getDataAttribute('id');
14700         },
14701 
14702         /**
14703          * Returns maximum number of dialogs allowed on this Media
14704          * @returns {String} The maximum number of Dialogs on this Media
14705          */
14706         getMaxDialogLimit: function() {
14707             return this._getDataAttribute('maxDialogLimit');
14708         },
14709 
14710         /**
14711          * Returns true if this media is interruptible
14712          * @returns {Boolean} true if interruptible; false otherwise
14713          */
14714         getInterruptible: function() {
14715             var interruptible = this._getDataAttribute('interruptible');
14716             return interruptible === 'true';
14717         },
14718 
14719         /**
14720          * Returns true if the user interruptible on this Media.
14721          * @returns {Boolean} true if interruptible; false otherwise
14722          */
14723         isInterruptible: function() {
14724             return this.getInterruptible();
14725         },
14726 
14727         /**
14728          * Returns true if the user is routable on this Media
14729          * @returns {Boolean} true if routable, false otherwise
14730          */
14731         getRoutable: function() {
14732             var routable = this._getDataAttribute('routable');
14733             return routable === 'true';
14734         },
14735 
14736         /**
14737          * Returns true if the user is routable on this Media.
14738          * @returns {Boolean} true if routable, false otherwise
14739          */
14740         isRoutable: function() {
14741             return this.getRoutable();
14742         },
14743 
14744         /**
14745          * Sets the routable status of this media
14746          * .
14747          * @param {Object} options
14748          *     An object with the following properties:<ul>
14749          *         <li><b>routable:</b> true if the agent is routable, false otherwise</li>
14750          *         <li><b>{@link finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
14751          * @returns {finesse.restservices.Media}
14752          *     This Media object, to allow cascading
14753          */
14754         setRoutable: function(params) {
14755             var handlers, contentBody = {},
14756                 restType = this.getRestType(),
14757                 url = this.getRestUrl();
14758             params = params || {};
14759 
14760             contentBody[restType] = {
14761                 "routable": params.routable
14762             };
14763 
14764             handlers = params.handlers || {};
14765 
14766             this._makeRequest(contentBody, url, handlers);
14767 
14768             return this;
14769         },
14770 
14771         /**
14772          * @private
14773          * Invoke a request to the server given a content body and handlers.
14774          *
14775          * @param {Object} contentBody
14776          *     A JS object containing the body of the action request.
14777          * @param {finesse.interfaces.RequestHandlers} handlers
14778          *     An object containing the handlers for the request
14779          */
14780         _makeRequest: function (contentBody, url, handlers) {
14781             // Protect against null dereferencing of options allowing its
14782             // (nonexistent) keys to be read as undefined
14783             handlers = handlers || {};
14784 
14785             this.restRequest(url, {
14786                 method: 'PUT',
14787                 success: handlers.success,
14788                 error: handlers.error,
14789                 content: contentBody
14790             });
14791         },
14792 
14793         /**
14794          * Return true if the params object contains one of the following:<ul>
14795          *     <li>maxDialogLimit</li>
14796          *     <li>interruptAction</li>
14797          *     <li>dialogLogoutAction</li></ul>
14798          *
14799          * @param {Object} params the parameters to evaluate
14800          * @returns {*|Boolean}
14801          * @private
14802          */
14803         _containsLoginOptions: function(params) {
14804             return params.maxDialogLimit || params.interruptAction || params.dialogLogoutAction;
14805         },
14806 
14807         /**
14808          * Create login parameters using the given algorithm:<ul>
14809          *     <li>if loginOptions have not be set in the call to MediaList.getMedia(), use the params given by the application</li>
14810          *     <li>if no params were set by the application, use the loginOptions set in the call to MediaList.getMedia()</li>
14811          *     <li>if the parameters given by the application contains login options, use the parameters given by the application</li>
14812          *     <li>if login options were given by the application but callbacks were given, create new login params with the
14813          *     loginOptions set in the call to MediaList.getMedia() and the callbacks specified in the given login params</li></ul>
14814          *
14815          * @param params the parameters specified by the application in the call to Media.login()
14816          *
14817          * @see finesse.restservices.Media#login
14818          * @see finesse.restservices.MediaList#getMedia
14819          *
14820          * @returns {Object} login parameters built based on the algorithm listed above.
14821          * @private
14822          */
14823         _makeLoginOptions: function(params) {
14824             if ( !this._mediaOptions ) {
14825                 // If loginOptions have not be set, use the params given by the application.
14826                 //
14827                 return params;
14828             }
14829 
14830             if ( !params ) {
14831                 // If there were no params given by the application, use the loginOptions set in the call to
14832                 // MediaList.getMedia().
14833                 //
14834                 return this._mediaOptions;
14835             }
14836 
14837             if (  this._containsLoginOptions(params) ) {
14838                 // if the parameters given by the application contains login options, use the parameters given by the
14839                 // application.
14840                 //
14841                 return params;
14842             }
14843 
14844             // If login options were given by the application but callbacks were given, create new login params with the
14845             // loginOptions set in the call to MediaList.getMedia() and the callbacks specified in the given login
14846             // params.
14847             //
14848             return {
14849                 maxDialogLimit: this._mediaOptions.maxDialogLimit,
14850                 interruptAction: this._mediaOptions.interruptAction,
14851                 dialogLogoutAction: this._mediaOptions.dialogLogoutAction,
14852                 handlers: params.handlers
14853             };
14854         },
14855 
14856         /**
14857          * Ensure that the maxDialogLimit, interruptAction, and dialogLogoutAction options are kept in synch across
14858          * fail-overs for this media object.
14859          * @private
14860          */
14861         _ensureOptionsAreInSynch: function() {
14862             if ( !this._optionsHelper && this._mediaOptions ) {
14863                 this._optionsHelper = new MediaOptionsHelper(this, this._mediaOptions);
14864             }
14865         },
14866 
14867         /**
14868          * Log the agent into this media.
14869          *
14870          * @param {Object} params
14871          *     An object with the following properties:<ul>
14872          *         <li><b>maxDialogLimit:</b>The maximum number of tasks that is allowed to handle concurrently</li>
14873          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
14874          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li>
14875          *         <li><b>{@link finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
14876          *
14877          *     If maxDialogLimit, interruptAction, and dialogLogoutAction are not present, loginOptions specified in the
14878          *     call to finesse.restservices.MediaList.getMedia() will be used.
14879          *
14880          * @see finesse.restservices.MediaList#getMedia
14881          *
14882          * @returns {finesse.restservices.Media}
14883          *     This Media object, to allow cascading
14884          */
14885         login: function(params) {
14886             this.setState(Media.States.LOGIN, null, this._makeLoginOptions(params));
14887             return this; // Allow cascading
14888         },
14889 
14890         /**
14891          * Perform a logout for a user on this media.
14892          * 
14893          * @param {String} reasonCode
14894          *     The reason code for this user to logging out of this media.  Pass null for no reason.
14895          * @param {Object} params
14896          *     An object with the following properties:<ul>
14897          *         <li><b>{@link finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
14898          * 
14899          * @returns {finesse.restservices.Media}
14900          *     This Media object, to allow cascading
14901          */
14902         logout: function(reasonCode, params) {
14903             var state = Media.States.LOGOUT;
14904             return this.setState(state, reasonCode, params);
14905         },
14906 
14907         /**
14908          * Set the state of the user on this Media.
14909          * 
14910          * @param {String} newState
14911          *     The new state to be set.
14912          * @param {ReasonCode} reasonCode
14913          *     The reason code for the state change for this media. Pass null for no reason.
14914          * @param {Object} params
14915          *     An object with the following properties:<ul>
14916          *         <li><b>maxDialogLimit:</b>The maximum number of tasks that is allowed to handle concurrently</li>
14917          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
14918          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li>
14919          *         <li><b>{@link finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
14920          *
14921          * @see finesse.restservices.User.States
14922          * @returns {finesse.restservices.Media}
14923          *     This Media object, to allow cascading
14924          * @example
14925          *     _media.setState(finesse.restservices.Media.States.NOT_READY, 
14926          *         {
14927          *             id: _reasonCodeId
14928          *         },
14929          *         {
14930          *             handlers: {
14931          *                 success: _handleStateChangeSuccess,
14932          *                 error : _handleStateChangeError
14933          *             }
14934          *         });
14935          */
14936         setState: function(state, reasonCode, params) {
14937             var handlers, reasonCodeId, contentBody = {},
14938                 restType = this.getRestType(),
14939                 url = this.getRestUrl();
14940             params = params || {};
14941 
14942             if(reasonCode) {
14943                 reasonCodeId = reasonCode.id;
14944             }
14945 
14946             contentBody[restType] = {
14947                 "state": state,
14948                 "maxDialogLimit": params.maxDialogLimit,
14949                 "interruptAction": params.interruptAction,
14950                 "dialogLogoutAction": params.dialogLogoutAction,
14951                 "reasonCodeId": reasonCodeId
14952             };
14953 
14954             handlers = params.handlers || {};
14955 
14956             this._makeRequest(contentBody, url, handlers);
14957 
14958             return this;
14959         },
14960 
14961         /**
14962          * Returns a MediaDialogs collection object that is associated with User on this Media.
14963          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
14964          * applicable when Object has not been previously created).
14965          * @returns {finesse.restservices.MediaDialogs}
14966          *     A MediaDialogs collection object.
14967          * @example
14968          *  First call:
14969          *      _mediaDialogs = _media.getMediaDialogs({
14970          *                      onLoad : _handleMediaDialogsLoad,
14971          *                      onChange : _handleTeamChange,
14972          *                      onAdd: _handleMediaDialogAdd,
14973          *                      onDelete: _handleMediaDialogDelete,
14974          *                      onError: _errorHandler
14975          *      });
14976          *  Subsequent calls on the same object, after the media dialogs are loaded:
14977          *      ...
14978          *      _mediaDialogsNew = _media.getMediaDialogs();
14979          *      _dialogsCollection = _mediaDialogsNew.getCollection();
14980          *      ...
14981          */
14982         getMediaDialogs: function (callbacks) {
14983             var options = callbacks || {};
14984             options.parentObj = this._restObj;
14985             options.mediaObj = this;
14986             this.isLoaded();
14987 
14988             if (this._mdialogs === null) {
14989                 this._mdialogs = new MediaDialogs(options);
14990             }
14991 
14992             return this._mdialogs;
14993         },
14994 
14995         /**
14996          * Refresh the dialog collection associated with this media.
14997          * The refresh will happen only if the media dialogs have been initialized.
14998          */
14999         refreshMediaDialogs: function() {
15000             if ( this._mdialogs ) {
15001                 this._mdialogs.refresh();
15002             }
15003         },
15004 
15005         /**
15006          * Set the maxDialogLimit, interruptAction, and dialogLogoutAction settings that the application will use for
15007          * this media. In the event of a failure, these options will be set on the new Finesse server.
15008          *
15009          * @param {Object} mediaOptions an object with the following properties:<ul>
15010          *         <li><b>maxDialogLimit:</b>The maximum number of tasks that is allowed to handle concurrently</li>
15011          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
15012          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
15013          */
15014         setMediaOptions: function(mediaOptions) {
15015             if ( mediaOptions ) {
15016                 this._mediaOptions = mediaOptions;
15017                 if ( !this._optionsHelper ) {
15018                     this._optionsHelper = new MediaOptionsHelper(this, this._mediaOptions);
15019                 }
15020             }
15021         },
15022 
15023         /**
15024          * Refresh this media object and optionally refresh the list of media dialogs associated with this object.
15025          *
15026          * @param {Integer} retries the number of times to retry synchronizing this media object.
15027          */
15028         refresh: function(retries) {
15029             retries = retries || 1;
15030             this._synchronize(retries);
15031             this.refreshMediaDialogs();
15032         },
15033 
15034         /**
15035          * Returns true if the user in work state on this Media.
15036          * @returns {boolean} true if the media is in work state; false otherwise
15037          */
15038         isInWorkState: function() {
15039             return this.getState() === Media.States.WORK;
15040         },
15041 
15042         /**
15043          * Returns true if the user in any state except LOGOUT on this Media.
15044          * @returns {boolean} returns true if the agent is in any state except LOGOUT in this media
15045          */
15046         isLoggedIn: function() {
15047          var state = this.getState();
15048          return state && state !== Media.States.LOGOUT;
15049         }
15050     });
15051 
15052     Media.States = /** @lends finesse.restservices.Media.States.prototype */ {
15053         /**
15054          * User Login on a non-voice Media.  Note that while this is an action, is not technically a state, since a
15055          * logged-in User will always be in a specific state (READY, NOT_READY, etc.).
15056          */
15057         LOGIN: "LOGIN",
15058         /**
15059          * User is logged out of this Media.
15060          */
15061         LOGOUT: "LOGOUT",
15062         /**
15063          * User is not ready on this Media.
15064          */
15065         NOT_READY: "NOT_READY",
15066         /**
15067          * User is ready for activity on this Media.
15068          */
15069         READY: "READY",
15070         /**
15071          * User has a dialog coming in, but has not accepted it.
15072          */
15073         RESERVED: "RESERVED",
15074         /**
15075          * The dialogs in this media have been interrupted by a dialog in a non-interruptible media.
15076          */
15077         INTERRUPTED: "INTERRUPTED",
15078         /**
15079          * User enters this state when failing over from one Finesse to the other or when failing over from one
15080          * PG side to the other.
15081          */
15082         WORK: "WORK",
15083         /**
15084          * @class Possible Media state values. Used in finesse.restservices.Media#setState.
15085          * @constructs
15086          */
15087         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15088 
15089     };
15090 
15091     window.finesse = window.finesse || {};
15092     window.finesse.restservices = window.finesse.restservices || {};
15093     window.finesse.restservices.Media = Media;
15094 
15095     return Media;
15096 });
15097 /**
15098  * JavaScript representation of the Finesse Media collection
15099  * object which contains a list of Media objects.
15100  *
15101  * @requires finesse.clientservices.ClientServices
15102  * @requires Class
15103  * @requires finesse.FinesseBase
15104  * @requires finesse.restservices.RestBase
15105  * @requires finesse.restservices.Media
15106  */
15107 /** @private */
15108 define('restservices/MediaList',[
15109         'restservices/RestCollectionBase',
15110         'restservices/Media',
15111         'restservices/RestBase',
15112         'utilities/Utilities'
15113     ],
15114     function (RestCollectionBase, Media, RestBase, Utilities) {
15115         var MediaList = RestCollectionBase.extend(/** @lends finesse.restservices.MediaList.prototype */{
15116 
15117             /**
15118              * @class
15119              * JavaScript representation of a MediaList collection object.
15120              * @augments finesse.restservices.RestCollectionBase
15121              * @constructs
15122              * @see finesse.restservices.Media
15123              * @example
15124              *  mediaList = _user.getMediaList( {
15125              *      onCollectionAdd : _handleMediaAdd,
15126              *      onCollectionDelete : _handleMediaDelete,
15127              *      onLoad : _handleMediaListLoaded
15128              *  });
15129              *
15130              * _mediaCollection = mediaList.getCollection();
15131              * for (var mediaId in _mediaCollection) {
15132              *     if (_mediaCollection.hasOwnProperty(mediaId)) {
15133              *         media = _mediaCollection[mediaId];
15134              *         etc...
15135              *     }
15136              * }
15137              */
15138             _fakeConstuctor: function () {
15139                 /* This is here to hide the real init constructor from the public docs */
15140             },
15141 
15142             /**
15143              * @private
15144              * @param {Object} options
15145              *     An object with the following properties:<ul>
15146              *         <li><b>id:</b> The id of the object being constructed</li>
15147              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
15148              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
15149              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
15150              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
15151              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
15152              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15153              *             <li><b>content:</b> {String} Raw string of response</li>
15154              *             <li><b>object:</b> {Object} Parsed object of response</li>
15155              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15156              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15157              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15158              *             </ul></li>
15159              *         </ul></li>
15160              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15161              **/
15162             init: function (options) {
15163                 this._super(options);
15164             },
15165 
15166             /**
15167              * @private
15168              * Gets the REST class for the current object - this is the MediaList class.
15169              */
15170             getRestClass: function () {
15171                 return MediaList;
15172             },
15173 
15174             /**
15175              * @private
15176              * Gets the REST class for the objects that make up the collection. - this
15177              * is the Media class.
15178              */
15179             getRestItemClass: function () {
15180                 return Media;
15181             },
15182 
15183             /**
15184              * @private
15185              * Gets the REST type for the current object - this is a "MediaList".
15186              */
15187             getRestType: function () {
15188                 return "MediaList";
15189             },
15190 
15191             /**
15192              * @private
15193              * Gets the REST type for the objects that make up the collection - this is "Media".
15194              */
15195             getRestItemType: function () {
15196                 return "Media";
15197             },
15198 
15199             /**
15200              * @private
15201              * Override default to indicates that the collection doesn't support making
15202              * requests.
15203              */
15204             supportsRequests: true,
15205 
15206             /**
15207              * @private
15208              * Override default to indicates that the collection subscribes to its objects.
15209              */
15210             supportsRestItemSubscriptions: true,
15211 
15212             /**
15213              * The REST URL in which this object can be referenced.
15214              * @return {String}
15215              *     The REST URI for this object.
15216              * @private
15217              */
15218             getRestUrl: function () {
15219                 var
15220                     restObj = this._restObj,
15221                     restUrl = "";
15222 
15223                 //Prepend the base REST object if one was provided.
15224                 if (restObj instanceof RestBase) {
15225                     restUrl += restObj.getRestUrl();
15226                 }
15227                 //Otherwise prepend with the default webapp name.
15228                 else {
15229                     restUrl += "/finesse/api";
15230                 }
15231 
15232                 //Append the REST type. (Media not MediaList)
15233                 restUrl += "/" + this.getRestItemType();
15234 
15235                 //Append ID if it is not undefined, null, or empty.
15236                 if (this._id) {
15237                     restUrl += "/" + this._id;
15238                 }
15239 
15240                 return restUrl;
15241             },
15242 
15243             /**
15244              * Returns a specific Media with the id passed, from the MediaList collection.
15245              *  * @param {Object} options
15246              *     An object with the following properties:<ul>
15247              *         <li><b>id:</b> The id of the media to fetch</li>
15248              *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
15249              *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
15250              *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
15251              *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
15252              *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
15253              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15254              *             <li><b>content:</b> {String} Raw string of response</li>
15255              *             <li><b>object:</b> {Object} Parsed object of response</li>
15256              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15257              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15258              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15259              *             </ul></li>
15260              *         </ul></li>
15261              *         <li><b>mediaOptions:</b> {Object} An object with the following properties:<ul>
15262              *             <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
15263              *             <li><b>interruptAction:</b> Accept or ignore interrupts</li>
15264              *             <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
15265              *         </li></ul>
15266              *
15267              * @returns {finesse.restservices.Media}
15268              *     A Media object.
15269              */
15270             getMedia: function (options) {
15271                 this.isLoaded();
15272                 options = options || {};
15273                 var objectId = options.id,
15274                     media = this._collection[objectId];
15275 
15276                 //throw error if media not found
15277                 if(!media) {
15278                     throw new Error("No media found with id: " + objectId);
15279                 }
15280 
15281                 media.addHandler('load', options.onLoad);
15282                 media.addHandler('change', options.onChange);
15283                 media.addHandler('add', options.onAdd);
15284                 media.addHandler('delete', options.onDelete);
15285                 media.addHandler('error', options.onError);
15286 
15287                 media.setMediaOptions(options.mediaOptions);
15288 
15289                 return media;
15290             },
15291 
15292             /**
15293              * Utility method to build the internal collection data structure (object) based on provided data
15294              * @param {Object} data
15295              *     The data to build the internal collection from
15296              * @private
15297              */
15298             _buildCollection: function (data) {
15299                 //overriding this from RestBaseCollection because we need to pass in parentObj
15300                 //because restUrl is finesse/api/User/<useri>/Media/<mediaId>
15301                 //other objects like dialog have finesse/api/Dialog/<dialogId>
15302 
15303                 var i, object, objectId, dataArray;
15304                 if (data && this.getProperty(data, this.getRestItemType()) !== null) {
15305                     dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
15306                     for (i = 0; i < dataArray.length; i += 1) {
15307 
15308                         object = {};
15309                         object[this.getRestItemType()] = dataArray[i];
15310 
15311                         //get id from object.id instead of uri, since uri is not there for some reason
15312                         objectId = object[this.getRestItemType()].id;//this._extractId(object);
15313 
15314                         //create the Media Object
15315                         this._collection[objectId] = new (this.getRestItemClass())({
15316                             id: objectId,
15317                             data: object,
15318                             parentObj: this._restObj
15319                         });
15320                         this.length += 1;
15321                     }
15322                 }
15323             }
15324         });
15325 
15326         window.finesse = window.finesse || {};
15327         window.finesse.restservices = window.finesse.restservices || {};
15328         window.finesse.restservices.MediaList = MediaList;
15329 
15330         return MediaList;
15331     });
15332 
15333 /**
15334 * JavaScript representation of the Finesse ReasonCodes collection
15335 * object which contains a list of ReasonCodes objects.
15336  *
15337  * @requires Class
15338  */
15339 
15340 /** @private */
15341 define('restservices/ReasonCodes',[], function () {
15342 
15343     var ReasonCodes = Class.extend(/** @lends finesse.restservices.ReasonCodes.prototype */{
15344         
15345         /**
15346          * @class
15347          * JavaScript representation of a ReasonCodes collection object. 
15348          * @augments Class
15349          * @constructs
15350          * @example
15351          *  _user.getNotReadyReasonCodes({
15352          *           success: handleNotReadyReasonCodesSuccess,
15353          *           error: handleNotReadyReasonCodesError
15354          *       });
15355          *  
15356          * handleNotReadyReasonCodesSuccess = function(reasonCodes) {
15357          *          for(var i = 0; i < reasonCodes.length; i++) {
15358          *             var reasonCode = reasonCodes[i];
15359          *             var reasonCodeId = reasonCode.id;
15360          *             var reasonCodeLabel = reasonCode.label;
15361          *          }
15362          *      }
15363          * }
15364         */
15365 
15366         _fakeConstuctor: function () {
15367             /* This is here to hide the real init constructor from the public docs */
15368         }
15369         
15370     });
15371  
15372     window.finesse = window.finesse || {};
15373     window.finesse.restservices = window.finesse.restservices || {};
15374     window.finesse.restservices.ReasonCodes = ReasonCodes;
15375        
15376     return ReasonCodes;
15377 });
15378 
15379 /**
15380  * Utility class for fetch all the reason codes on Team. Reason codes result will include all global and system reason codes as well.
15381  *
15382  */
15383 
15384 /** @private */
15385 define('restservices/TeamResource',['restservices/RestBase', 'utilities/Utilities', "clientservices/Topics", "utilities/RestCachingService"], function (RestBase, Utilities, Topics, RestCachingService) {
15386     
15387     var TeamResource = RestBase.extend(/** @lends finesse.restservices.TeamResource.prototype */{
15388         /**
15389          * @private
15390          * Returns whether this object supports subscriptions
15391          */
15392         supportsSubscriptions: false,
15393 
15394         doNotRefresh: true,
15395         
15396         autoSubscribe: false,
15397         
15398         supportsRequests: false,
15399         
15400         teamId: null,
15401         
15402         rcs: RestCachingService,
15403         
15404         /**
15405          * References to ClientServices logger methods
15406          * @private
15407          */
15408         _logger: {
15409             log: finesse.clientservices.ClientServices.log
15410         },
15411       
15412         /**
15413          * JavaScript representation of a TeamResource object. Also exposes methods to operate
15414          * on the object against the server.
15415          *
15416          * @param {Object} options
15417          *     An object with the following properties:<ul>
15418          *         <li><b>teamId:</b> teamId of the user</li>
15419          *        </ul>
15420          * @constructs        
15421          **/
15422         init: function (options){
15423             this._super(options);
15424             if(!options.teamId){
15425             	throw Error("Team Id is mandatory")
15426             }
15427             
15428             this.teamId = options.teamId;
15429         },
15430         
15431         /**
15432          * @private
15433          * Do get disabled.
15434          */
15435         doGet: function(handlers) {
15436             return;
15437         },
15438 
15439         /**
15440          * @private
15441          * Gets the REST class for the current object - this is the TeamResource object.
15442          */
15443         getRestClass: function () {
15444             return TeamResource;
15445         },
15446 
15447         /**
15448          * @private
15449          * Gets the REST type for the current object - this is a "TeamResource".
15450          */
15451         getRestType: function () {
15452             return "TeamResource";
15453         },
15454         
15455         /**
15456          * @private
15457          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
15458          */
15459         getRestItemType: function () {
15460             return "ReasonCodes";
15461         },
15462         
15463         getRestUrl: function () {
15464         	return this.getBaseRestUrl() + "/" + this.getRestType() + "/" + this.teamId + "/"	
15465         },
15466       
15467         /**
15468          * @private
15469          * Parses a uriString to retrieve the id portion
15470          * @param {String} uriString
15471          * @return {String} id
15472          */
15473         _parseIdFromUriString : function (uriString) {
15474             return Utilities.getId(uriString);
15475         },
15476         
15477         /**
15478          * Get reason codes cache from cache. If reason code does not exists in the reason codes cache, then fetch it from Finesse server, for looking up the reason code 
15479          * with its reason code value, and category.
15480          * Note that there is no return value; use the success handler to process a
15481          * valid return.
15482          * 
15483          * @param {finesse.interfaces.RequestHandlers} handlers
15484          *     An object containing the handlers for the request
15485          * @param {String} reasonCodeValue The code for the reason code to lookup
15486          * @param {String} reasonCodeCategory The category for the reason code to lookup.
15487          *                 The possible values are "NOT_READY" and "LOGOUT".
15488          *
15489          * @example
15490          *      new finesse.restservices.TeamResource().lookupReasonCode({
15491          *                                              success: _handleReasonCodeGet,
15492          *                                              error: _handleReasonCodeGetError
15493          *                                              }, '32762', 'NOT_READY');
15494          *      _handleReasonCodeGet(_reasonCode) {
15495          *          var id = _reasonCode.id;
15496          *          var uri = _reasonCode.uri;
15497          *          var label = _reasonCode.label;
15498          *          ...
15499          *      }
15500          * 
15501          */
15502         lookupReasonCode : function (handlers, reasonCodeValue, reasonCodeCategory) {
15503             
15504             /**
15505              * Lookup the reason code from cache if already cached. 
15506              * if not cached fetch the reson codes and cache it.
15507              */ 
15508             this.getReasonCodesByType(reasonCodeCategory, {
15509     			success: function(reasonCodes) {
15510     				handlers.success(reasonCodes.filter(function(rc){
15511     					return rc.code === reasonCodeValue;
15512     				})[0])
15513     			},
15514     			error: function (rsp) {
15515                     handlers.error(rsp);
15516                 },
15517     		}, true);
15518         },
15519         
15520         /**
15521          * Get the reason code by category from reason code cache. reason code cache is empty, then fetch all the reason codes Not_READY, LOGOUT and System Reason codes 
15522          * from server adn cache it in the local memory.
15523          * * @param {String} type, The reason code type "NOT_READY" and "LOGOUT"
15524          * @param {finesse.interfaces.RequestHandlers} handlers
15525          *     An object containing the handlers for the request
15526          * @param {boolean} includeSystemCode should return system code also.
15527          */
15528         getReasonCodesByType : function (type, handlers, includeSystemCode)
15529         {
15530         	 var restType = 'ReasonCodes';
15531         	 var self = this, url = this.getRestUrl() + restType, reasonCodes, i, reasonCodeArray;
15532              var includeSystemCode = includeSystemCode ? true: false;
15533         	 
15534 			    if(this.rcs.isRestCacheServiceReachable() && this.rcs.isCacheable(restType)) {
15535                  var options = {
15536                      key: Topics.getTopic(url),
15537                      onsuccess: function (result) {
15538                         	// check if reason code already exists in the cache.
15539                             if(result){
15540                             	self._logger.log('Getting Reason Codes from cache for category=' + type + ' and Team='+this.teamId);
15541                             	self._reasonCodesSuccessHandler(type, url, result.data.text, handlers, includeSystemCode);
15542                                 return;
15543                             } else {
15544                             	self._makeRestRequest(self, url, this.teamId, type, handlers, includeSystemCode, restType)
15545                             }
15546                      },
15547                      onerror: function (error) {
15548                     	 self._logger.log("Error while fetching reason codes from cache", error);
15549                      }
15550                  }
15551                  finesse.idb.RestCacheDataContainer.fetchData(options);
15552 			    	return;
15553              } else {
15554             	 this._makeRestRequest(self, url, this.teamId, type, handlers, includeSystemCode, restType);
15555              }
15556         },
15557 
15558         _makeRestRequest: function(self,url, teamId, type, handlers, includeSystemCode, restType){
15559         	var contentBody = {};
15560         	self._logger.log('Fetching Reason Codes category=ALL');
15561         	// if reason code does not exists in the cache, fetch from server.
15562         	self.restRequest(url + "?category=ALL", {
15563 	                 method: 'GET',
15564 	                 success: function (rsp) {
15565 	                	self._reasonCodesSuccessHandler(type, url, rsp.content, handlers, includeSystemCode);
15566 	                 },
15567 	                 error: function (rsp) {
15568 	                     handlers.error(rsp);
15569 	                 },
15570 	                 content: contentBody,
15571 	                 restType: restType
15572 	             });
15573         },
15574         
15575         _reasonCodesSuccessHandler: function(type, url, reasonCodesXml, handlers, includeSystemCode) {
15576         	 var reasonCodesRsp = [];
15577         	 var systemReasonCodes = [];
15578         	 var reasonCodesObj = this._util.xml2js(reasonCodesXml);
15579         	 var reasonCodes = [];
15580         	 if(reasonCodesObj && reasonCodesObj.ReasonCodes.ReasonCode){
15581         		 reasonCodes = reasonCodesObj.ReasonCodes.ReasonCode;
15582         	 }
15583              // if there more than one reason codes   
15584         	 if (reasonCodes[0] !== undefined) {
15585                 	
15586                     for (i = 0; i < reasonCodes.length; i = i + 1) {
15587 	                   	 if(type === reasonCodes[i].category){
15588 	                   		 if(reasonCodes[i].systemCode === 'true'){
15589 	                   			systemReasonCodes[i] = this._getResonCodeObject(reasonCodes[i]);
15590 	                   		 } else {
15591 	                   			reasonCodesRsp[i] = this._getResonCodeObject(reasonCodes[i]);
15592 	                   		 }
15593 	                   	 }
15594                     }
15595                 } else {
15596 	               	 if(type === reasonCodes.category){
15597 	               		 if(reasonCodes[i].systemCode === 'true'){
15598 	               			systemReasonCodes[i] =  _getResonCodeObject(reasonCodes);
15599 	               		 }else{
15600 	               			reasonCodesRsp[i] =  _getResonCodeObject(reasonCodes);
15601 	               		 }
15602 	               	 }
15603                 }
15604                 // invoke call back with reason codes. includeSystemCode is true then include system reason codes also
15605                 handlers.success(includeSystemCode? reasonCodesRsp.concat(systemReasonCodes): reasonCodesRsp);
15606         },
15607         _getResonCodeObject: function(reasonCode){
15608         	return  {
15609                 label: reasonCode.label,
15610                 id: Utilities.getId(reasonCode.uri),
15611                 uri: reasonCode.uri,
15612                 code: reasonCode.code
15613             };
15614         }
15615     });
15616     
15617         
15618     window.finesse = window.finesse || {};
15619     window.finesse.restservices = window.finesse.restservices || {};
15620     window.finesse.restservices.TeamResource = TeamResource;
15621     
15622     return TeamResource;
15623 });
15624 
15625 /**
15626  * JavaScript representation of the Finesse User object
15627  *
15628  * @requires finesse.clientservices.ClientServices
15629  * @requires Class
15630  * @requires finesse.FinesseBase
15631  * @requires finesse.restservices.RestBase
15632  */
15633 
15634 /** @private */
15635 define('restservices/User',[
15636     'restservices/RestBase',
15637     'restservices/Dialogs',
15638     'restservices/ClientLog',
15639     'restservices/Queues',
15640     'restservices/WrapUpReasons',
15641     'restservices/PhoneBooks',
15642     'restservices/Workflows',
15643     'restservices/UserMediaPropertiesLayout',
15644     'restservices/UserMediaPropertiesLayouts',
15645     'restservices/Media',
15646     'restservices/MediaList',
15647     'restservices/ReasonCodes',
15648     'utilities/Utilities',
15649     "restservices/TeamResource"
15650 ],
15651 function (RestBase, Dialogs, ClientLog, Queues, WrapUpReasons, PhoneBooks, Workflows, UserMediaPropertiesLayout, UserMediaPropertiesLayouts, Media, MediaList, ReasonCodes, Utilities, TeamResource) {
15652 
15653     var User = RestBase.extend(/** @lends finesse.restservices.User.prototype */{
15654 
15655         _dialogs : null,
15656         _clientLogObj : null,
15657         _wrapUpReasons : null,
15658         _phoneBooks : null,
15659         _workflows : null,
15660         _mediaPropertiesLayout : null,
15661         _mediaPropertiesLayouts : null,
15662         _queues : null,
15663         media : null,
15664         mediaList : null,
15665         _TeamResource: null,
15666 
15667         /**
15668          * @class
15669          * The User represents a Finesse Agent or Supervisor.
15670          *
15671          * @param {Object} options
15672          *     An object with the following properties:<ul>
15673          *         <li><b>id:</b> The id of the object being constructed</li>
15674          *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
15675          *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
15676          *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
15677          *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
15678          *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
15679          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
15680          *             <li><b>content:</b> {String} Raw string of response</li>
15681          *             <li><b>object:</b> {Object} Parsed object of response</li>
15682          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
15683          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
15684          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
15685          *             </ul></li>
15686          *         </ul></li>
15687          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
15688          * @augments finesse.restservices.RestBase
15689          * @constructs
15690          * @example
15691          *      _user = new finesse.restservices.User({
15692          *                      id: _id,
15693          *                      onLoad : _handleUserLoad,
15694          *                      onChange : _handleUserChange
15695          *      });
15696          **/
15697         init: function (options) {
15698             this._super(options);
15699         },
15700 
15701         Callbacks: {},
15702 
15703         /**
15704          * @private
15705          * Gets the REST class for the current object - this is the User object.
15706          * @returns {Object}
15707          *      The User constructor.
15708          */
15709         getRestClass: function () {
15710             return User;
15711         },
15712 
15713         /**
15714         * @private
15715          * Gets the REST type for the current object - this is a "User".
15716          * @returns {String}
15717          *      The User String
15718          */
15719         getRestType: function () {
15720             return "User";
15721         },
15722         /**
15723          * @private
15724          * overloading this to return URI
15725          * @returns {String}
15726          *      The REST URI for this object.
15727          */
15728         getXMPPNodePath: function () {
15729             return this.getRestUrl();
15730         },
15731         /**
15732         * @private
15733          * Returns whether this object supports subscriptions
15734          * @returns {Boolean} True
15735          */
15736         supportsSubscriptions: function () {
15737             return true;
15738         },
15739 
15740         /**
15741          * Getter for the firstName of this User.
15742          * @returns {String}
15743          *     The firstName for this User
15744          */
15745         getFirstName: function () {
15746             this.isLoaded();
15747             return Utilities.convertNullToEmptyString(this.getData().firstName);
15748         },
15749 
15750                                 
15751         /**
15752          * Getter for the reasonCode of this User.
15753          * 
15754          * @returns {Object} The reasonCode for the state of the User<br>
15755          * The contents may include the following:<ul>
15756          *         <li>uri: The URI for the reason code object.
15757          *         <li>id: The unique ID for the reason code.
15758          *         <li>category: The category. Can be either NOT_READY or LOGOUT.
15759          *         <li>code: The numeric reason code value.
15760          *         <li>label: The label for the reason code.
15761          *         <li>forAll: Boolean flag that denotes the global status for the reason code.
15762          *         <li>systemCode: Boolean flag which denotes whether the reason code is system generated or custom one.
15763          *     </ul>
15764          * 
15765          */
15766         getReasonCode : function() {
15767             this.isLoaded();
15768             return this.getData().reasonCode;
15769         },
15770         
15771         /**
15772          * Getter for the pending state reasonCode of this User.
15773          * 
15774          * @returns {Object} The reasonCode for the pending state of the User.<br>
15775          * The contents may include the following:<ul>
15776          *         <li>uri: The URI for the reason code object.
15777          *         <li>id: The unique ID for the reason code.
15778          *         <li>category: The category. Can be either NOT_READY or LOGOUT.
15779          *         <li>code: The numeric reason code value.
15780          *         <li>label: The label for the reason code.
15781          *         <li>forAll: Boolean flag that denotes the global status for the reason code.
15782          *         <li>systemCode: Boolean flag which denotes whether the reason code is system generated or custom one.
15783          *     </ul>
15784          */
15785         getPendingStateReasonCode : function() {
15786             this.isLoaded();
15787             return this.getData().pendingStateReasonCode;
15788         },
15789 
15790                                 
15791         /**
15792          * Getter for the reasonCode of this User.
15793          * 
15794          * @returns {Boolean} True if the reason code for the state of the user
15795          * is a system generated reason code. 
15796          */
15797         isReasonCodeReserved : function() {
15798             var resonCode = this.getReasonCode();
15799             if (resonCode) {
15800                 return resonCode.systemCode === "true" ? true
15801                         : false;
15802             }
15803             return false;
15804         },
15805 
15806 
15807         /**
15808          * Getter for the lastName of this User.
15809          * @returns {String}
15810          *     The lastName for this User
15811          */
15812         getLastName: function () {
15813             this.isLoaded();
15814             return Utilities.convertNullToEmptyString(this.getData().lastName);
15815         },
15816 
15817         /**
15818          * Getter for the full name of this User.
15819          * The full name is of the format "FirstName LastName" (for example: "John Doe")
15820          * 
15821          * @returns {String}
15822          *     The full name of this User.
15823          */
15824         getFullName : function() {
15825             this.isLoaded();
15826 
15827             /*
15828              * Currently, the expected format is "FirstName LastName", but can differ later
15829              * to something "LastName, FirstName".
15830              * To accommodate such, we use formatString utility method so that, if required,
15831              * the same can be achieved just by flipping the format place holders as follows:
15832              * "{1}, {0}"
15833              * Also, the function could be enhanced to take the format parameter.
15834              */
15835             var fmt = "{0} {1}";
15836             return Utilities.formatString(fmt,
15837                 Utilities.convertNullToEmptyString(this.getData().firstName),
15838                 Utilities.convertNullToEmptyString(this.getData().lastName));
15839         },
15840 
15841         /**
15842          * Getter for the extension of this User.
15843          * @returns {String}
15844          *     The extension, if any, of this User
15845          */
15846         getExtension: function () {
15847             this.isLoaded();
15848             return Utilities.convertNullToEmptyString(this.getData().extension);
15849         },
15850 
15851         /**
15852          * Getter for the id of the Team of this User
15853          * @returns {String}
15854          *     The current (or last fetched) id of the Team of this User
15855          */
15856         getTeamId: function () {
15857             this.isLoaded();
15858             return this.getData().teamId;
15859         },
15860 
15861         /**
15862          * Getter for the name of the Team of this User
15863          * @returns {String}
15864          *     The current (or last fetched) name of the Team of this User
15865          */
15866         getTeamName: function () {
15867             this.isLoaded();
15868             return this.getData().teamName;
15869         },
15870 
15871         /**
15872          * Is user an agent?
15873          * @returns {Boolean} True if user has role of agent, else false.
15874          */
15875         hasAgentRole: function () {
15876             this.isLoaded();
15877             return this.hasRole("Agent");
15878         },
15879 
15880         /**
15881          * Is user a supervisor?
15882          * @returns {Boolean} True if user has role of supervisor, else false.
15883          */
15884         hasSupervisorRole: function () {
15885             this.isLoaded();
15886             return this.hasRole("Supervisor");
15887         },
15888 
15889         /**
15890          * @private
15891          * Checks to see if user has "theRole"
15892          * @returns {Boolean} True if "theRole" has the role of supervisor or agent, else false.
15893          */
15894         hasRole: function (theRole) {
15895             this.isLoaded();
15896             var result = false, i, roles, len;
15897 
15898             roles = this.getData().roles.role;
15899             len = roles.length;
15900             if (typeof roles === 'string') {
15901                 if (roles === theRole) {
15902                     result = true;
15903                 }
15904             } else {
15905                 for (i = 0; i < len ; i = i + 1) {
15906                     if (roles[i] === theRole) {
15907                         result = true;
15908                         break;
15909                     }
15910                 }
15911             }
15912 
15913             return result;
15914         },
15915 
15916         /**
15917          * Getter for the pending state of this User.
15918          * @returns {String}
15919          *     The pending state of this User
15920          * @see finesse.restservices.User.States
15921          */
15922         getPendingState: function () {
15923             this.isLoaded();
15924             return Utilities.convertNullToEmptyString(this.getData().pendingState);
15925         },
15926         
15927 
15928         /**
15929          * Getter for the work mode timer for the user
15930          * @returns {String}
15931          *     The WrapUpTimer for the user
15932          * @since 12.0.1
15933          */
15934         getWrapUpTimer: function () {
15935             this.isLoaded();
15936             return this.getData().wrapUpTimer;
15937         },
15938 
15939         /**
15940          * Getter for the state of this User.
15941          * @returns {String}
15942          *     The current (or last fetched) state of this User
15943          * @see finesse.restservices.User.States
15944          */
15945         getState: function () {
15946             this.isLoaded();
15947             return this.getData().state;
15948         },
15949         
15950         /**
15951          * Getter for the media state of this User.
15952          * @returns {String}
15953          *     The current (or last fetched) media state of this User
15954          *     Will be applicable only in CCX deployments
15955          *     When the agent is talking on a manual outbound call, it returns busy 
15956          *     The value will not be present in other cases.
15957          */
15958         getMediaState: function () {
15959             this.isLoaded();
15960             /* There is an assertion that the value should not be undefined while setting the value in datastore. Hence setting it to null*/
15961             if(this.getData().mediaState === undefined) {
15962                   this.getData().mediaState = null;
15963             }
15964             
15965             return this.getData().mediaState;
15966          },
15967         
15968         /**
15969          * Getter for the state change time of this User.
15970          * @returns {String}
15971          *     The state change time of this User
15972          */
15973         getStateChangeTime: function () {
15974             this.isLoaded();
15975             return this.getData().stateChangeTime;
15976         },
15977 
15978         /**
15979          * Getter for the wrap-up mode of this User for incoming calls.
15980          * @returns {String} The wrap-up mode of this user that is a value of {@link finesse.restservices.User.WrapUpMode}
15981          * @see finesse.restservices.User.WrapUpMode
15982          */
15983         getWrapUpOnIncoming: function () {
15984             this.isLoaded();
15985             return this.getData().settings.wrapUpOnIncoming;
15986         },
15987         
15988         /**
15989          * Getter for the wrap-up mode of this User for outgoing calls.
15990          * @returns {String} The wrap-up mode of this user that is a value of {@link finesse.restservices.User.WrapUpMode}
15991          * @see finesse.restservices.User.WrapUpMode
15992          */
15993         getWrapUpOnOutgoing: function () {
15994             this.isLoaded();
15995             return this.getData().settings.wrapUpOnOutgoing;
15996         },
15997 
15998 
15999         /**
16000          * Is User required to go into wrap-up?
16001          * @return {Boolean}
16002          *      True if this agent is required to go into wrap-up.
16003          * @see finesse.restservices.User.WrapUpMode
16004          */
16005         isWrapUpRequired: function () {
16006             return (this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED ||
16007                     this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED_WITH_WRAP_UP_DATA);
16008         },
16009 
16010         /**
16011          * Checks to see if the user is considered a mobile agent by checking for
16012          * the existence of the mobileAgent node.
16013          * @returns {Boolean}
16014          *      True if this agent is a mobile agent.
16015          */
16016         isMobileAgent: function () {
16017             this.isLoaded();
16018             var ma = this.getData().mobileAgent;
16019             return ma !== null && typeof ma === "object";
16020         },
16021 
16022         /**
16023          * Getter for the mobile agent work mode.
16024          * @returns {finesse.restservices.User.WorkMode}
16025          *      If available, return the mobile agent work mode, otherwise null.
16026          * @see finesse.restservices.User.WorkMode
16027          */
16028         getMobileAgentMode: function () {
16029             this.isLoaded();
16030             if (this.isMobileAgent()) {
16031                 return this.getData().mobileAgent.mode;
16032             }
16033             return null;
16034         },
16035 
16036         /**
16037          * Getter for the mobile agent dial number.
16038          * @returns {String}
16039          *      If available, return the mobile agent dial number, otherwise null.
16040          */
16041         getMobileAgentDialNumber: function () {
16042             this.isLoaded();
16043             if (this.isMobileAgent()) {
16044                 return this.getData().mobileAgent.dialNumber;
16045             }
16046             return null;
16047         },
16048 
16049         /**
16050          * Getter for a Dialogs collection object that is associated with User.
16051          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
16052          * applicable when Object has not been previously created).
16053          * @returns {finesse.restservices.Dialogs}
16054          *     A Dialogs collection object.
16055          * @see finesse.restservices.Dialogs
16056          */
16057         getDialogs: function (callbacks) {
16058             var options = callbacks || {};
16059             options.parentObj = this;
16060             this.isLoaded();
16061 
16062             if (this._dialogs === null) {
16063                 this._dialogs = new Dialogs(options);
16064             }
16065 
16066             return this._dialogs;
16067         },
16068         
16069         /**
16070          * Getter for a Dialogs collection object that is associated with User.This will always query from server
16071          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers 
16072          * @returns {finesse.restservices.Dialogs}
16073          *     A Dialogs collection object.
16074          * @see finesse.restservices.Dialogs
16075          */
16076         getDialogsNoCache: function (callbacks) {
16077             var options = callbacks || {};
16078             options.parentObj = this;
16079             this.isLoaded();
16080             this._dialogs = new Dialogs(options);
16081 
16082             return this._dialogs;
16083         },
16084 
16085         /**
16086          * Getter for Media collection object that is associated with User.
16087          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
16088          * applicable when Object has not been previously created).
16089          * @returns {finesse.restservices.MediaList}
16090          *     A Media Dialogs collection object.
16091          * @see finesse.restservices.MediaList
16092          */
16093         getMediaList: function (callbacks) {
16094             var options = callbacks || {};
16095             options.parentObj = this;
16096             this.isLoaded();
16097 
16098             if (this.mediaList === null) {
16099                 this.mediaList = new MediaList(options);
16100             }
16101 
16102             return this.mediaList;
16103         },
16104 
16105         /**
16106          * @private
16107          * Getter for a ClientLog object that is associated with User.
16108          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
16109          * applicable when Object has not been previously created).
16110          * @returns {finesse.restservices.ClientLog}
16111          *     A ClientLog collection object.
16112          * @see finesse.restservices.ClientLog
16113          */
16114         getClientLog: function (callbacks) {
16115             var options = callbacks || {};
16116             options.parentObj = this;
16117             this.isLoaded();
16118            
16119             if (this._clientLogObj === null) {
16120                 this._clientLogObj = new ClientLog(options);
16121             }
16122             else {
16123                 if(options.onLoad && typeof options.onLoad === "function") {
16124                 options.onLoad(this._clientLogObj);
16125                 }
16126             }
16127             return this._clientLogObj;
16128         },
16129        
16130         /**
16131          * Getter for a Queues collection object that is associated with User.
16132          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
16133          * applicable when Object has not been previously created).
16134          * @returns {finesse.restservices.Queues}
16135          *     A Queues collection object.
16136          * @see finesse.restservices.Queues
16137          */
16138         getQueues: function (callbacks) {
16139             var options = callbacks || {};
16140             options.parentObj = this;
16141             this.isLoaded();
16142     
16143             if (this._queues === null) {
16144                 this._queues = new Queues(options);
16145             }
16146     
16147             return this._queues;
16148         },
16149 
16150         /**
16151          * Getter for a WrapUpReasons collection object that is associated with User.
16152          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
16153          * applicable when Object has not been previously created).
16154          * @returns {finesse.restservices.WrapUpReasons}
16155          *     A WrapUpReasons collection object.
16156          * @see finesse.restservices.WrapUpReasons
16157          */
16158         getWrapUpReasons: function (callbacks) {
16159             var options = callbacks || {};
16160             options.parentObj = this;
16161             options.teamId = this.getTeamId();
16162             this.isLoaded();
16163     
16164             if (this._wrapUpReasons === null) {
16165                 this._wrapUpReasons = new WrapUpReasons(options);
16166             }
16167     
16168             return this._wrapUpReasons;
16169         },
16170 
16171         /**
16172          * Getter for a PhoneBooks collection object that is associated with User.
16173          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
16174          * applicable when Object has not been previously created).
16175          * @returns {finesse.restservices.PhoneBooks}
16176          *     A PhoneBooks collection object.
16177          * @see finesse.restservices.PhoneBooks
16178          */
16179         getPhoneBooks: function (callbacks) {
16180             var options = callbacks || {};
16181             options.parentObj = this;
16182             options.teamId = this.getTeamId();
16183             this.isLoaded();
16184     
16185             if (this._phoneBooks === null) {
16186                 this._phoneBooks = new PhoneBooks(options);
16187             }
16188     
16189             return this._phoneBooks;
16190         },
16191 
16192         /**
16193          * @private
16194          * Loads the Workflows collection object that is associated with User and
16195          * 'returns' them to the caller via the handlers.
16196          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
16197          * applicable when Object has not been previously created).
16198          * @see finesse.restservices.Workflow
16199          * @see finesse.restservices.Workflows
16200          * @see finesse.restservices.RestCollectionBase
16201          */
16202         loadWorkflows: function (callbacks) {
16203             var options = callbacks || {};
16204             options.parentObj = this;
16205             options.teamId = this.getTeamId();
16206             this.isLoaded();
16207 
16208             if (this._workflows === null) {
16209                 this._workflows = new Workflows(options);
16210             } else {
16211                 this._workflows.refresh();
16212             }
16213 
16214         },
16215 
16216         /**
16217          * Getter for a UserMediaPropertiesLayout object that is associated with User.
16218          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
16219          * applicable when Object has not been previously created).
16220          * @returns {finesse.restservices.UserMediaPropertiesLayout}
16221          *     The UserMediaPropertiesLayout object associated with this user
16222          * @see finesse.restservices.UserMediaPropertiesLayout
16223          */
16224         getMediaPropertiesLayout: function (callbacks) {
16225             var options = callbacks || {};
16226             options.parentObj = this;
16227             options.id = this._id;
16228     
16229             this.isLoaded();
16230             if (this._mediaPropertiesLayout === null) {
16231                 this._mediaPropertiesLayout = new UserMediaPropertiesLayout(options);
16232             }
16233             return this._mediaPropertiesLayout;
16234         },
16235 
16236         /**
16237          * Getter for a UserMediaPropertiesLayouts object that is associated with User.
16238          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
16239          * applicable when Object has not been previously created).
16240          * @returns {finesse.restservices.UserMediaPropertiesLayout}
16241          *     The UserMediaPropertiesLayout object associated with this user
16242          * @see finesse.restservices.UserMediaPropertiesLayout
16243          */
16244         getMediaPropertiesLayouts: function (callbacks) {
16245             var options = callbacks || {};
16246             options.parentObj = this;
16247             options.teamId = this.getTeamId();
16248             this.isLoaded();
16249             if (this._mediaPropertiesLayouts === null) {
16250                 this._mediaPropertiesLayouts = new UserMediaPropertiesLayouts(options);
16251             }
16252             return this._mediaPropertiesLayouts;
16253         },
16254     
16255         /**
16256          * Getter for the Teams managed by this User(Supervisor), if any.
16257          * 
16258          * @returns {Array} of objects containing id, name, uri of the Teams managed by this User(Supervisor).<br>
16259          * The object content includes the following:<ul>
16260          * 		<li>id: The unique ID for the team.
16261          * 		<li>name: The team name for the team.
16262          * 		<li>uri: The URI for the team.
16263          * </ul>
16264          * 
16265          */
16266         getSupervisedTeams: function () {
16267             this.isLoaded();
16268     
16269             try {
16270                 return Utilities.getArray(this.getData().teams.Team);
16271             } catch (e) {
16272                 return [];
16273             }
16274     
16275         },
16276     
16277         /**
16278          * Perform an agent login for this user, associating him with the
16279          * specified extension.
16280          * @param {Object} params
16281          *     An object containing properties for agent login.
16282          * @param {String} params.reasonCodeId 
16283          *     The reason code id associated with this login
16284          * @param {String} params.extension
16285          *     The extension to associate with this user
16286          * @param {Object} [params.mobileAgent]
16287          *     A mobile agent object containing the mode and dial number properties.
16288          * @param {finesse.interfaces.RequestHandlers} params.handlers
16289          * @see finesse.interfaces.RequestHandlers
16290          * @returns {finesse.restservices.User}
16291          *     This User object, to allow cascading
16292          * @private
16293          */
16294         _login: function (params) {
16295             var handlers, contentBody = {},
16296             restType = this.getRestType();
16297             
16298             // Protect against null dereferencing.
16299             params = params || {};
16300     
16301             contentBody[restType] = {
16302                 "state": User.States.LOGIN,
16303                 "extension": params.extension
16304             };
16305             
16306             if(params.reasonCodeId){
16307                  contentBody[restType].reasonCodeId = params.reasonCodeId
16308             }
16309     
16310             // Create mobile agent node if available.
16311             if (typeof params.mobileAgent === "object") {
16312                 contentBody[restType].mobileAgent = {
16313                     "mode": params.mobileAgent.mode,
16314                     "dialNumber": params.mobileAgent.dialNumber
16315                 };
16316             }
16317     
16318             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16319             handlers = params.handlers || {};
16320     
16321             this.restRequest(this.getRestUrl(), {
16322                 method: 'PUT',
16323                 success: handlers.success,
16324                 error: handlers.error,
16325                 content: contentBody
16326             });
16327     
16328             return this; // Allow cascading
16329         },
16330     
16331         /**
16332          * Perform an agent login for this user, associating him with the
16333          * specified extension.
16334          * @param {String} extension
16335          *     The extension to associate with this user
16336          * @param {finesse.interfaces.RequestHandlers} handlers
16337          *     An object containing the handlers for the request
16338          * @returns {finesse.restservices.User}
16339          *     This User object, to allow cascading
16340          */
16341         login: function (extension, handlers) {
16342             this.isLoaded();
16343             var params = {
16344                 "extension": extension,
16345                 "handlers": handlers
16346             };
16347             return this._login(params);
16348         },
16349         
16350         
16351 
16352     
16353         /**
16354          * Perform an agent login for this user, associating him with the
16355          * specified extension.
16356          * @param {String} extension
16357          *     The extension to associate with this user
16358          * @param {String} mode
16359          *     The mobile agent work mode as defined in finesse.restservices.User.WorkMode.
16360          * @param {String} extension
16361          *     The external dial number desired to be used by the mobile agent.
16362          * @param {finesse.interfaces.RequestHandlers} handlers
16363          *     An object containing the handlers for the request
16364          * @param {Object} reasonCode
16365          *     An object containing the reasonCode for the login request
16366          * @returns {finesse.restservices.User}
16367          *     This User object, to allow cascading
16368          */
16369         loginMobileAgent: function (extension, mode, dialNumber, handlers, reasonCode) {
16370             this.isLoaded();
16371             
16372             var params = {
16373                 "extension": extension,
16374                 "mobileAgent": {
16375                     "mode": mode,
16376                     "dialNumber": dialNumber
16377                 },
16378                 "handlers": handlers
16379             };
16380             //US303866 - reasonCode added for restoring MobileAgent after CTI client disconnect
16381             if(reasonCode) {
16382                 params.reasonCodeId = reasonCode.id;
16383             }
16384             return this._login(params);
16385         },
16386         
16387         
16388         _updateMobileAgent: function (params) {
16389             var handlers, contentBody = {},
16390             restType = this.getRestType();
16391 
16392             params = params || {};
16393 
16394             contentBody[restType] = {
16395             };
16396 
16397             if (typeof params.mobileAgent === "object") {
16398                 contentBody[restType].mobileAgent = {
16399                     "mode": params.mobileAgent.mode,
16400                     "dialNumber": params.mobileAgent.dialNumber
16401                 };
16402             }
16403 
16404             handlers = params.handlers || {};
16405 
16406             this.restRequest(this.getRestUrl(), {
16407                 method: 'PUT',
16408                 success: handlers.success,
16409                 error: handlers.error,
16410                 content: contentBody
16411             });
16412 
16413             return this;
16414         },
16415         
16416         /**
16417          * Update user object in Finesse with agent's mobile login information (Refer defect CSCvc35407)
16418          * @param {String} mode
16419          *      The mobile agent work mode as defined in finesse.restservices.User.WorkMode.
16420          * @param {String} dialNumber
16421          *      The external dial number desired to be used by the mobile agent.
16422          * @param {finesse.interfaces.RequestHandlers} handlers
16423          *     An object containing the handlers for the request
16424          * @returns {finesse.restservices.User}
16425          *     This User object, to allow cascading
16426          */
16427         updateToMobileAgent: function (mode, dialNumber, handlers) {
16428             this.isLoaded();
16429             var params = {
16430                 "mobileAgent": {
16431                     "mode": mode,
16432                     "dialNumber": dialNumber
16433                 },
16434                 "handlers": handlers
16435             };
16436             return this._updateMobileAgent(params);
16437         },
16438     
16439         /**
16440          * Perform an agent logout for this user.
16441          * @param {String} reasonCode
16442          *     The reason this user is logging out.  Pass null for no reason.
16443          * @param {finesse.interfaces.RequestHandlers} handlers
16444          *     An object containing the handlers for the request
16445          * @returns {finesse.restservices.User}
16446          *     This User object, to allow cascading
16447          */
16448         logout: function (reasonCode, handlers) {
16449             return this.setState("LOGOUT", reasonCode, handlers);
16450         },
16451     
16452         /**
16453          * Set the state of the user.
16454          * @param {String} newState
16455          *     The state you are setting
16456          * @param {ReasonCode} reasonCode
16457          *     The reason this user is logging out.  Pass null for no reason.
16458          * @param {finesse.interfaces.RequestHandlers} handlers
16459          *     An object containing the handlers for the request
16460          * @see finesse.restservices.User.States
16461          * @returns {finesse.restservices.User}
16462          *     This User object, to allow cascading
16463          */
16464         setState: function (newState, reasonCode, handlers) {
16465             this.isLoaded();
16466     
16467             var options, contentBody = {};
16468     
16469             if (!reasonCode) {
16470             	contentBody[this.getRestType()] = {
16471             			"state": newState
16472             	};
16473                 
16474             } else {
16475             	contentBody[this.getRestType()] = {
16476             			"state": newState,
16477             			"reasonCodeId": reasonCode.id
16478             	};
16479                
16480             }
16481     
16482             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16483             handlers = handlers || {};
16484     
16485             options = {
16486                 method: 'PUT',
16487                 success: handlers.success,
16488                 error: handlers.error,
16489                 content: contentBody
16490             };
16491     
16492             // After removing the selective 202 handling, we should be able to just use restRequest
16493             this.restRequest(this.getRestUrl(), options);
16494     
16495             return this; // Allow cascading
16496         },
16497     
16498         /**
16499          * Make call to a particular phone number.
16500          *
16501          * @param {String} 
16502          *     The number to call
16503          * @param {finesse.interfaces.RequestHandlers} handlers
16504          *     An object containing the handlers for the request
16505          * @returns {finesse.restservices.User}
16506          *     This User object, to allow cascading
16507          */ 
16508         makeCall: function (number, handlers) {
16509             this.isLoaded();
16510     
16511             this.getDialogs().createNewCallDialog(number, this.getExtension(), handlers);
16512     
16513             return this; // Allow cascading
16514         },
16515     
16516         /**
16517          * Make a silent monitor call to a particular agent's phone number.
16518          *
16519          * @param {String} 
16520          *     The number to call
16521          * @param {finesse.interfaces.RequestHandlers} handlers
16522          *     An object containing the handlers for the request
16523          * @returns {finesse.restservices.User}
16524          *     This User object, to allow cascading
16525          */
16526         makeSMCall: function (number, handlers) {
16527             this.isLoaded();
16528     
16529             var actionType = "SILENT_MONITOR";
16530     
16531             this.getDialogs().createNewSuperviseCallDialog(number, this.getExtension(), actionType, handlers);
16532     
16533             return this; // Allow cascading
16534         },
16535         
16536     
16537         /**
16538          * Make a silent monitor call to a particular agent's phone number.
16539          *
16540          * @param {String}
16541          *     The number to call
16542          * @param {String} dialogUri
16543          *     The associated dialog uri of SUPERVISOR_MONITOR call
16544          * @param {finesse.interfaces.RequestHandlers} handlers
16545          *     An object containing the handlers for the request
16546          * @see finesse.restservices.dialog
16547          * @returns {finesse.restservices.User}
16548          *     This User object, to allow cascading
16549          */
16550         makeBargeCall:function (number, dialogURI, handlers) {
16551             this.isLoaded();
16552             var actionType = "BARGE_CALL";
16553             this.getDialogs().createNewBargeCall( this.getExtension(), number, actionType, dialogURI,handlers);
16554     
16555             return this; // Allow cascading
16556         },
16557         
16558         /**
16559          * Returns true if the user's current state will result in a pending state change. A pending state
16560          * change is a request to change state that does not result in an immediate state change. For
16561          * example if an agent attempts to change to the NOT_READY state while in the TALKING state, the
16562          * agent will not change state until the call ends.
16563          *
16564          * The current set of states that result in pending state changes is as follows:
16565          *     TALKING
16566          *     HOLD
16567          *     RESERVED_OUTBOUND_PREVIEW
16568          *  @returns {Boolean} True if there is a pending state change.
16569          *  @see finesse.restservices.User.States
16570          */
16571         isPendingStateChange: function () {
16572             var state = this.getState();
16573             return state && ((state === User.States.TALKING) || (state === User.States.HOLD) || (state === User.States.RESERVED_OUTBOUND_PREVIEW));
16574         },
16575         
16576         /**
16577          * Returns true if the user's current state is WORK or WORK_READY. This is used so
16578          * that a pending state is not cleared when moving into wrap up (work) mode. 
16579          * Note that we don't add this as a pending state, since changes while in wrap up
16580          * occur immediately (and we don't want any "pending state" to flash on screen.
16581          * 
16582          * @see finesse.restservices.User.States
16583          * @returns {Boolean} True if user is in wrap-up mode.
16584          */
16585         isWrapUp: function () {
16586             var state = this.getState();
16587             return state && ((state === User.States.WORK) || (state === User.States.WORK_READY));
16588         },
16589     
16590         /**
16591          * @private
16592          * Parses a uriString to retrieve the id portion
16593          * @param {String} uriString
16594          * @return {String} id
16595          */
16596         _parseIdFromUriString : function (uriString) {
16597             return Utilities.getId(uriString);
16598         },
16599                         
16600         /**
16601          * Gets the user's Reason Code label. Works for both Not Ready and
16602          * Logout reason codes
16603          * 
16604          * @return {String} the reason code label, or empty string if none
16605          */
16606         getReasonCodeLabel : function() {
16607             this.isLoaded();
16608 
16609             if (this.getData().reasonCode) {
16610                 return this.getData().reasonCode.label;
16611             } else {
16612                 return "";
16613             }
16614         },
16615     
16616         /**
16617          * Gets the user's Not Ready reason code.
16618          * 
16619          * @return {String} Reason Code Id, or undefined if not set or
16620          *         indeterminate
16621          */
16622         getNotReadyReasonCodeId : function () {
16623             this.isLoaded();
16624     
16625             var reasoncodeIdResult, finesseServerReasonCodeId;
16626             finesseServerReasonCodeId = this.getData().reasonCodeId;
16627     
16628             //FinesseServer will give "-l" => we will set to undefined (for convenience)
16629             if (finesseServerReasonCodeId !== "-1") {
16630                 reasoncodeIdResult = finesseServerReasonCodeId;
16631             }
16632     
16633             return reasoncodeIdResult;
16634         },
16635     
16636         /**
16637          * Performs a GET against the Finesse server looking up the reasonCodeId specified.
16638          * Note that there is no return value; use the success handler to process a
16639          * valid return.
16640          * @param {finesse.interfaces.RequestHandlers} handlers
16641          *     An object containing the handlers for the request
16642          * @param {String} reasonCodeId The id for the reason code to lookup
16643          * 
16644          */
16645         getReasonCodeById : function (handlers, reasonCodeId)
16646         {
16647             var self = this, contentBody, reasonCode, url;
16648             contentBody = {};
16649     
16650             url = this.getRestUrl() + "/ReasonCode/" + reasonCodeId;
16651             this.restRequest(url, {
16652                 method: 'GET',
16653                 success: function (rsp) {
16654                     reasonCode = {
16655                         uri: rsp.object.ReasonCode.uri,
16656                         label: rsp.object.ReasonCode.label,
16657                         id: self._parseIdFromUriString(rsp.object.ReasonCode.uri)
16658                     };
16659                     handlers.success(reasonCode);
16660                 },
16661                 error: function (rsp) {
16662                     handlers.error(rsp);
16663                 },
16664                 content: contentBody
16665             });
16666         },
16667     
16668         /**
16669          * Performs a GET against Finesse server retrieving all the specified type of reason codes.
16670          * @param {String} type (LOGOUT or NOT_READY)
16671          * @param {finesse.interfaces.RequestHandlers} handlers
16672          *     An object containing the handlers for the request
16673          */
16674         _getReasonCodesByType : function (type, handlers)
16675         {
16676         	if(!this._TeamResource){
16677         		this._TeamResource = new TeamResource({teamId: this.getTeamId()});
16678         	}
16679         	
16680         	this._TeamResource.getReasonCodesByType(type, handlers);
16681         },
16682 
16683         /**
16684          * Performs a GET against the Finesse server and retrieves all of the Not Ready
16685          * reason codes. Note that there is no return value; use the success handler to
16686          * process a valid return.
16687          *
16688          * <pre class="code">
16689          *      _user.getSignoutReasonCodes({
16690          *           success: handleSignoutReasonCodesSuccess,
16691          *           error: handleSignoutReasonCodesError
16692          *       });
16693          * </pre>
16694          *
16695          * @see finesse.restservices.ReasonCodes
16696          *
16697          * @param {finesse.interfaces.RequestHandlers} handlers
16698          *     An object containing the handlers for the request
16699          */
16700         getSignoutReasonCodes : function (handlers)
16701         {
16702             this._getReasonCodesByType("LOGOUT", handlers);
16703         },
16704     
16705         /**
16706          * Performs a GET against the Finesse server and retrieves all of the Not Ready
16707          * reason codes. Note that there is no return value; use the success handler to
16708          * process a valid return.
16709          *
16710          * <pre class="code">
16711          *      _user.getNotReadyReasonCodes({
16712          *           success: handleNotReadyReasonCodesSuccess,
16713          *           error: handleNotReadyReasonCodesError
16714          *       });
16715          * </pre>
16716          *
16717          * @see finesse.restservices.ReasonCodes
16718          *
16719          * @param {finesse.interfaces.RequestHandlers} handlers
16720          *     An object containing the handlers for the request
16721          */
16722         getNotReadyReasonCodes : function (handlers)
16723         {
16724             this._getReasonCodesByType("NOT_READY", handlers);
16725         }
16726     });
16727     User.MediaStates = /** @lends finesse.restservices.User.MediaStates.prototype */ {
16728          /**
16729          * Will be applicable only in CCX deployments
16730          * When the agent is talking on a manual outbound call
16731          */
16732          BUSY: "BUSY",
16733              /**
16734              * @class Possible Agent Media States.
16735              * @constructs
16736              */
16737             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
16738             
16739     };
16740     User.States = /** @lends finesse.restservices.User.States.prototype */ {
16741             /**
16742              * User Login.  Note that while this is an action, is not technically a state, since a 
16743              * logged-in User will always be in a specific state (READY, NOT_READY, TALKING, etc.).
16744              */
16745             LOGIN: "LOGIN",
16746             /**
16747              * User is logged out.
16748              */
16749             LOGOUT: "LOGOUT",
16750             /**
16751              * User is not ready. Note that in UCCX implementations, the user is in this state while on a non-routed call.
16752              */
16753             NOT_READY: "NOT_READY",
16754             /**
16755              * User is ready for calls.
16756              */
16757             READY: "READY",
16758             /**
16759              * User has a call coming in, but has not answered it.
16760              */
16761             RESERVED: "RESERVED",
16762             /**
16763              * User has an outbound call being made, but has not been connected to it.
16764              */
16765             RESERVED_OUTBOUND: "RESERVED_OUTBOUND",
16766             /**
16767              * User has an outbound call's preview information being displayed, but has not acted on it.
16768              */
16769             RESERVED_OUTBOUND_PREVIEW: "RESERVED_OUTBOUND_PREVIEW",
16770             /**
16771              * User is on a call.  Note that in UCCX implementations, this is for routed calls only.
16772              */
16773             TALKING: "TALKING",
16774             /**
16775              * User is on hold.  Note that in UCCX implementations, the user remains in TALKING state while on hold.
16776              */
16777             HOLD: "HOLD",
16778             /**
16779              * User is wrap-up/work mode.  This mode is typically configured to time out, after which the user becomes NOT_READY.
16780              */
16781             WORK: "WORK",
16782             /**
16783              * This is the same as WORK, except that after time out user becomes READY.
16784              */
16785             WORK_READY: "WORK_READY",
16786             /**
16787              * @class Possible User state values.
16788              * @constructs
16789              */
16790             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
16791           
16792         };
16793     
16794     User.WorkMode = { /** @lends finesse.restservices.User.WorkMode.prototype */
16795         /**
16796          * Mobile agent is connected (dialed) for each incoming call received.
16797          */
16798         CALL_BY_CALL: "CALL_BY_CALL",
16799         /**
16800          * Mobile agent is connected (dialed) at login.
16801          */
16802         NAILED_CONNECTION: "NAILED_CONNECTION",
16803         /**
16804          * @class Possible Mobile Agent Work Mode Types.
16805          * @constructs
16806          */
16807         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
16808         
16809     };
16810 
16811     User.WrapUpMode = { /** @lends finesse.restservices.User.WrapUpMode.prototype */
16812         /**
16813          * Agent must go into wrap-up when call ends
16814          */
16815         REQUIRED: "REQUIRED",
16816         /**
16817          * Agent must go into wrap-up when call ends and must enter wrap-up data
16818          */
16819         REQUIRED_WITH_WRAP_UP_DATA: "REQUIRED_WITH_WRAP_UP_DATA",
16820         /**
16821          * Agent can choose to go into wrap-up on a call-by-call basis when the call ends
16822          */
16823         OPTIONAL: "OPTIONAL",
16824         /**
16825          * Agent is not allowed to go into wrap-up when call ends.
16826          */
16827         NOT_ALLOWED: "NOT_ALLOWED",
16828         /**
16829          * @class Possible Wrap-up Mode Types.
16830          * @constructs
16831          */
16832         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
16833         
16834     };
16835 
16836     window.finesse = window.finesse || {};
16837     window.finesse.restservices = window.finesse.restservices || {};
16838     window.finesse.restservices.User = User;
16839         
16840     return User;
16841 });
16842 
16843 /**
16844  * JavaScript representation of the Finesse Users collection
16845  * object which contains a list of Users objects.
16846  *
16847  * @requires finesse.clientservices.ClientServices
16848  * @requires Class
16849  * @requires finesse.FinesseBase
16850  * @requires finesse.restservices.RestBase
16851  * @requires finesse.restservices.RestCollectionBase
16852  * @requires finesse.restservices.User
16853  */
16854 
16855 /** @private */
16856 define('restservices/Users',[
16857     'restservices/RestCollectionBase',
16858     'restservices/RestBase',
16859     'restservices/User'
16860 ],
16861 function (RestCollectionBase, RestBase, User) {
16862 
16863     var Users = RestCollectionBase.extend(/** @lends finesse.restservices.Users.prototype */{
16864 
16865     /**
16866      * @class
16867      * JavaScript representation of a Users collection object. 
16868      * While there is no method provided to retrieve all Users, this collection is
16869      * used to return the Users in a supervised Team.
16870      * @augments finesse.restservices.RestCollectionBase
16871      * @constructs
16872      * @see finesse.restservices.Team
16873      * @see finesse.restservices.User
16874      * @see finesse.restservices.User#getSupervisedTeams
16875      * @example
16876      *  // Note: The following method gets an Array of Teams, not a Collection.
16877      *  _teams = _user.getSupervisedTeams();
16878      *  if (_teams.length > 0) {
16879      *      _team0Users = _teams[0].getUsers();
16880      *  }
16881      */
16882     _fakeConstuctor: function () {
16883         /* This is here to hide the real init constructor from the public docs */
16884     },
16885         
16886     /**
16887      * @private
16888      * JavaScript representation of the Finesse Users collection
16889      * object which contains a list of Users objects.
16890      *
16891 	 * @param {Object} options
16892 	 *     An object with the following properties:<ul>
16893      *         <li><b>id:</b> The id of the object being constructed</li>
16894      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16895      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16896      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16897      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16898      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16899      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16900      *             <li><b>content:</b> {String} Raw string of response</li>
16901      *             <li><b>object:</b> {Object} Parsed object of response</li>
16902      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16903      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16904      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16905      *             </ul></li>
16906      *         </ul></li>
16907      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16908      **/
16909 	init: function (options) {
16910 		this._super(options);
16911 	},
16912 
16913 	/**
16914      * @private
16915 	 * Gets the REST class for the current object - this is the Users class.
16916      * @returns {Object} 
16917      *      The User constructor.
16918 	 */
16919 	getRestClass: function () {
16920 	    return Users;
16921 	},
16922 
16923 	/**
16924      * @private
16925 	 * Gets the REST class for the objects that make up the collection. - this
16926 	 * is the User class.
16927      * @returns {finesse.restservices.User}
16928      *      This User object
16929      * @see finesse.restservices.User
16930 	 */
16931 	getRestItemClass: function () {
16932 		return User;
16933 	},
16934 
16935 	/**
16936      * @private
16937 	 * Gets the REST type for the current object - this is a "Users".
16938      * @returns {String} The Users String
16939 	 */
16940 	getRestType: function () {
16941 	    return "Users";
16942 	},
16943 
16944 	/**
16945      * @private
16946 	 * Gets the REST type for the objects that make up the collection - this is "User".
16947      * @returns {String} The User String
16948 	 */
16949 	getRestItemType: function () {
16950 	    return "User";
16951 	},
16952 
16953 	/**
16954      * @private
16955      * Gets the node path for the current object - this is the team Users node
16956      * @returns {String} The node path
16957      */
16958     getXMPPNodePath: function () {
16959 		return this.getRestUrl();
16960     },
16961 
16962     /**
16963      * @private
16964      * Overloading _doGET to reroute the GET to /Team/id from /Team/id/Users
16965      * This needs to be done because the GET /Team/id/Users API is missing
16966      * @returns {Users} This Users (collection) object to allow cascading
16967      */
16968     _doGET: function (handlers) {
16969         var _this = this;
16970         handlers = handlers || {};
16971         // Only do this for /Team/id/Users
16972         if (this._restObj && this._restObj.getRestType() === "Team") {
16973             this._restObj._doGET({
16974                 success: function (rspObj) {
16975                     // Making sure the response was a valid Team
16976                     if (_this._restObj._validate(rspObj.object)) {
16977                         // Shimmying the response to look like a Users collection by extracting it from the Team response
16978                         rspObj.object[_this.getRestType()] = rspObj.object[_this._restObj.getRestType()][_this.getRestType().toLowerCase()];
16979                         handlers.success(rspObj);
16980                     } else {
16981                         handlers.error(rspObj);
16982                     }
16983                 },
16984                 error: handlers.error
16985             });
16986             return this; // Allow cascading
16987         } else {
16988             return this._super(handlers);
16989         }
16990     },
16991 
16992 	/**
16993      * @private
16994      * Override default to indicates that the collection doesn't support making
16995 	 * requests.
16996 	 */
16997 	supportsRequests: false,
16998 
16999     /**
17000      * @private
17001      * Indicates that this collection handles the subscription for its items
17002      */
17003     handlesItemSubscription: true,
17004 	
17005     /**
17006      * @private
17007      * Override default to indicate that we need to subscribe explicitly
17008      */
17009     explicitSubscription: true
17010     
17011 	});
17012 
17013     window.finesse = window.finesse || {};
17014     window.finesse.restservices = window.finesse.restservices || {};
17015     window.finesse.restservices.Users = Users;
17016 
17017     return Users;
17018 });
17019 
17020 /**
17021  * JavaScript representation of the Finesse Team Not Ready Reason Code Assignment object.
17022  *
17023  * @requires finesse.clientservices.ClientServices
17024  * @requires Class
17025  * @requires finesse.FinesseBase
17026  * @requires finesse.restservices.RestBase
17027  */
17028 
17029 /** @private */
17030 define('restservices/TeamNotReadyReasonCode',['restservices/RestBase'], function (RestBase) {
17031     
17032     var TeamNotReadyReasonCode = RestBase.extend(/** @lends finesse.restservices.TeamNotReadyReasonCode.prototype */{
17033 
17034         /**
17035          * @class
17036          * JavaScript representation of a Team Not Ready ReasonCode object. Also exposes
17037          * methods to operate on the object against the server.
17038          *
17039          * @param {Object} options
17040          *     An object with the following properties:<ul>
17041          *         <li><b>id:</b> The id of the object being constructed</li>
17042          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17043          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17044          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17045          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17046          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17047          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17048          *             <li><b>content:</b> {String} Raw string of response</li>
17049          *             <li><b>object:</b> {Object} Parsed object of response</li>
17050          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17051          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17052          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17053          *             </ul></li>
17054          *         </ul></li>
17055          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17056          * @constructs
17057          **/
17058         init: function (options) {
17059             this._super(options);
17060         },
17061     
17062         /**
17063          * @private
17064          * Gets the REST class for the current object - this is the TeamNotReadyReasonCode class.
17065          * @returns {Object} The TeamNotReadyReasonCode class.
17066          */
17067         getRestClass: function () {
17068             return TeamNotReadyReasonCode;
17069         },
17070     
17071         /**
17072          * @private
17073          * Gets the REST type for the current object - this is a "ReasonCode".
17074          * @returns {String} The ReasonCode string.
17075          */
17076         getRestType: function () {
17077             return "ReasonCode";
17078         },
17079     
17080         /**
17081          * @private
17082          * Override default to indicate that this object doesn't support making
17083          * requests.
17084          */
17085         supportsRequests: false,
17086     
17087         /**
17088          * @private
17089          * Override default to indicate that this object doesn't support subscriptions.
17090          */
17091         supportsSubscriptions: false,
17092     
17093         /**
17094          * Getter for the category.
17095          * @returns {String} The category.
17096          */
17097         getCategory: function () {
17098             this.isLoaded();
17099             return this.getData().category;
17100         },
17101     
17102         /**
17103          * Getter for the code.
17104          * @returns {String} The code.
17105          */
17106         getCode: function () {
17107             this.isLoaded();
17108             return this.getData().code;
17109         },
17110     
17111         /**
17112          * Getter for the label.
17113          * @returns {String} The label.
17114          */
17115         getLabel: function () {
17116             this.isLoaded();
17117             return this.getData().label;
17118         },
17119     
17120         /**
17121          * Getter for the forAll value.
17122          * @returns {String} The forAll.
17123          */
17124         getForAll: function () {
17125             this.isLoaded();
17126             return this.getData().forAll;
17127         },
17128     
17129         /**
17130          * Getter for the Uri value.
17131          * @returns {String} The Uri.
17132          */
17133         getUri: function () {
17134             this.isLoaded();
17135             return this.getData().uri;
17136         },
17137         /**
17138 	     * Getter for the systemCode value.
17139 	     * @returns {String} The value for systemCode.
17140 	     * @since   11.6(1)-ES1 onwards
17141 	     */
17142 	    getSystemCode: function () {
17143 	        this.isLoaded();
17144 	        return this.getData().systemCode;
17145 	    }
17146 
17147     });
17148     
17149     window.finesse = window.finesse || {};
17150     window.finesse.restservices = window.finesse.restservices || {};
17151     window.finesse.restservices.TeamNotReadyReasonCode = TeamNotReadyReasonCode;
17152         
17153     return TeamNotReadyReasonCode;
17154 });
17155 
17156 /**
17157 * JavaScript representation of the Finesse TeamNotReadyReasonCodes collection
17158 * object which contains a list of TeamNotReadyReasonCode objects.
17159  *
17160  * @requires finesse.clientservices.ClientServices
17161  * @requires Class
17162  * @requires finesse.FinesseBase
17163  * @requires finesse.restservices.RestBase
17164  * @requires finesse.restservices.Dialog
17165  * @requires finesse.restservices.RestCollectionBase
17166  */
17167 
17168 /** @private */
17169 define('restservices/TeamNotReadyReasonCodes',[
17170     'restservices/RestCollectionBase',
17171     'restservices/RestBase',
17172     'restservices/TeamNotReadyReasonCode'
17173 ],
17174 function (RestCollectionBase, RestBase, TeamNotReadyReasonCode) {
17175 
17176     var TeamNotReadyReasonCodes = RestCollectionBase.extend(/** @lends finesse.restservices.TeamNotReadyReasonCodes.prototype */{
17177 
17178       /**
17179        * @class
17180        * JavaScript representation of a TeamNotReadyReasonCodes collection object. Also exposes
17181        * methods to operate on the object against the server.
17182        *
17183        * @param {Object} options
17184        *     An object with the following properties:<ul>
17185        *         <li><b>id:</b> The id of the object being constructed</li>
17186        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17187        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17188        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17189        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17190        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17191        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17192        *             <li><b>content:</b> {String} Raw string of response</li>
17193        *             <li><b>object:</b> {Object} Parsed object of response</li>
17194        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17195        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17196        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17197        *             </ul></li>
17198        *         </ul></li>
17199        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17200        * @augments finesse.restservices.RestCollectionBase
17201        * @constructs
17202        **/
17203       init: function (options) {
17204           this._super(options);
17205       },
17206     
17207       /**
17208        * @private
17209        * Gets the REST class for the current object - this is the TeamNotReadyReasonCodes class.
17210        * @returns {Object}
17211        *     The TeamNotReadyReasonCodes constructor.
17212        */
17213       getRestClass: function () {
17214           return TeamNotReadyReasonCodes;
17215       },
17216     
17217       /**
17218        * @private
17219        * Gets the REST class for the objects that make up the collection. - this
17220        * is the TeamNotReadyReasonCode class.
17221        * @returns {finesse.restservices.TeamNotReadyReasonCode}
17222        *        The TeamNotReadyReasonCode Object
17223        * @see finesse.restservices.TeamNotReadyReasonCode
17224        */
17225       getRestItemClass: function () {
17226           return TeamNotReadyReasonCode;
17227       },
17228     
17229       /**
17230        * @private
17231        * Gets the REST type for the current object - this is a "ReasonCodes".
17232        * @returns {String} The ReasonCodes String
17233        */
17234       getRestType: function () {
17235           return "ReasonCodes";
17236       },
17237     
17238       /**
17239        * @private
17240        * Overrides the parent class.  Returns the url for the NotReadyReasonCodes resource
17241        */
17242       getRestUrl: function () {
17243           // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
17244           var restObj = this._restObj,
17245               restUrl = "";
17246           //Prepend the base REST object if one was provided.
17247           //Otherwise prepend with the default webapp name.
17248           if (restObj instanceof RestBase) {
17249               restUrl += restObj.getRestUrl();
17250           }
17251           else {
17252               restUrl += "/finesse/api";
17253           }
17254           //Append the REST type.
17255           restUrl += "/ReasonCodes?category=NOT_READY";
17256           //Append ID if it is not undefined, null, or empty.
17257           if (this._id) {
17258               restUrl += "/" + this._id;
17259           }
17260           return restUrl;
17261       },
17262     
17263       /**
17264        * @private
17265        * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
17266        */
17267       getRestItemType: function () {
17268           return "ReasonCode";
17269       },
17270     
17271       /**
17272        * @private
17273        * Override default to indicates that the collection supports making
17274        * requests.
17275        */
17276       supportsRequests: true,
17277     
17278       /**
17279        * @private
17280        * Override default to indicate that this object doesn't support subscriptions.
17281        */
17282       supportsRestItemSubscriptions: false,
17283     
17284       /**
17285        * @private
17286        * Retrieve the Not Ready Reason Codes.
17287        *
17288        * @returns {TeamNotReadyReasonCodes}
17289        *     This TeamNotReadyReasonCodes object to allow cascading.
17290        */
17291       get: function () {
17292           // set loaded to false so it will rebuild the collection after the get
17293           this._loaded = false;
17294           // reset collection
17295           this._collection = {};
17296           // perform get
17297           this._synchronize();
17298           return this;
17299       },
17300     
17301       /**
17302        * @private
17303        * Set up the PutSuccessHandler for TeamNotReadyReasonCodes
17304        * @param {Object} reasonCodes
17305        * @param {String} contentBody
17306        * @param successHandler    
17307        * @return {function}
17308        */
17309       createPutSuccessHandler: function (reasonCodes, contentBody, successHandler) {
17310           return function (rsp) {
17311               // Update internal structure based on response. Here we
17312               // inject the contentBody from the PUT request into the
17313               // rsp.object element to mimic a GET as a way to take
17314               // advantage of the existing _processResponse method.
17315               rsp.object = contentBody;
17316               reasonCodes._processResponse(rsp);
17317     
17318               //Remove the injected contentBody object before cascading response
17319               rsp.object = {};
17320     
17321               //cascade response back to consumer's response handler
17322               successHandler(rsp);
17323           };
17324       },
17325     
17326       /**
17327        * @private
17328        * Perform the REST API PUT call to update the reason code assignments for the team
17329        * @param {string[]} newValues
17330        * @param handlers     
17331        */
17332       update: function (newValues, handlers) {
17333           this.isLoaded();
17334           var contentBody = {}, contentBodyInner = [], i, innerObject = {};
17335     
17336           contentBody[this.getRestType()] = {
17337           };
17338     
17339           for (i in newValues) {
17340               if (newValues.hasOwnProperty(i)) {
17341                   innerObject = {
17342                       "uri": newValues[i]
17343                   };
17344                   contentBodyInner.push(innerObject);
17345               }
17346           }
17347     
17348           contentBody[this.getRestType()] = {
17349               "ReasonCode" : contentBodyInner
17350           };
17351     
17352           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17353           handlers = handlers || {};
17354     
17355           this.restRequest(this.getRestUrl(), {
17356               method: 'PUT',
17357               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17358               error: handlers.error,
17359               content: contentBody
17360           });
17361     
17362           return this; // Allow cascading
17363       }
17364   });
17365   
17366     window.finesse = window.finesse || {};
17367     window.finesse.restservices = window.finesse.restservices || {};
17368     window.finesse.restservices.TeamNotReadyReasonCodes = TeamNotReadyReasonCodes;
17369     
17370   return TeamNotReadyReasonCodes;
17371 });
17372 
17373 /**
17374  * JavaScript representation of the Finesse Team Wrap Up Reason object.
17375  *
17376  * @requires finesse.clientservices.ClientServices
17377  * @requires Class
17378  * @requires finesse.FinesseBase
17379  * @requires finesse.restservices.RestBase
17380  */
17381 /** @private */
17382 define('restservices/TeamWrapUpReason',['restservices/RestBase'], function (RestBase) {
17383 
17384     var TeamWrapUpReason = RestBase.extend({
17385 
17386     /**
17387      * @class
17388      * JavaScript representation of a TeamWrapUpReason object. Also exposes
17389      * methods to operate on the object against the server.
17390      *
17391      * @param {Object} options
17392      *     An object with the following properties:<ul>
17393      *         <li><b>id:</b> The id of the object being constructed</li>
17394      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17395      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17396      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17397      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17398      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17399      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17400      *             <li><b>content:</b> {String} Raw string of response</li>
17401      *             <li><b>object:</b> {Object} Parsed object of response</li>
17402      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17403      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17404      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17405      *             </ul></li>
17406      *         </ul></li>
17407      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17408      * @constructs
17409      **/
17410     init: function (options) {
17411         this._super(options);
17412     },
17413 
17414     /**
17415      * @private
17416      * Gets the REST class for the current object - this is the TeamWrapUpReason class.
17417      * @returns {Object} The TeamWrapUpReason class.
17418      */
17419     getRestClass: function () {
17420         return TeamWrapUpReason;
17421     },
17422 
17423     /**
17424      * @private
17425      * Gets the REST type for the current object - this is a "WrapUpReason".
17426      * @returns {String} The WrapUpReason string.
17427      */
17428     getRestType: function () {
17429         return "WrapUpReason";
17430     },
17431 
17432     /**
17433      * @private
17434      * Override default to indicate that this object doesn't support making
17435      * requests.
17436      */
17437     supportsRequests: false,
17438 
17439     /**
17440      * @private
17441      * Override default to indicate that this object doesn't support subscriptions.
17442      */
17443     supportsSubscriptions: false,
17444 
17445     /**
17446      * Getter for the label.
17447      * @returns {String} The label.
17448      */
17449     getLabel: function () {
17450         this.isLoaded();
17451         return this.getData().label;
17452     },
17453 
17454     /**
17455      * @private
17456      * Getter for the forAll value.
17457      * @returns {Boolean} True if global
17458      */
17459     getForAll: function () {
17460         this.isLoaded();
17461         return this.getData().forAll;
17462     },
17463 
17464     /**
17465      * @private
17466      * Getter for the Uri value.
17467      * @returns {String} The Uri.
17468      */
17469     getUri: function () {
17470         this.isLoaded();
17471         return this.getData().uri;
17472     }
17473 	});
17474 
17475     window.finesse = window.finesse || {};
17476     window.finesse.restservices = window.finesse.restservices || {};
17477     window.finesse.restservices.TeamWrapUpReason = TeamWrapUpReason;
17478 
17479     return TeamWrapUpReason;
17480 });
17481 
17482 /**
17483 * JavaScript representation of the Finesse Team Wrap-Up Reasons collection
17484 * object which contains a list of Wrap-Up Reasons objects.
17485  *
17486  * @requires finesse.clientservices.ClientServices
17487  * @requires Class
17488  * @requires finesse.FinesseBase
17489  * @requires finesse.restservices.RestBase
17490  * @requires finesse.restservices.Dialog
17491  * @requires finesse.restservices.RestCollectionBase
17492  */
17493 /** @private */
17494 define('restservices/TeamWrapUpReasons',[
17495     'restservices/RestCollectionBase',
17496     'restservices/RestBase',
17497     'restservices/TeamWrapUpReason'
17498 ],
17499 function (RestCollectionBase, RestBase, TeamWrapUpReason) {
17500 
17501     var TeamWrapUpReasons = RestCollectionBase.extend({
17502 
17503     /**
17504      * @class
17505      * JavaScript representation of a TeamWrapUpReasons collection object. Also exposes
17506      * methods to operate on the object against the server.
17507      *
17508      * @param {Object} options
17509      *     An object with the following properties:<ul>
17510      *         <li><b>id:</b> The id of the object being constructed</li>
17511      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17512      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17513      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17514      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17515      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17516      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17517      *             <li><b>content:</b> {String} Raw string of response</li>
17518      *             <li><b>object:</b> {Object} Parsed object of response</li>
17519      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17520      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17521      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17522      *             </ul></li>
17523      *         </ul></li>
17524      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17525      * @constructs
17526      **/
17527     init: function (options) {
17528         this._super(options);
17529     },
17530 
17531     /**
17532      * @private
17533      * Gets the REST class for the current object - this is the TeamWrapUpReasons class.
17534      */
17535     getRestClass: function () {
17536         return TeamWrapUpReasons;
17537     },
17538 
17539     /**
17540      * @private
17541      * Gets the REST class for the objects that make up the collection. - this
17542      * is the TeamWrapUpReason class.
17543      */
17544     getRestItemClass: function () {
17545         return TeamWrapUpReason;
17546     },
17547 
17548     /**
17549      * @private
17550      * Gets the REST type for the current object - this is a "WrapUpReasons".
17551      */
17552     getRestType: function () {
17553         return "WrapUpReasons";
17554     },
17555 
17556     /**
17557      * @private
17558      * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
17559      */
17560     getRestItemType: function () {
17561         return "WrapUpReason";
17562     },
17563 
17564     /**
17565      * @private
17566      * Override default to indicates that the collection supports making
17567      * requests.
17568      */
17569     supportsRequests: true,
17570 
17571     /**
17572      * @private
17573      * Override default to indicate that this object doesn't support subscriptions.
17574      */
17575     supportsRestItemSubscriptions: false,
17576 
17577     /**
17578      * Retrieve the Team Wrap Up Reasons.
17579      *
17580      * @returns {finesse.restservices.TeamWrapUpReasons}
17581      *     This TeamWrapUpReasons object to allow cascading.
17582      */
17583     get: function () {
17584         // set loaded to false so it will rebuild the collection after the get
17585         this._loaded = false;
17586         // reset collection
17587         this._collection = {};
17588         // perform get
17589         this._synchronize();
17590         return this;
17591     },
17592 
17593     /**
17594      * Set up the PutSuccessHandler for TeamWrapUpReasons
17595      * @param {Object} wrapUpReasons
17596      * @param {Object} contentBody
17597      * @param successHandler
17598      * @returns response
17599      */
17600     createPutSuccessHandler: function (wrapUpReasons, contentBody, successHandler) {
17601         return function (rsp) {
17602             // Update internal structure based on response. Here we
17603             // inject the contentBody from the PUT request into the
17604             // rsp.object element to mimic a GET as a way to take
17605             // advantage of the existing _processResponse method.
17606             rsp.object = contentBody;
17607             
17608             wrapUpReasons._processResponse(rsp);
17609 
17610             //Remove the injected contentBody object before cascading response
17611             rsp.object = {};
17612 
17613             //cascade response back to consumer's response handler
17614             successHandler(rsp);
17615         };
17616     },
17617 
17618     /**    
17619      * Perform the REST API PUT call to update the reason code assignments for the team
17620      * @param {String Array} newValues
17621      * @param handlers
17622      * @returns {Object} this
17623      */
17624     update: function (newValues, handlers) {
17625         this.isLoaded();
17626         var contentBody = {}, contentBodyInner = [], i, innerObject = {};
17627 
17628         contentBody[this.getRestType()] = {
17629         };
17630 
17631         for (i in newValues) {
17632             if (newValues.hasOwnProperty(i)) {
17633                 innerObject = {
17634                     "uri": newValues[i]
17635                 };
17636                 contentBodyInner.push(innerObject);
17637             }
17638         }
17639 
17640         contentBody[this.getRestType()] = {
17641             "WrapUpReason" : contentBodyInner
17642         };
17643 
17644         // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17645         handlers = handlers || {};
17646 
17647         this.restRequest(this.getRestUrl(), {
17648             method: 'PUT',
17649             success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17650             error: handlers.error,
17651             content: contentBody
17652         });
17653 
17654         return this; // Allow cascading
17655     }
17656 	});
17657 
17658     window.finesse = window.finesse || {};
17659     window.finesse.restservices = window.finesse.restservices || {};
17660     window.finesse.restservices.TeamWrapUpReasons = TeamWrapUpReasons;
17661 
17662     return TeamWrapUpReasons;
17663 });
17664 
17665 /**
17666  * JavaScript representation of a TeamSignOutReasonCode.
17667  *
17668  * @requires finesse.clientservices.ClientServices
17669  * @requires Class
17670  * @requires finesse.FinesseBase
17671  * @requires finesse.restservices.RestBase
17672  */
17673 
17674 /** @private */
17675 define('restservices/TeamSignOutReasonCode',['restservices/RestBase'], function (RestBase) {
17676     var TeamSignOutReasonCode = RestBase.extend(/** @lends finesse.restservices.TeamSignOutReasonCode.prototype */{
17677 
17678         /**
17679          * @class
17680          * JavaScript representation of a TeamSignOutReasonCode object. Also exposes
17681          * methods to operate on the object against the server.
17682          *
17683          * @param {Object} options
17684          *     An object with the following properties:<ul>
17685          *         <li><b>id:</b> The id of the object being constructed</li>
17686          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17687          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17688          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17689          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17690          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17691          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17692          *             <li><b>content:</b> {String} Raw string of response</li>
17693          *             <li><b>object:</b> {Object} Parsed object of response</li>
17694          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17695          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17696          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17697          *             </ul></li>
17698          *         </ul></li>
17699          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17700          * @constructs
17701          * @ignore
17702          **/
17703         init: function (options) {
17704             this._super(options);
17705         },
17706 
17707         /**
17708          * @private
17709          * Gets the REST class for the current object - this is the TeamSignOutReasonCode class.
17710          * @returns {Object} The TeamSignOutReasonCode class.
17711          */
17712         getRestClass: function () {
17713             return TeamSignOutReasonCode;
17714         },
17715 
17716         /**
17717          * @private
17718          * Gets the REST type for the current object - this is a "ReasonCode".
17719          * @returns {String} The ReasonCode string.
17720          */
17721         getRestType: function () {
17722             return "ReasonCode";
17723         },
17724 
17725         /**
17726          * @private
17727          * Override default to indicate that this object doesn't support making
17728          * requests.
17729          */
17730         supportsRequests: false,
17731 
17732         /**
17733          * @private
17734          * Override default to indicate that this object doesn't support subscriptions.
17735          */
17736         supportsSubscriptions: false,
17737 
17738         /**
17739          * Getter for the category.
17740          * @returns {String} The category.
17741          */
17742         getCategory: function () {
17743             this.isLoaded();
17744             return this.getData().category;
17745         },
17746 
17747         /**
17748          * Getter for the code.
17749          * @returns {String} The code.
17750          */
17751         getCode: function () {
17752             this.isLoaded();
17753             return this.getData().code;
17754         },
17755 
17756         /**
17757          * Getter for the label.
17758          * @returns {String} The label.
17759          */
17760         getLabel: function () {
17761             this.isLoaded();
17762             return this.getData().label;
17763         },
17764 
17765         /**
17766          * Getter for the forAll value.
17767          * @returns {String} The forAll.
17768          */
17769         getForAll: function () {
17770             this.isLoaded();
17771             return this.getData().forAll;
17772         },
17773 
17774         /**
17775          * Getter for the Uri value.
17776          * @returns {String} The Uri.
17777          */
17778         getUri: function () {
17779             this.isLoaded();
17780             return this.getData().uri;
17781         },
17782         /**
17783 	     * Getter for the systemCode value.
17784 	     * @returns {String} The value for systemCode.
17785 	     * @since   11.6(1)-ES1 onwards
17786 	     */
17787 	    getSystemCode: function () {
17788 	        this.isLoaded();
17789 	        return this.getData().systemCode;
17790 	    }
17791 
17792     });
17793 
17794     window.finesse = window.finesse || {};
17795     window.finesse.restservices = window.finesse.restservices || {};
17796     window.finesse.restservices.TeamSignOutReasonCode = TeamSignOutReasonCode;
17797     
17798     return TeamSignOutReasonCode;
17799 });
17800 
17801 /**
17802 * JavaScript representation of the TeamSignOutReasonCodes collection
17803 * object which contains a list of TeamSignOutReasonCode objects.
17804  *
17805  * @requires finesse.clientservices.ClientServices
17806  * @requires Class
17807  * @requires finesse.FinesseBase
17808  * @requires finesse.restservices.RestBase
17809  * @requires finesse.restservices.Dialog
17810  * @requires finesse.restservices.RestCollectionBase
17811  */
17812 
17813 /** @private */
17814 define('restservices/TeamSignOutReasonCodes',[
17815     'restservices/RestCollectionBase',
17816     'restservices/RestBase',
17817     'restservices/TeamSignOutReasonCode'
17818 ],
17819 function (RestCollectionBase, RestBase, TeamSignOutReasonCode) {
17820     
17821     var TeamSignOutReasonCodes = RestCollectionBase.extend(/** @lends finesse.restservices.TeamSignOutReasonCodes.prototype */{
17822         /**
17823          * @class
17824          * JavaScript representation of a TeamSignOutReasonCodes collection object. Also exposes
17825          * methods to operate on the object against the server.
17826          *
17827          * @param {Object} options
17828          *     An object with the following properties:<ul>
17829          *         <li><b>id:</b> The id of the object being constructed</li>
17830          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17831          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17832          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17833          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17834          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17835          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17836          *             <li><b>content:</b> {String} Raw string of response</li>
17837          *             <li><b>object:</b> {Object} Parsed object of response</li>
17838          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17839          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17840          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17841          *             </ul></li>
17842          *         </ul></li>
17843          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17844          * @constructs
17845          **/
17846         init: function (options) {
17847             this._super(options);
17848         },
17849 
17850         /**
17851          * @private
17852          * Gets the REST class for the current object - this is the TeamSignOutReasonCodes class.
17853          * @returns {Object}
17854          *      The TeamSignOutReasonCodes constructor.
17855          */
17856         getRestClass: function () {
17857             return TeamSignOutReasonCodes;
17858         },
17859 
17860         /**
17861          * @private
17862          * Gets the REST class for the objects that make up the collection. - this
17863          * is the TeamSignOutReasonCode class.
17864          * @returns {finesse.restservices.TeamSignOutReasonCode}
17865          *      The TeamSignOutReasonCode Object
17866          * @see finesse.restservices.TeamSignOutReasonCode
17867          */
17868         getRestItemClass: function () {
17869             return TeamSignOutReasonCode;
17870         },
17871 
17872         /**
17873          * @private
17874          * Gets the REST type for the current object - this is a "ReasonCodes".
17875          * @returns {String} The ReasonCodes String
17876          */
17877         getRestType: function () {
17878             return "ReasonCodes";
17879         },
17880 
17881         /**
17882          * Overrides the parent class.  Returns the url for the SignOutReasonCodes resource
17883          */
17884         getRestUrl: function () {
17885             var restObj = this._restObj, restUrl = "";
17886 
17887             //Prepend the base REST object if one was provided.
17888             //Otherwise prepend with the default webapp name.
17889             if (restObj instanceof RestBase) {
17890                 restUrl += restObj.getRestUrl();
17891             } else {
17892                 restUrl += "/finesse/api";
17893             }
17894             //Append the REST type.
17895             restUrl += "/ReasonCodes?category=LOGOUT";
17896             //Append ID if it is not undefined, null, or empty.
17897             if (this._id) {
17898                 restUrl += "/" + this._id;
17899             }
17900             return restUrl;
17901         },
17902 
17903         /**
17904          * @private
17905          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
17906          */
17907         getRestItemType: function () {
17908             return "ReasonCode";
17909         },
17910 
17911         /**
17912          * @private
17913          * Override default to indicates that the collection supports making requests.
17914          */
17915         supportsRequests: true,
17916 
17917         /**
17918          * @private
17919          * Override default to indicates that the collection does not subscribe to its objects.
17920          */
17921         supportsRestItemSubscriptions: false,
17922 
17923         /**
17924          * Retrieve the Sign Out Reason Codes.
17925          *
17926          * @returns {finesse.restservices.TeamSignOutReasonCodes}
17927          *     This TeamSignOutReasonCodes object to allow cascading.
17928          */
17929         get: function () {
17930             // set loaded to false so it will rebuild the collection after the get
17931             this._loaded = false;
17932             // reset collection
17933             this._collection = {};
17934             // perform get
17935             this._synchronize();
17936             return this;
17937         },
17938 
17939         /* We only use PUT and GET on Reason Code team assignments
17940          * @param {Object} contact
17941          * @param {Object} contentBody
17942          * @param {Function} successHandler
17943          */
17944         createPutSuccessHandler: function (contact, contentBody, successHandler) {
17945             return function (rsp) {
17946                 // Update internal structure based on response. Here we
17947                 // inject the contentBody from the PUT request into the
17948                 // rsp.object element to mimic a GET as a way to take
17949                 // advantage of the existing _processResponse method.
17950                 rsp.object = contentBody;
17951                 contact._processResponse(rsp);
17952 
17953                 //Remove the injected contentBody object before cascading response
17954                 rsp.object = {};
17955 
17956                 //cascade response back to consumer's response handler
17957                 successHandler(rsp);
17958             };
17959         },
17960 
17961         /**
17962          * Update - This should be all that is needed.
17963          * @param {Object} newValues
17964          * @param {Object} handlers
17965          * @returns {finesse.restservices.TeamSignOutReasonCodes}
17966          *     This TeamSignOutReasonCodes object to allow cascading.
17967          */
17968         update: function (newValues, handlers) {
17969             this.isLoaded();
17970             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
17971 
17972             contentBody[this.getRestType()] = {
17973             };
17974 
17975             for (i in newValues) {
17976                 if (newValues.hasOwnProperty(i)) {
17977                     innerObject = {
17978                         "uri": newValues[i]
17979                     };
17980                     contentBodyInner.push(innerObject);
17981                 }
17982             }
17983 
17984             contentBody[this.getRestType()] = {
17985                 "ReasonCode" : contentBodyInner
17986             };
17987 
17988             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17989             handlers = handlers || {};
17990 
17991             this.restRequest(this.getRestUrl(), {
17992                 method: 'PUT',
17993                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17994                 error: handlers.error,
17995                 content: contentBody
17996             });
17997 
17998             return this; // Allow cascading
17999         }
18000 
18001     });
18002     
18003     window.finesse = window.finesse || {};
18004     window.finesse.restservices = window.finesse.restservices || {};
18005     window.finesse.restservices.TeamSignOutReasonCodes = TeamSignOutReasonCodes;
18006     
18007     return TeamSignOutReasonCodes;
18008 });
18009 
18010 /**
18011  * JavaScript representation of the Finesse PhoneBook Assignment object.
18012  *
18013  * @requires finesse.clientservices.ClientServices
18014  * @requires Class
18015  * @requires finesse.FinesseBase
18016  * @requires finesse.restservices.RestBase
18017  */
18018 
18019 /**
18020  * The following comment prevents JSLint errors concerning undefined global variables.
18021  * It tells JSLint that these identifiers are defined elsewhere.
18022  */
18023 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
18024 
18025 /** The following comment is to prevent jslint errors about 
18026  * using variables before they are defined.
18027  */
18028 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
18029 
18030 /** @private */
18031 define('restservices/TeamPhoneBook',['restservices/RestBase'], function (RestBase) {
18032     var TeamPhoneBook = RestBase.extend({
18033 
18034         /**
18035          * @class
18036          * JavaScript representation of a PhoneBook object. Also exposes
18037          * methods to operate on the object against the server.
18038          *
18039          * @param {Object} options
18040          *     An object with the following properties:<ul>
18041          *         <li><b>id:</b> The id of the object being constructed</li>
18042          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18043          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18044          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18045          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18046          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18047          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18048          *             <li><b>content:</b> {String} Raw string of response</li>
18049          *             <li><b>object:</b> {Object} Parsed object of response</li>
18050          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18051          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18052          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18053          *             </ul></li>
18054          *         </ul></li>
18055          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18056          * @constructs
18057          **/
18058         init: function (options) {
18059             this._super(options);
18060         },
18061 
18062         /**
18063          * @private
18064          * Gets the REST class for the current object - this is the PhoneBooks class.
18065          * @returns {Object} The PhoneBooks class.
18066          */
18067         getRestClass: function () {
18068             return TeamPhoneBook;
18069         },
18070 
18071         /**
18072          * @private
18073          * Gets the REST type for the current object - this is a "PhoneBook".
18074          * @returns {String} The PhoneBook string.
18075          */
18076         getRestType: function () {
18077             return "PhoneBook";
18078         },
18079 
18080         /**
18081          * @private
18082          * Override default to indicate that this object doesn't support making
18083          * requests.
18084          */
18085         supportsRequests: false,
18086 
18087         /**
18088          * @private
18089          * Override default to indicate that this object doesn't support subscriptions.
18090          */
18091         supportsSubscriptions: false,
18092 
18093         /**
18094          * Getter for the name.
18095          * @returns {String} The name.
18096          */
18097         getName: function () {
18098             this.isLoaded();
18099             return this.getData().name;
18100         },
18101 
18102         /**
18103          * Getter for the Uri value.
18104          * @returns {String} The Uri.
18105          */
18106         getUri: function () {
18107             this.isLoaded();
18108             return this.getData().uri;
18109         }
18110 
18111     });
18112 
18113     window.finesse = window.finesse || {};
18114     window.finesse.restservices = window.finesse.restservices || {};
18115     window.finesse.restservices.TeamPhoneBook = TeamPhoneBook;
18116     
18117     return TeamPhoneBook;
18118 });
18119 
18120 /**
18121 * JavaScript representation of the Finesse PhoneBook Assignments collection
18122 * object which contains a list of Not Ready Reason Codes objects.
18123  *
18124  * @requires finesse.clientservices.ClientServices
18125  * @requires Class
18126  * @requires finesse.FinesseBase
18127  * @requires finesse.restservices.RestBase
18128  * @requires finesse.restservices.Dialog
18129  * @requires finesse.restservices.RestCollectionBase
18130  */
18131 
18132 /**
18133  * The following comment prevents JSLint errors concerning undefined global variables.
18134  * It tells JSLint that these identifiers are defined elsewhere.
18135  */
18136 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
18137 
18138 /** The following comment is to prevent jslint errors about 
18139  * using variables before they are defined.
18140  */
18141 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
18142 
18143 /** @private */
18144 define('restservices/TeamPhoneBooks',[
18145     'restservices/RestCollectionBase',
18146     'restservices/RestBase',
18147     'restservices/TeamPhoneBook'
18148 ],
18149 function (RestCollectionBase, RestBase, TeamPhoneBook) {
18150     var TeamPhoneBooks = RestCollectionBase.extend({
18151         
18152         /**
18153          * @class
18154          * JavaScript representation of a TeamPhoneBooks collection object. Also exposes
18155          * methods to operate on the object against the server.
18156          *
18157          * @param {Object} options
18158          *     An object with the following properties:<ul>
18159          *         <li><b>id:</b> The id of the object being constructed</li>
18160          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18161          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18162          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18163          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18164          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18165          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18166          *             <li><b>content:</b> {String} Raw string of response</li>
18167          *             <li><b>object:</b> {Object} Parsed object of response</li>
18168          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18169          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18170          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18171          *             </ul></li>
18172          *         </ul></li>
18173          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18174          * @constructs
18175          **/
18176         init: function (options) {
18177             this._super(options);           
18178         },
18179 
18180         /**
18181          * @private
18182          * Gets the REST class for the current object - this is the TeamPhoneBooks class.
18183          */
18184         getRestClass: function () {
18185             return TeamPhoneBooks;
18186         },
18187 
18188         /**
18189          * @private
18190          * Gets the REST class for the objects that make up the collection. - this
18191          * is the TeamPhoneBooks class.
18192          */
18193         getRestItemClass: function () {
18194             return TeamPhoneBook;
18195         },
18196 
18197         /**
18198          * @private
18199          * Gets the REST type for the current object - this is a "ReasonCodes".
18200          */
18201         getRestType: function () {
18202             return "PhoneBooks";
18203         },
18204         
18205         /**
18206          * Overrides the parent class.  Returns the url for the PhoneBooks resource
18207          */
18208         getRestUrl: function () {
18209             // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
18210             var restObj = this._restObj,
18211             restUrl = "";
18212             //Prepend the base REST object if one was provided.
18213             if (restObj instanceof RestBase) {
18214                 restUrl += restObj.getRestUrl();
18215             }
18216             //Otherwise prepend with the default webapp name.
18217             else {
18218                 restUrl += "/finesse/api";
18219             }
18220             //Append the REST type.
18221             restUrl += "/PhoneBooks";
18222             //Append ID if it is not undefined, null, or empty.
18223             if (this._id) {
18224                 restUrl += "/" + this._id;
18225             }
18226             return restUrl;        
18227         },
18228         
18229         /**
18230          * @private
18231          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
18232          */
18233         getRestItemType: function () {
18234             return "PhoneBook";
18235         },
18236 
18237         /**
18238          * @private
18239          * Override default to indicates that the collection supports making
18240          * requests.
18241          */
18242         supportsRequests: true,
18243 
18244         /**
18245          * @private
18246          * Override default to indicates that the collection subscribes to its objects.
18247          */
18248         supportsRestItemSubscriptions: false,
18249         
18250         /**
18251          * Retrieve the Not Ready Reason Codes.
18252          *
18253          * @returns {finesse.restservices.TeamPhoneBooks}
18254          *     This TeamPhoneBooks object to allow cascading.
18255          */
18256         get: function () {
18257             // set loaded to false so it will rebuild the collection after the get
18258             /** @private */
18259             this._loaded = false;
18260             // reset collection
18261             /** @private */
18262             this._collection = {};
18263             // perform get
18264             this._synchronize();
18265             return this;
18266         },
18267 
18268         /* We only use PUT and GET on Reason Code team assignments 
18269          */
18270         createPutSuccessHandler: function(contact, contentBody, successHandler){
18271             return function (rsp) {
18272                 // Update internal structure based on response. Here we
18273                 // inject the contentBody from the PUT request into the
18274                 // rsp.object element to mimic a GET as a way to take
18275                 // advantage of the existing _processResponse method.
18276                 rsp.object = contentBody;
18277                 contact._processResponse(rsp);
18278 
18279                 //Remove the injected Contact object before cascading response
18280                 rsp.object = {};
18281                 
18282                 //cascade response back to consumer's response handler
18283                 successHandler(rsp);
18284             };
18285         },
18286 
18287         /**
18288          * Update - This should be all that is needed.
18289          */
18290         update: function (newValues, handlers) {
18291             this.isLoaded();
18292             var contentBody = {}, contentBodyInner = [], i, innerObject;
18293 
18294             contentBody[this.getRestType()] = {
18295             };
18296         
18297             for (i in newValues) {
18298                 if (newValues.hasOwnProperty(i)) {
18299                     innerObject = {};
18300                     innerObject = {
18301                         "uri": newValues[i]
18302                     };
18303                     contentBodyInner.push(innerObject);
18304                 }
18305             }
18306 
18307             contentBody[this.getRestType()] = {
18308                 "PhoneBook" : contentBodyInner
18309             };
18310 
18311             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
18312             handlers = handlers || {};
18313 
18314             this.restRequest(this.getRestUrl(), {
18315                 method: 'PUT',
18316                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
18317                 error: handlers.error,
18318                 content: contentBody
18319             });
18320 
18321             return this; // Allow cascading
18322         }       
18323         
18324     });
18325         
18326     window.finesse = window.finesse || {};
18327     window.finesse.restservices = window.finesse.restservices || {};
18328     window.finesse.restservices.TeamPhoneBooks = TeamPhoneBooks;
18329     
18330     return TeamPhoneBooks;
18331 });
18332 
18333 /**
18334  * JavaScript representation of the Finesse LayoutConfig object
18335  * @requires ClientServices
18336  * @requires finesse.FinesseBase
18337  * @requires finesse.restservices.RestBase
18338  */
18339 
18340 /** @private */
18341 define('restservices/LayoutConfig',['restservices/RestBase'], function (RestBase) {
18342     /** @private */
18343 	var LayoutConfig = RestBase.extend({
18344 
18345 		/**
18346 		 * @class
18347 		 * JavaScript representation of a LayoutConfig object. Also exposes methods to operate
18348 		 * on the object against the server.
18349 		 *
18350 		 * @param {String} id
18351 		 *     Not required...
18352 		 * @param {Object} callbacks
18353 		 *     An object containing callbacks for instantiation and runtime
18354 		 * @param {Function} callbacks.onLoad(this)
18355 		 *     Callback to invoke upon successful instantiation
18356 		 * @param {Function} callbacks.onLoadError(rsp)
18357 		 *     Callback to invoke on instantiation REST request error
18358 		 *     as passed by finesse.clientservices.ClientServices.ajax()
18359 		 *     {
18360 		 *         status: {Number} The HTTP status code returned
18361 		 *         content: {String} Raw string of response
18362 		 *         object: {Object} Parsed object of response
18363 		 *         error: {Object} Wrapped exception that was caught
18364 		 *         error.errorType: {String} Type of error that was caught
18365 		 *         error.errorMessage: {String} Message associated with error
18366 		 *     }
18367 		 * @param {Function} callbacks.onChange(this)
18368 		 *     Callback to invoke upon successful update
18369 		 * @param {Function} callbacks.onError(rsp)
18370 		 *     Callback to invoke on update error (refresh or event)
18371 		 *     as passed by finesse.clientservices.ClientServices.ajax()
18372 		 *     {
18373 		 *         status: {Number} The HTTP status code returned
18374 		 *         content: {String} Raw string of response
18375 		 *         object: {Object} Parsed object of response
18376 		 *         error: {Object} Wrapped exception that was caught
18377 		 *         error.errorType: {String} Type of error that was caught
18378 		 *         error.errorMessage: {String} Message associated with error
18379 		 *     }
18380 		 *  
18381 	     * @constructs
18382 		 */
18383 		init: function (callbacks) {
18384 			this._super("", callbacks);
18385 			//when post is performed and id is empty
18386 			/*if (id === "") {
18387 				this._loaded = true;
18388 			}*/
18389 	        this._layoutxml = {};
18390 		},
18391 	
18392 		/**
18393 		 * Returns REST class of LayoutConfig object
18394 		 */
18395 		getRestClass: function () {
18396 			return LayoutConfig;
18397 		},
18398 	
18399 		/**
18400 		 * The type of this REST object is LayoutConfig
18401 		 */
18402 		getRestType: function () {
18403 			return "LayoutConfig";
18404 		},
18405 
18406 		/**
18407 		 * Gets the REST URL of this object.
18408 		 * 
18409 		 * If the parent has an id, the id is appended.
18410 		 * On occasions of POST, it will not have an id.
18411 		 */
18412 		getRestUrl: function () {
18413 			var layoutUri = "/finesse/api/" + this.getRestType() + "/default";
18414 			/*if (this._id) {
18415 				layoutUri = layoutUri + "/" + this._id;
18416 			}*/
18417 			return layoutUri;
18418 		},
18419 	
18420 		/**
18421 		 * This API does not support subscription
18422 		 */
18423 		supportsSubscriptions: false,
18424 		
18425 		keepRestResponse: true,
18426 
18427 
18428 		/**
18429 		 * Gets finesselayout.xml retrieved from the API call
18430 		 */
18431 		getLayoutxml: function () {
18432 			this.isLoaded();
18433 			var layoutxml = this.getData().layoutxml;
18434 
18435             // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with update())
18436             layoutxml = layoutxml.replace(/&/g,"&");
18437 
18438             return layoutxml;
18439 		},
18440 	
18441 		/**
18442 		 * Gets the type of this LayoutConfig object
18443 		 */
18444 		/*
18445 		getType: function () {
18446 			this.isLoaded();
18447 			return this.getData().type;
18448 		},*/
18449 	
18450 		/**
18451 		 * Retrieve the LayoutConfig settings.
18452 		 * If the id is not provided the API call will fail.
18453 		 * @returns {LayoutConfig}
18454 		 *     This LayoutConfig object to allow cascading.
18455 		 */
18456 		get: function () {      
18457 			this._synchronize();
18458 			return this;
18459 		},
18460 
18461 		/**
18462 		 * Closure handle updating of the internal data for the LayoutConfig object
18463 		 * upon a successful update (PUT) request before calling the intended
18464 		 * success handler provided by the consumer
18465 		 * 
18466 		 * @param {Object}
18467 		 *            layoutconfig Reference to this LayoutConfig object
18468 		 * @param {Object}
18469 		 *            LayoutConfig Object that contains the  settings to be
18470 		 *            submitted in the api request
18471 		 * @param {Function}
18472 		 *            successHandler The success handler specified by the consumer
18473 		 *            of this object
18474 		 * @returns {LayoutConfig} This LayoutConfig object to allow cascading
18475 		 */
18476 	
18477 		createPutSuccessHandler: function (layoutconfig, contentBody, successHandler) {
18478 			return function (rsp) {			
18479 				// Update internal structure based on response. Here we
18480 				// inject the contentBody from the PUT request into the
18481 				// rsp.object element to mimic a GET as a way to take
18482 				// advantage of the existing _processResponse method.
18483 				rsp.content = contentBody;
18484 				rsp.object.LayoutConfig = {};
18485 				rsp.object.LayoutConfig.finesseLayout = contentBody;
18486 				layoutconfig._processResponse(rsp);
18487 	
18488 				//Remove the injected layoutConfig object before cascading response
18489 				rsp.object.LayoutConfig = {};
18490 	
18491 				//cascade response back to consumer's response handler
18492 				successHandler(rsp);
18493 			};
18494 		},
18495 	
18496 		/**
18497 		 *  Update LayoutConfig
18498 		 * @param {Object} finesselayout
18499 		 *     The XML for FinesseLayout being stored
18500 		 * 
18501 		 * @param {Object} handlers
18502 		 *     An object containing callback handlers for the request. Optional.
18503 		 * @param {Function} options.success(rsp)
18504 		 *     A callback function to be invoked for a successful request.
18505 		 *     {
18506 		 *         status: {Number} The HTTP status code returned
18507 		 *         content: {String} Raw string of response
18508 		 *         object: {Object} Parsed object of response
18509 		 *     }
18510 		 * @param {Function} options.error(rsp)
18511 		 *     A callback function to be invoked for an unsuccessful request.
18512 		 *     {
18513 		 *         status: {Number} The HTTP status code returned
18514 		 *         content: {String} Raw string of response
18515 		 *         object: {Object} Parsed object of response (HTTP errors)
18516 		 *         error: {Object} Wrapped exception that was caught
18517 		 *         error.errorType: {String} Type of error that was caught
18518 		 *         error.errorMessage: {String} Message associated with error
18519 		 *     }
18520 		 * @returns {finesse.restservices.LayoutConfig}
18521 		 *     This LayoutConfig object to allow cascading
18522 		 */
18523 	
18524 		update: function (layoutxml, handlers) {
18525 			this.isLoaded();
18526 
18527 			
18528 			var contentBody = {}, 
18529 			//Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
18530 			re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
18531 
18532 			// We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
18533 			layoutxml = layoutxml.replace(/&(?!amp;)/g, "&");
18534 
18535 			//used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
18536 			layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
18537 
18538 			contentBody[this.getRestType()] = {
18539 				"layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
18540 			};
18541 
18542 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
18543 			handlers = handlers || {};
18544 
18545 			this.restRequest(this.getRestUrl(), {
18546 				method: 'PUT',
18547 				success: this.createPutSuccessHandler(this, layoutxml, handlers.success),
18548 				error: handlers.error,
18549 				content: contentBody
18550 			});
18551 
18552 			return this; // Allow cascading
18553 		}
18554 	
18555 		/**
18556 		 *TODO createPostSuccessHandler needs to be debugged to make it working
18557 		 * Closure handle creating new  LayoutConfig object
18558 		 * upon a successful create (POST) request before calling the intended
18559 		 * success handler provided by the consumer
18560 		 * 
18561 		 * @param {Object}
18562 		 *            layoutconfig Reference to this LayoutConfig object
18563 		 * @param {Object}
18564 		 *            LayoutConfig Object that contains the  settings to be
18565 		 *            submitted in the api request
18566 		 * @param {Function}
18567 		 *            successHandler The success handler specified by the consumer
18568 		 *            of this object
18569 		 * @returns {finesse.restservices.LayoutConfig} This LayoutConfig object to allow cascading
18570 		 */
18571 	/*
18572 		createPostSuccessHandler: function (layoutconfig, contentBody, successHandler) {
18573 			return function (rsp) {
18574 	
18575 				rsp.object = contentBody;
18576 				layoutconfig._processResponse(rsp);
18577 	
18578 				//Remove the injected layoutConfig object before cascading response
18579 				rsp.object = {};
18580 	
18581 				//cascade response back to consumer's response handler
18582 				successHandler(rsp);
18583 			};
18584 		}, */
18585 	
18586 		/**
18587 		 * TODO Method needs to be debugged to make POST working
18588 		 *  Add LayoutConfig
18589 		 * @param {Object} finesselayout
18590 		 *     The XML for FinesseLayout being stored
18591 		 * 
18592 		 * @param {Object} handlers
18593 		 *     An object containing callback handlers for the request. Optional.
18594 		 * @param {Function} options.success(rsp)
18595 		 *     A callback function to be invoked for a successful request.
18596 		 *     {
18597 		 *         status: {Number} The HTTP status code returned
18598 		 *         content: {String} Raw string of response
18599 		 *         object: {Object} Parsed object of response
18600 		 *     }
18601 		 * @param {Function} options.error(rsp)
18602 		 *     A callback function to be invoked for an unsuccessful request.
18603 		 *     {
18604 		 *         status: {Number} The HTTP status code returned
18605 		 *         content: {String} Raw string of response
18606 		 *         object: {Object} Parsed object of response (HTTP errors)
18607 		 *         error: {Object} Wrapped exception that was caught
18608 		 *         error.errorType: {String} Type of error that was caught
18609 		 *         error.errorMessage: {String} Message associated with error
18610 		 *     }
18611 		 * @returns {finesse.restservices.LayoutConfig}
18612 		 *     This LayoutConfig object to allow cascading
18613 		 */
18614 	/*
18615 		add: function (layoutxml, handlers) {
18616 			this.isLoaded();
18617 			var contentBody = {};
18618 	
18619 	
18620 			contentBody[this.getRestType()] = {
18621 					"layoutxml": layoutxml,
18622 					"type": "current"
18623 			    };
18624 	
18625 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
18626 			handlers = handlers || {};
18627 	
18628 			this.restRequest(this.getRestUrl(), {
18629 				method: 'POST',
18630 				success: this.createPostSuccessHandler(this, contentBody, handlers.success),
18631 				error: handlers.error,
18632 				content: contentBody
18633 			});
18634 	
18635 			return this; // Allow cascading
18636 		} */
18637 	});
18638 	
18639 	window.finesse = window.finesse || {};
18640     window.finesse.restservices = window.finesse.restservices || {};
18641     window.finesse.restservices.LayoutConfig = LayoutConfig;
18642     
18643 	return LayoutConfig;
18644 	
18645 });
18646 
18647 /**
18648  * JavaScript representation of the Finesse LayoutConfig object for a Team.
18649  *
18650  * @requires finesse.clientservices.ClientServices
18651  * @requires Class
18652  * @requires finesse.FinesseBase
18653  * @requires finesse.restservices.RestBase
18654  * @requires finesse.utilities.Utilities
18655  * @requires finesse.restservices.LayoutConfig
18656  */
18657 
18658 /** The following comment is to prevent jslint errors about 
18659  * using variables before they are defined.
18660  */
18661 /*global Exception */
18662 
18663 /** @private */
18664 define('restservices/TeamLayoutConfig',[
18665     'restservices/RestBase',
18666     'utilities/Utilities',
18667     'restservices/LayoutConfig'
18668 ],
18669 function (RestBase, Utilities, LayoutConfig) {
18670     
18671     var TeamLayoutConfig = RestBase.extend({
18672       // Keep the restresponse so we can parse the layoutxml out of it in getLayoutXML()
18673       keepRestResponse: true,
18674     
18675       /**
18676        * @class
18677        * JavaScript representation of a LayoutConfig object for a Team. Also exposes
18678        * methods to operate on the object against the server.
18679        *
18680        * @param {Object} options
18681        *     An object with the following properties:<ul>
18682        *         <li><b>id:</b> The id of the object being constructed</li>
18683        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18684        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18685        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18686        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18687        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18688        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18689        *             <li><b>content:</b> {String} Raw string of response</li>
18690        *             <li><b>object:</b> {Object} Parsed object of response</li>
18691        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18692        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18693        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18694        *             </ul></li>
18695        *         </ul></li>
18696        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18697        * @constructs
18698        **/
18699       init: function (options) {
18700           this._super(options);
18701       },
18702     
18703       /**
18704        * @private
18705        * Gets the REST class for the current object - this is the LayoutConfigs class.
18706        * @returns {Object} The LayoutConfigs class.
18707        */
18708       getRestClass: function () {
18709           return TeamLayoutConfig;
18710       },
18711     
18712       /**
18713        * @private
18714        * Gets the REST type for the current object - this is a "LayoutConfig".
18715        * @returns {String} The LayoutConfig string.
18716        */
18717       getRestType: function () {
18718           return "TeamLayoutConfig";
18719       },
18720     
18721       /**
18722        * @private
18723        * Override default to indicate that this object doesn't support making
18724        * requests.
18725        */
18726       supportsRequests: false,
18727     
18728       /**
18729        * @private
18730        * Override default to indicate that this object doesn't support subscriptions.
18731        */
18732       supportsSubscriptions: false,
18733     
18734       /**
18735        * Getter for the category.
18736        * @returns {String} The category.
18737        */
18738       getLayoutXML: function () {
18739           this.isLoaded();
18740           var layoutxml = this.getData().layoutxml;
18741 
18742           // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with put())
18743           layoutxml = layoutxml.replace(/&/g,"&");
18744 
18745           return layoutxml;
18746       },
18747     
18748       /**
18749        * Getter for the code.
18750        * @returns {String} The code.
18751        */
18752       getUseDefault: function () {
18753           this.isLoaded();
18754           return this.getData().useDefault;
18755       },
18756       
18757       /**
18758        * Retrieve the TeamLayoutConfig.
18759        *
18760        * @returns {finesse.restservices.TeamLayoutConfig}
18761        */
18762       get: function () {
18763           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
18764           this._id = "0";
18765           // set loaded to false so it will rebuild the collection after the get
18766           this._loaded = false;
18767           // reset collection
18768           this._collection = {};
18769           // perform get
18770           this._synchronize();
18771           return this;
18772       },
18773     
18774       createPutSuccessHandler: function(contact, contentBody, successHandler){
18775           return function (rsp) {
18776               // Update internal structure based on response. Here we
18777               // inject the contentBody from the PUT request into the
18778               // rsp.object element to mimic a GET as a way to take
18779               // advantage of the existing _processResponse method.
18780               rsp.object = contentBody;
18781               contact._processResponse(rsp);
18782     
18783               //Remove the injected Contact object before cascading response
18784               rsp.object = {};
18785               
18786               //cascade response back to consumer's response handler
18787               successHandler(rsp);
18788           };
18789       },
18790       
18791       put: function (newValues, handlers) {
18792           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
18793           this._id = "0";
18794           this.isLoaded();
18795 
18796           // We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
18797           var layoutxml = newValues.layoutXML.replace(/&(?!amp;)/g, "&"),
18798               contentBody = {}, 
18799               //Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
18800               re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
18801 
18802           //used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
18803           layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
18804           
18805           contentBody[this.getRestType()] = {
18806               "useDefault": newValues.useDefault,
18807               // The LayoutConfig restservice javascript class only translates ampersands, so we'll do that also
18808               "layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
18809           };
18810     
18811           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
18812           handlers = handlers || {};
18813     
18814           this.restRequest(this.getRestUrl(), {
18815               method: 'PUT',
18816               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
18817               error: handlers.error,
18818               content: contentBody
18819           });
18820     
18821           return this; // Allow cascading
18822       },
18823     
18824       getRestUrl: function(){
18825           // return team's url + /LayoutConfig
18826           // eg: /api/Team/1/LayoutConfig
18827           if(this._restObj === undefined){
18828               throw new Exception("TeamLayoutConfig instances must have a parent team object.");
18829           }
18830           return this._restObj.getRestUrl() + '/LayoutConfig';
18831       }
18832     
18833       });
18834         
18835     window.finesse = window.finesse || {};
18836     window.finesse.restservices = window.finesse.restservices || {};
18837     window.finesse.restservices.TeamLayoutConfig = TeamLayoutConfig;
18838       
18839     return TeamLayoutConfig;
18840 });
18841 
18842 /**
18843  * JavaScript representation of a TeamWorkflow.
18844  *
18845  * @requires finesse.clientservices.ClientServices
18846  * @requires Class
18847  * @requires finesse.FinesseBase
18848  * @requires finesse.restservices.RestBase
18849  */
18850 /** @private */
18851 define('restservices/TeamWorkflow',['restservices/RestBase'], function (RestBase) {
18852 
18853     var TeamWorkflow = RestBase.extend({
18854 
18855         /**
18856          * @class
18857          * JavaScript representation of a TeamWorkflow object. Also exposes
18858          * methods to operate on the object against the server.
18859          *
18860          * @param {Object} options
18861          *     An object with the following properties:<ul>
18862          *         <li><b>id:</b> The id of the object being constructed</li>
18863          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18864          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18865          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18866          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18867          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18868          *             <li><b>status:</b> {Number} The HTTP status description returned</li>
18869          *             <li><b>content:</b> {String} Raw string of response</li>
18870          *             <li><b>object:</b> {Object} Parsed object of response</li>
18871          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18872          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18873          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18874          *             </ul></li>
18875          *         </ul></li>
18876          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18877          * @constructs
18878          **/
18879         init: function (options) {
18880             this._super(options);
18881         },
18882 
18883         /**
18884          * @private
18885          * Gets the REST class for the current object - this is the TeamWorkflow class.
18886          * @returns {Object} The TeamWorkflow class.
18887          */
18888         getRestClass: function () {
18889             return TeamWorkflow;
18890         },
18891 
18892         /**
18893          * @private
18894          * Gets the REST type for the current object - this is a "Workflow".
18895          * @returns {String} The Workflow string.
18896          */
18897         getRestType: function () {
18898             return "Workflow";
18899         },
18900 
18901         /**
18902          * @private
18903          * Override default to indicate that this object doesn't support making
18904          * requests.
18905          */
18906         supportsRequests: false,
18907 
18908         /**
18909          * @private
18910          * Override default to indicate that this object doesn't support subscriptions.
18911          */
18912         supportsSubscriptions: false,
18913 
18914         /**
18915          * Getter for the name.
18916          * @returns {String} The name.
18917          */
18918         getName: function () {
18919             this.isLoaded();
18920             return this.getData().name;
18921         },
18922 
18923         /**
18924          * Getter for the description.
18925          * @returns {String} The description.
18926          */
18927         getDescription: function () {
18928             this.isLoaded();
18929             return this.getData().description;
18930         },
18931 
18932         /**
18933          * Getter for the Uri value.
18934          * @returns {String} The Uri.
18935          */
18936         getUri: function () {
18937             this.isLoaded();
18938             return this.getData().uri;
18939         }
18940 
18941     });
18942     
18943 	window.finesse = window.finesse || {};
18944     window.finesse.restservices = window.finesse.restservices || {};
18945     window.finesse.restservices.TeamWorkflow = TeamWorkflow;
18946 
18947     return TeamWorkflow;
18948 });
18949 
18950 /**
18951 * JavaScript representation of the TeamWorkflows collection
18952 * object which contains a list of TeamWorkflow objects.
18953  *
18954  * @requires finesse.clientservices.ClientServices
18955  * @requires Class
18956  * @requires finesse.FinesseBase
18957  * @requires finesse.restservices.RestBase
18958  * @requires finesse.restservices.Dialog
18959  * @requires finesse.restservices.RestCollectionBase
18960  */
18961 /** @private */
18962 define('restservices/TeamWorkflows',[
18963     'restservices/RestCollectionBase',
18964     'restservices/TeamWorkflow',
18965     'restservices/RestBase'
18966 ],
18967 function (RestCollectionBase, TeamWorkflow, RestBase) {
18968 
18969     var TeamWorkflows = RestCollectionBase.extend({
18970     
18971         /**
18972          * @class
18973          * JavaScript representation of a TeamWorkflows collection object. Also exposes
18974          * methods to operate on the object against the server.
18975          *
18976          * @param {Object} options
18977          *     An object with the following properties:<ul>
18978          *         <li><b>id:</b> The id of the object being constructed</li>
18979          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18980          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18981          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18982          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18983          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18984          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18985          *             <li><b>content:</b> {String} Raw string of response</li>
18986          *             <li><b>object:</b> {Object} Parsed object of response</li>
18987          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18988          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18989          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18990          *             </ul></li>
18991          *         </ul></li>
18992          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18993          * @constructs
18994          **/
18995         init: function (options) {
18996             this._super(options);
18997         },
18998 
18999         /**
19000          * @private
19001          * Gets the REST class for the current object - this is the TeamWorkflows class.
19002          */
19003         getRestClass: function () {
19004             return TeamWorkflows;
19005         },
19006 
19007         /**
19008          * @private
19009          * Gets the REST class for the objects that make up the collection. - this
19010          * is the TeamWorkflow class.
19011          */
19012         getRestItemClass: function () {
19013             return TeamWorkflow;
19014         },
19015 
19016         /**
19017          * @private
19018          * Gets the REST type for the current object - this is a "Workflows".
19019          */
19020         getRestType: function () {
19021             return "Workflows";
19022         },
19023 
19024         /**
19025          * Overrides the parent class.  Returns the url for the Workflows resource
19026          */
19027         getRestUrl: function () {
19028             var restObj = this._restObj, restUrl = "";
19029 
19030             //Prepend the base REST object if one was provided.
19031             //Otherwise prepend with the default webapp name.
19032             if (restObj instanceof RestBase) {
19033                 restUrl += restObj.getRestUrl();
19034             } else {
19035                 restUrl += "/finesse/api/Team";
19036             }
19037             //Append ID if it is not undefined, null, or empty.
19038             if (this._id) {
19039                 restUrl += "/" + this._id;
19040             }
19041             //Append the REST type.
19042             restUrl += "/Workflows";
19043             
19044             return restUrl;
19045         },
19046 
19047         /**
19048          * @private
19049          * Gets the REST type for the objects that make up the collection - this is "Workflow".
19050          */
19051         getRestItemType: function () {
19052             return "Workflow";
19053         },
19054 
19055         /**
19056          * @private
19057          * Override default to indicates that the collection supports making requests.
19058          */
19059         supportsRequests: true,
19060 
19061         /**
19062          * @private
19063          * Override default to indicates that the collection does not subscribe to its objects.
19064          */
19065         supportsRestItemSubscriptions: false,
19066 
19067         /**
19068          * Retrieve the Sign Out Reason Codes.
19069          *
19070          * @returns {finesse.restservices.TeamWorkflows}
19071          *     This TeamWorkflows object to allow cascading.
19072          */
19073         get: function () {
19074             // set loaded to false so it will rebuild the collection after the get
19075             this._loaded = false;
19076             // reset collection
19077             this._collection = {};
19078             // perform get
19079             this._synchronize();
19080             return this;
19081         },
19082 
19083         /* We only use PUT and GET on Reason Code team assignments
19084          * @param {Object} contact
19085          * @param {Object} contentBody
19086          * @param {Function} successHandler
19087          */
19088         createPutSuccessHandler: function (contact, contentBody, successHandler) {
19089             return function (rsp) {
19090                 // Update internal structure based on response. Here we
19091                 // inject the contentBody from the PUT request into the
19092                 // rsp.object element to mimic a GET as a way to take
19093                 // advantage of the existing _processResponse method.
19094                 rsp.object = contentBody;
19095                 contact._processResponse(rsp);
19096 
19097                 //Remove the injected contentBody object before cascading response
19098                 rsp.object = {};
19099 
19100                 //cascade response back to consumer's response handler
19101                 successHandler(rsp);
19102             };
19103         },
19104 
19105         /**
19106          * Update - This should be all that is needed.
19107          * @param {Object} newValues
19108          * @param {Object} handlers
19109          * @returns {finesse.restservices.TeamWorkflows}
19110          *     This TeamWorkflows object to allow cascading.
19111          */
19112         update: function (newValues, handlers) {
19113             this.isLoaded();
19114             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
19115 
19116             contentBody[this.getRestType()] = {
19117             };
19118 
19119             for (i in newValues) {
19120                 if (newValues.hasOwnProperty(i)) {
19121                     innerObject = {
19122                         "uri": newValues[i]
19123                     };
19124                     contentBodyInner.push(innerObject);
19125                 }
19126             }
19127 
19128             contentBody[this.getRestType()] = {
19129                 "Workflow" : contentBodyInner
19130             };
19131 
19132             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
19133             handlers = handlers || {};
19134 
19135             this.restRequest(this.getRestUrl(), {
19136                 method: 'PUT',
19137                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
19138                 error: handlers.error,
19139                 content: contentBody
19140             });
19141 
19142             return this; // Allow cascading
19143         }
19144 
19145     });
19146     
19147 	window.finesse = window.finesse || {};
19148     window.finesse.restservices = window.finesse.restservices || {};
19149     window.finesse.restservices.TeamWorkflows = TeamWorkflows;
19150     
19151     return TeamWorkflows;
19152 });
19153 
19154 /**
19155  * JavaScript representation of the Finesse TeamMessage Message object.
19156  *
19157  * @requires finesse.clientservices.ClientServices
19158  * @requires Class
19159  * @requires finesse.FinesseBase
19160  * @requires finesse.restservices.RestBase
19161  */
19162 
19163 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
19164 /*global define,finesse */
19165 
19166 /** @private */
19167 define('restservices/TeamMessage',[
19168         'restservices/RestBase'
19169     ],
19170     function (RestBase) {
19171 
19172         var TeamMessage = RestBase.extend({
19173 
19174             /**
19175              * @class
19176              * JavaScript representation of a TeamMessage message object. Also exposes
19177              * methods to operate on the object against the server.
19178              *
19179              * @param {Object} options
19180              *     An object with the following properties:<ul>
19181              *         <li><b>id:</b> The id of the object being constructed</li>
19182              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
19183              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
19184              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
19185              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
19186              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
19187              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
19188              *             <li><b>content:</b> {String} Raw string of response</li>
19189              *             <li><b>object:</b> {Object} Parsed object of response</li>
19190              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
19191              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
19192              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
19193              *             </ul></li>
19194              *         </ul></li>
19195              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
19196              * @constructs
19197              **/
19198             init: function (options) {
19199                 this._super(options);
19200             },
19201 
19202             /**
19203              * @private
19204              * Gets the REST class for the current object - this is the TeamMessage class.
19205              * @returns {Object} The TeamMessage class.
19206              */
19207             getRestClass: function () {
19208                 throw new Error("getRestClass(): Not implemented in subtype.");
19209             },
19210 
19211             /**
19212              * @private
19213              * Gets the REST type for the current object - this is a "TeamMessage".
19214              * @returns {String} The Workflow string.
19215              */
19216             getRestType: function () {
19217                 return "TeamMessage";
19218             },
19219 
19220 
19221             /**
19222              * @private
19223              * Override default to indicate that this object doesn't support making
19224              * requests.
19225              */
19226             supportsRequests: false,
19227 
19228             /**
19229              * @private
19230              * Override default to indicate that this object doesn't support subscriptions.
19231              */
19232             supportsSubscriptions: false,
19233 
19234             /**
19235              * Getter for the TeamMessageuri value.
19236              * @returns {String} uri.
19237              */
19238             getTeamMessageUri: function () {
19239                 this.isLoaded();
19240                 return this.getData().uri;
19241             },
19242 
19243             /**
19244              * Getter for the teamMessage id value.
19245              * @returns {String} id.
19246              */
19247             getId: function () {
19248                 this.isLoaded();
19249                 return this.getData().id;
19250             },
19251 
19252             /**
19253              * Getter for the teamMessage createdBy value.
19254              * @returns {String} createdBy.
19255              */
19256             getCreatedBy: function () {
19257                 this.isLoaded();
19258                 return this.getData().createdBy;
19259             },
19260             /**
19261              * Getter for the teamMessage createdAt value.
19262              * @returns {String} createdAt.
19263              */
19264             getCreatedAt: function () {
19265                 this.isLoaded();
19266                 return this.getData().createdAt;
19267             },
19268 
19269             /**
19270              * Getter for the teamMessage duration value.
19271              * @returns {String} duration.
19272              */
19273             getDuration: function () {
19274                 this.isLoaded();
19275                 return this.getData().duration;
19276             },
19277             /**
19278              * Getter for the teamMessage content value.
19279              * @returns {String} content.
19280              */
19281             getContent: function () {
19282                 this.isLoaded();
19283                 return this.getData().content;
19284 
19285             },
19286 
19287             add: function (newValues, handlers) {
19288                 // this.isLoaded();
19289                 var contentBody = {};
19290 
19291                 contentBody[this.getRestType()] = {
19292                     "duration": newValues.expires,
19293                     "content": newValues.message,
19294                     "teams": newValues.teams
19295 
19296                 };
19297 
19298                 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
19299                 handlers = handlers || {};
19300 
19301                 this.restRequest(this.getRestUrl(), {
19302                     method: 'POST',
19303                     success: handlers.success,
19304                     error: handlers.error,
19305                     content: contentBody
19306                 });
19307 
19308                 return this; // Allow cascading
19309             }
19310 
19311         });
19312 
19313         window.finesse = window.finesse || {};
19314         window.finesse.restservices = window.finesse.restservices || {};
19315         window.finesse.restservices.TeamMessage = TeamMessage;
19316 
19317         return TeamMessage;
19318     });
19319 
19320 /**
19321  * JavaScript representation of the Finesse TeamMessages object for a Team.
19322  *
19323  * @requires Class
19324  * @requires finesse.FinesseBase
19325  * @requires finesse.restservices.RestBase
19326  * @requires finesse.utilities.Utilities
19327  * @requires finesse.restservices.TeamMessage
19328  */
19329 
19330 /** The following comment is to prevent jslint errors about 
19331  * using variables before they are defined.
19332  */
19333 /*global Exception */
19334 
19335 /** @private */
19336 define('restservices/TeamMessages',[
19337         'restservices/RestCollectionBase',
19338         'restservices/RestBase',
19339         'restservices/TeamMessage',
19340         'utilities/Utilities'
19341     ],
19342     function (RestCollectionBase, RestBase, TeamMessage, Utilities) {
19343 
19344         var TeamMessages = RestCollectionBase.extend({
19345 
19346             /**
19347              * @class
19348              * JavaScript representation of a TeamMessages object for a Team. Also exposes
19349              * methods to operate on the object against the server.
19350              *
19351              * @param {Object} options
19352              *     An object with the following properties:<ul>
19353              *         <li><b>id:</b> The id of the object being constructed</li>
19354              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
19355              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
19356              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
19357              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
19358              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
19359              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
19360              *             <li><b>content:</b> {String} Raw string of response</li>
19361              *             <li><b>object:</b> {Object} Parsed object of response</li>
19362              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
19363              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
19364              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
19365              *             </ul></li>
19366              *         </ul></li>
19367              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
19368              * @constructs
19369              **/
19370             init: function (options) {
19371                 this._super(options);
19372             },
19373 
19374             /**
19375              * Gets the REST class for the current object - this is the TeamMessages class.
19376              * @returns {Object} The TeamMessages class.
19377              */
19378             getRestClass: function () {
19379                 return TeamMessages;
19380             },
19381             /**
19382              * Gets the REST class for the rest item - this is the TeamMessages class.
19383              * @returns {Object} The TeamMessages class.
19384              */
19385             getRestItemClass: function () {
19386                 return TeamMessage;
19387             },
19388 
19389             /**
19390              * Gets the REST type for the current object - that is a "teamMessages" class.
19391              * @returns {String} The teamMessages string.
19392              */
19393             getRestType: function () {
19394                 return "teamMessages";
19395             },
19396 
19397             /**
19398              * Gets the REST type for the current object - this is a "TeamMessage" class.
19399              * @returns {String} The TeamMessage string.
19400              */
19401             getRestItemType: function () {
19402                 return "TeamMessage";
19403             },
19404 
19405 
19406             /**
19407              * @private
19408              * Override default to indicate that this object support making
19409              * requests.
19410              */
19411             supportsRequests: true,
19412 
19413             /**
19414              * @private
19415              * Override default to indicate that this object support subscriptions.
19416              */
19417             supportsSubscriptions: true,
19418             /**
19419              * @private
19420              * Override default to indicates that the collection subscribes to its objects.
19421              */
19422             supportsRestItemSubscriptions: true,
19423 
19424             /**
19425              * Getter for the teamMessages.
19426              * @returns {Object} teamMessages.
19427              */
19428             getBroadcastMessages: function () {
19429                 this.isLoaded();
19430                 return this.getData;
19431             },
19432 
19433 
19434             getRestUrl: function () {
19435                 // return team's url + /TeamMessages
19436                 // eg: /api/Team/1/TeamMessages
19437                 if (this._restObj === undefined) {
19438                     throw new Exception("Broadcast message instances must have a parent team object.");
19439                 }
19440                 return this._restObj.getRestUrl() + '/TeamMessages';
19441             },
19442 
19443             _buildCollection: function (data) {
19444                 var i, object, objectId, dataArray;
19445                 if (data && this.getProperty(data, this.getRestItemType()) !== null) {
19446                     dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
19447                     for (i = 0; i < dataArray.length; i += 1) {
19448 
19449                         object = {};
19450                         object[this.getRestItemType()] = dataArray[i];
19451 
19452                         //get id from object.id instead of uri, since uri is not there for some reason
19453                         objectId = object[this.getRestItemType()].id; //this._extractId(object);
19454 
19455                         //create the Media Object
19456                         this._collection[objectId] = new(this.getRestItemClass())({
19457                             id: objectId,
19458                             data: object,
19459                             parentObj: this._restObj
19460                         });
19461                         this.length += 1;
19462                     }
19463                 }
19464             }
19465 
19466         });
19467 
19468         window.finesse = window.finesse || {};
19469         window.finesse.restservices = window.finesse.restservices || {};
19470         window.finesse.restservices.TeamMessages = TeamMessages;
19471 
19472         return TeamMessages;
19473     });
19474 
19475 /**
19476  * JavaScript representation of the Finesse Team REST object.
19477  *
19478  * @requires finesse.clientservices.ClientServices
19479  * @requires Class
19480  * @requires finesse.FinesseBase
19481  * @requires finesse.restservices.RestBase
19482  * @requires finesse.restservices.RestCollectionBase
19483  * @requires finesse.restservices.User
19484  * @requires finesse.restservices.Users
19485  */
19486 
19487 /**
19488  * The following comment prevents JSLint errors concerning undefined global variables.
19489  * It tells JSLint that these identifiers are defined elsewhere.
19490  */
19491 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
19492 
19493 /** The following comment is to prevent jslint errors about 
19494  * using variables before they are defined.
19495  */
19496 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
19497 
19498 /** @private */
19499 define('restservices/Team',[
19500     'restservices/RestBase',
19501     'utilities/Utilities',
19502     'restservices/Users',
19503     'restservices/TeamNotReadyReasonCodes',
19504     'restservices/TeamWrapUpReasons',
19505     'restservices/TeamSignOutReasonCodes',
19506     'restservices/TeamPhoneBooks',
19507     'restservices/TeamLayoutConfig',
19508     'restservices/TeamWorkflows',
19509     'restservices/TeamMessages'
19510 ],
19511 function (RestBase, Utilities, Users, TeamNotReadyReasonCodes, TeamWrapUpReasons, TeamSignOutReasonCodes, TeamPhoneBooks, TeamLayoutConfig, TeamWorkflows, TeamMessages) {
19512     var Team = RestBase.extend(/** @lends finesse.restservices.Team.prototype */{
19513         
19514         _teamLayoutConfig: null,
19515 
19516         /**
19517          *
19518          * @class
19519          * JavaScript representation of a Team object. Also exposes methods to operate
19520          * on the object against the server.
19521          *
19522          * @param {Object} options
19523          *     An object with the following properties:<ul>
19524          *         <li><b>id:</b> The id of the object being constructed</li>
19525          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
19526          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
19527          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
19528          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
19529          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
19530          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
19531          *             <li><b>content:</b> {String} Raw string of response</li>
19532          *             <li><b>object:</b> {Object} Parsed object of response</li>
19533          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
19534          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
19535          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
19536          *             </ul></li>
19537          *         </ul></li>
19538          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
19539          * @augments finesse.restservices.RestBase
19540          * @constructs
19541          * @example
19542          *      _team = new finesse.restservices.Team({
19543          *                      id: _id,
19544          *                      onLoad : _handleTeamLoad(team),
19545          *                      onChange : _handleTeamChange(team)
19546          *      });         
19547          **/
19548         init: function (options) {
19549             this._super(options);
19550         },
19551     
19552         /**
19553          * @private
19554          * Gets the REST class for the current object - this is the Team class.
19555          * @returns {Object} The Team constructor.
19556          */
19557         getRestClass: function () {
19558             return finesse.restesrvices.Team;
19559         },
19560     
19561         /**
19562          * @private
19563          * Gets the REST type for the current object - this is a "Team".
19564          * @returns {String} The Team string.
19565          */
19566         getRestType: function () {
19567             return "Team";
19568         },
19569     
19570         /**
19571          * @private
19572          * Override default to indicate that this object doesn't support making
19573          * requests.
19574          */
19575         supportsSubscriptions: false,
19576     
19577         /**
19578          * Getter for the team id.
19579          * @returns {String} The team id.
19580          */
19581         getId: function () {
19582             this.isLoaded();
19583             return this.getData().id;
19584         },
19585     
19586         /**
19587          * Getter for the team name.
19588          * @returns {String} The team name
19589          */
19590         getName: function () {
19591             this.isLoaded();
19592             return this.getData().name;
19593         },
19594     
19595         /**
19596          * @private
19597          * Getter for the team uri.
19598          * @returns {String} The team uri
19599          */
19600         getUri: function () {
19601             this.isLoaded();
19602             return this.getData().uri;        
19603         },
19604     
19605         /**
19606          * Constructs and returns a collection of Users.
19607          * @param {Object} options that sets callback handlers.
19608          * 		An object with the following properties:<ul>
19609          *         <li><b>onLoad(users): (optional)</b> when the object is successfully loaded from the server</li>
19610          *         <li><b>onError(): (optional)</b> if loading of the object fails, invoked with the error response object</li>
19611          * @returns {finesse.restservices.Users} Users collection of User objects.
19612          */
19613         getUsers: function (options) {
19614             this.isLoaded();
19615             options = options || {};
19616     
19617             options.parentObj = this;
19618             // We are using getData() instead of getData.Users because the superclass (RestCollectionBase)
19619             // for Users needs the "Users" key to validate the provided payload matches the class type.
19620             options.data = this.getData();
19621     
19622             return new Users(options);
19623         },
19624     
19625         /**
19626          * @private
19627          * Getter for a teamNotReadyReasonCodes collection object that is associated with Team.
19628          * @param callbacks
19629          * @returns {teamNotReadyReasonCodes}
19630          *     A teamNotReadyReasonCodes collection object.
19631          */
19632         getTeamNotReadyReasonCodes: function (callbacks) {
19633             var options = callbacks || {};
19634             options.parentObj = this;
19635             this.isLoaded();
19636     
19637             if (!this._teamNotReadyReasonCodes) {
19638                 this._teamNotReadyReasonCodes = new TeamNotReadyReasonCodes(options);
19639             }
19640     
19641             return this._teamNotReadyReasonCodes;
19642         },
19643     
19644         /**
19645          * @private
19646          * Getter for a teamWrapUpReasons collection object that is associated with Team.
19647          * @param callbacks
19648          * @returns {teamWrapUpReasons}
19649          *     A teamWrapUpReasons collection object.
19650          */
19651         getTeamWrapUpReasons: function (callbacks) {
19652             var options = callbacks || {};
19653             options.parentObj = this;
19654             this.isLoaded();
19655     
19656             if (!this._teamWrapUpReasons) {
19657                 this._teamWrapUpReasons = new TeamWrapUpReasons(options);
19658             }
19659     
19660             return this._teamWrapUpReasons;
19661         },
19662     
19663         /**
19664          * @private
19665          * Getter for a teamSignOutReasonCodes collection object that is associated with Team.
19666          * @param callbacks
19667          * @returns {teamSignOutReasonCodes}
19668          *     A teamSignOutReasonCodes collection object.
19669          */
19670     
19671         getTeamSignOutReasonCodes: function (callbacks) {
19672             var options = callbacks || {};
19673             options.parentObj = this;
19674             this.isLoaded();
19675     
19676             if (!this._teamSignOutReasonCodes) {
19677                 this._teamSignOutReasonCodes = new TeamSignOutReasonCodes(options);
19678             }
19679     
19680             return this._teamSignOutReasonCodes;
19681         },
19682     
19683         /**
19684          * @private
19685          * Getter for a teamPhoneBooks collection object that is associated with Team.
19686          * @param callbacks
19687          * @returns {teamPhoneBooks}
19688          *     A teamPhoneBooks collection object.
19689          */
19690         getTeamPhoneBooks: function (callbacks) {
19691             var options = callbacks || {};
19692             options.parentObj = this;
19693             this.isLoaded();
19694     
19695             if (!this._phonebooks) {
19696                 this._phonebooks = new TeamPhoneBooks(options);
19697             }
19698     
19699             return this._phonebooks;
19700         },
19701     
19702         /**
19703          * @private
19704          * Getter for a teamWorkflows collection object that is associated with Team.
19705          * @param callbacks
19706          * @returns {teamWorkflows}
19707          *     A teamWorkflows collection object.
19708          */
19709         getTeamWorkflows: function (callbacks) {
19710             var options = callbacks || {};
19711             options.parentObj = this;
19712             this.isLoaded();
19713     
19714             if (!this._workflows) {
19715                 this._workflows = new TeamWorkflows(options);
19716             }
19717     
19718             return this._workflows;
19719         },
19720     
19721         /**
19722          * @private
19723          * Getter for a teamLayoutConfig object that is associated with Team.
19724          * @param callbacks
19725          * @returns {teamLayoutConfig}
19726          */
19727         getTeamLayoutConfig: function (callbacks) {
19728             var options = callbacks || {};
19729             options.parentObj = this;
19730             this.isLoaded();
19731     
19732             if (this._teamLayoutConfig === null) {
19733                 this._teamLayoutConfig = new TeamLayoutConfig(options);
19734             }
19735     
19736             return this._teamLayoutConfig;
19737         },
19738         /**
19739          * @private
19740          * Getter for the teamMessages object that is associated with Team.
19741          * @param callbacks
19742          * @returns {teamMessages}
19743          */
19744         getTeamMessages: function (callbacks) {
19745             var options = callbacks || {};
19746             options.parentObj = this;
19747             this.isLoaded();
19748             if(!this._teamMessages) {
19749                 this._teamMessages = new TeamMessages(options);
19750             }
19751             return this._teamMessages;
19752         }
19753     
19754     });
19755     
19756     window.finesse = window.finesse || {};
19757     window.finesse.restservices = window.finesse.restservices || {};
19758     window.finesse.restservices.Team = Team;
19759     
19760     return Team;    
19761 });
19762 
19763 /**
19764  * JavaScript representation of the Finesse Teams collection.
19765  * object which contains a list of Team objects
19766  * @requires finesse.clientservices.ClientServices
19767  * @requires Class
19768  * @requires finesse.FinesseBase
19769  * @requires finesse.restservices.RestBase
19770  * @requires finesse.restservices.RestCollectionBase
19771  */
19772 
19773 /** @private */
19774 define('restservices/Teams',[
19775     'restservices/RestCollectionBase',
19776     'restservices/Team'
19777 ],
19778 function (RestCollectionBase, Team) {
19779     /** @private */
19780     var Teams = RestCollectionBase.extend({
19781 
19782         /**
19783          * @class
19784          * JavaScript representation of a Teams collection object. Also exposes methods to operate
19785          * on the object against the server.
19786          *
19787          * @param {Object} options
19788          *     An object with the following properties:<ul>
19789          *         <li><b>id:</b> The id of the object being constructed</li>
19790          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
19791          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
19792          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
19793          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
19794          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
19795          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
19796          *             <li><b>content:</b> {String} Raw string of response</li>
19797          *             <li><b>object:</b> {Object} Parsed object of response</li>
19798          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
19799          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
19800          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
19801          *             </ul></li>
19802          *         </ul></li>
19803          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
19804          * @constructs
19805          **/
19806         init: function (options) {
19807             this._super(options);
19808         },
19809 
19810         /**
19811          * @private
19812          * Gets the REST class for the current object - this is the Teams class.
19813          * @returns {Object} The Teams constructor.
19814          */
19815         getRestClass: function () {
19816             return Teams;
19817         },
19818 
19819         /**
19820          * @private
19821          * Gets the REST class for the objects that make up the collection. - this
19822          * is the Team class.
19823          */
19824         getRestItemClass: function () {
19825             return Team;
19826         },
19827 
19828         /**
19829          * @private
19830          * Gets the REST type for the current object - this is a "Teams".
19831          * @returns {String} The Teams string.
19832          */
19833         getRestType: function () {
19834             return "Teams";
19835         },
19836         
19837         /**
19838          * @private
19839          * Gets the REST type for the objects that make up the collection - this is "Team".
19840          */
19841         getRestItemType: function () {
19842             return "Team";
19843         },
19844 
19845         /**
19846          * @private
19847          * Override default to indicates that the collection supports making
19848          * requests.
19849          */
19850         supportsRequests: true,
19851 
19852         /**
19853          * @private
19854          * Override default to indicate that this object doesn't support subscriptions.
19855          */
19856         supportsRestItemSubscriptions: false,
19857         
19858         /**
19859          * @private
19860          * Retrieve the Teams.  This call will re-query the server and refresh the collection.
19861          *
19862          * @returns {finesse.restservices.Teams}
19863          *     This Teams object to allow cascading.
19864          */
19865         get: function () {
19866             // set loaded to false so it will rebuild the collection after the get
19867             this._loaded = false;
19868             // reset collection
19869             this._collection = {};
19870             // perform get
19871             this._synchronize();
19872             return this;
19873         }
19874 
19875     });
19876 
19877     window.finesse = window.finesse || {};
19878     window.finesse.restservices = window.finesse.restservices || {};
19879     window.finesse.restservices.Teams = Teams;
19880     
19881     return Teams;
19882 });
19883 
19884 /**
19885  * JavaScript representation of the Finesse SystemInfo object
19886  *
19887  * @requires finesse.clientservices.ClientServices
19888  * @requires Class
19889  * @requires finesse.FinesseBase
19890  * @requires finesse.restservices.RestBase
19891  */
19892 
19893 /** @private */
19894 define('restservices/SystemInfo',['restservices/RestBase'], function (RestBase) {
19895     
19896     var SystemInfo = RestBase.extend(/** @lends finesse.restservices.SystemInfo.prototype */{
19897         /**
19898          * @private
19899          * Returns whether this object supports subscriptions
19900          */
19901         supportsSubscriptions: false,
19902 
19903         doNotRefresh: true,
19904       
19905         /**
19906          * @class
19907          * JavaScript representation of a SystemInfo object.
19908          * 
19909          * @augments finesse.restservices.RestBase
19910          * @see finesse.restservices.SystemInfo.Statuses
19911          * @constructs
19912          */
19913         _fakeConstuctor: function () {
19914             /* This is here to hide the real init constructor from the public docs */
19915         },
19916         
19917          /**
19918          * @private
19919          * JavaScript representation of a SystemInfo object. Also exposes methods to operate
19920          * on the object against the server.
19921          *
19922          * @param {Object} options
19923          *     An object with the following properties:<ul>
19924          *         <li><b>id:</b> The id of the object being constructed</li>
19925          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
19926          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
19927          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
19928          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
19929          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
19930          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
19931          *             <li><b>content:</b> {String} Raw string of response</li>
19932          *             <li><b>object:</b> {Object} Parsed object of response</li>
19933          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
19934          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
19935          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
19936          *             </ul></li>
19937          *         </ul></li>
19938          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
19939          **/
19940         init: function (id, callbacks, restObj)
19941         {
19942             this._super(id, callbacks, restObj);
19943         },
19944 
19945         /**
19946          * @private
19947          * Gets the REST class for the current object - this is the SystemInfo object.
19948          * @returns {Object} The SystemInfo constructor.
19949          */
19950         getRestClass: function () {
19951             return SystemInfo;
19952         },
19953 
19954         /**
19955          * @private
19956          * Gets the REST type for the current object - this is a "SystemInfo".
19957          * @returns {String} The SystemInfo string.
19958          */
19959         getRestType: function ()
19960         {
19961             return "SystemInfo";
19962         },
19963         
19964         _validate: function (obj)
19965         {
19966             return true;
19967         },
19968         
19969         /**
19970          * Returns the status of the Finesse system.
19971          *   IN_SERVICE if the Finesse API reports that it is in service,
19972          *   OUT_OF_SERVICE otherwise.
19973          * @returns {finesse.restservices.SystemInfo.Statuses} System Status
19974          */
19975         getStatus: function () {
19976             this.isLoaded();
19977             return this.getData().status;
19978         },
19979         
19980         /**
19981          * Returns the lastCTIHeartbeatStatus
19982          *   success - last CTI heartbeat status was successful.
19983          *   failure - last CTI heartbeat status was unsuccessful.
19984          * @returns {finesse.restservices.SystemInfo.lastCTIHeartbeatStatus} Last Heartbeat  to CTI was successful or not.
19985          */
19986         getLastCTIHeartbeatStatus: function () {
19987             this.isLoaded();
19988             return this.getData().lastCTIHeartbeatStatus;
19989         },
19990         
19991         /**
19992          * Returns the reason due to which Finesse is OUT OF SERVICE.
19993          * It returns empty string when Finesse status is IN_SERVICE.
19994          * @returns {String} statusReason if finesse is OUT OF SERVICE , or empty string otherwise.
19995          */
19996         getStatusReason: function () {
19997             this.isLoaded();
19998             return this.getData().statusReason;
19999         },
20000         
20001         /**
20002          * Returns the current timestamp from this SystemInfo object.
20003          *   This is used to calculate time drift delta between server and client.
20004          *  @returns {String} Time (GMT): yyyy-MM-dd'T'HH:mm:ss'Z'
20005          */
20006         getCurrentTimestamp: function () {
20007             this.isLoaded();
20008             return this.getData().currentTimestamp;
20009         },
20010         
20011         /**
20012          * Getter for the xmpp domain of the system.
20013          * @returns {String} The xmpp domain corresponding to this SystemInfo object.
20014          */
20015         getXmppDomain: function () {
20016             this.isLoaded();
20017             return this.getData().xmppDomain;
20018         },
20019         
20020         /**
20021          * Getter for the xmpp pubsub domain of the system.
20022          * @returns {String} The xmpp pubsub domain corresponding to this SystemInfo object.
20023          */
20024         getXmppPubSubDomain: function () {
20025             this.isLoaded();
20026             return this.getData().xmppPubSubDomain;
20027         },
20028 
20029         /**
20030          * Getter for the deployment type (UCCE or UCCX).
20031          * @returns {String} "UCCE" or "UCCX"
20032          */ 
20033         getDeploymentType: function () {
20034             this.isLoaded();
20035             return this.getData().deploymentType;
20036         },
20037 
20038         /**
20039          * Returns whether this is a single node deployment or not by checking for the existence of the secondary node in SystemInfo.
20040          * @returns {Boolean} True for single node deployments, false otherwise.
20041          */ 
20042         isSingleNode: function () {
20043             var secondary = this.getData().secondaryNode;
20044             if (secondary && secondary.host) {
20045                 return false;
20046             }
20047             return true;
20048         },
20049 
20050         /**
20051          * This is useful for getting the FQDN of the other Finesse server, i.e. for failover purposes.
20052          * @param {String} arguments - any number of arguments to match against
20053          * @returns {String} FQDN (if properly configured) of the alternate node, defaults to primary if no match is found, undefined for single node deployments.
20054          */ 
20055         getAlternateHost: function () {
20056             var i,
20057             isPrimary = false,
20058             primary = this.getData().primaryNode,
20059             secondary = this.getData().secondaryNode,
20060             xmppDomain = this.getData().xmppDomain,
20061             alternateHost;
20062 
20063             if (primary && primary.host) {
20064                     if (xmppDomain === primary.host) {
20065                         isPrimary = true;
20066                     }
20067                 if (secondary && secondary.host) {
20068                     if (isPrimary) {
20069                         return secondary.host;
20070                     }
20071                     return primary.host;
20072                 }
20073             }
20074         },
20075         
20076         /**
20077          * Gets the peripheral ID that Finesse is connected to. The peripheral
20078          * ID is the ID of the PG Routing Client (PIM).
20079          * 
20080          * @returns {String} The peripheral Id if UCCE, or empty string otherwise.
20081          */
20082         getPeripheralId : function () {
20083              this.isLoaded();
20084              
20085              var peripheralId = this.getData().peripheralId;
20086              if (peripheralId === null) {
20087                   return "";
20088              } else {
20089                   return this.getData().peripheralId;
20090              }
20091         },
20092 
20093          /**
20094          * Gets the license. Only apply to UCCX.
20095          * 
20096          * @returns {String} The license if UCCX, or empty string otherwise.
20097          */
20098         getlicense : function () {
20099              this.isLoaded();
20100              return this.getData().license || "";
20101         },
20102         
20103         /**
20104          * Gets the systemAuthMode for the current deployment
20105          * 
20106          * @returns {String} The System auth mode for current deployment
20107          */
20108         getSystemAuthMode : function() {
20109             this.isLoaded();
20110             return this.getData().systemAuthMode;
20111         },
20112 
20113         /**
20114          * Gets the ctiVersion for the current deployment
20115          * 
20116          * @returns {String} The CTI version used in finesse to connect CTI server
20117          */
20118         getCtiVersion : function() {
20119             this.isLoaded();
20120             return this.getData().ctiVersion;
20121         },
20122 
20123         /**
20124          * Gets the ctiHeartbeatInterval for the current deployment
20125          * 
20126          * @returns {String} The ctiHeartbeatInterval used in finesse to connect CTI server
20127          */
20128         getHeartbeatInterval : function() {
20129             this.isLoaded();
20130             return this.getData().ctiHeartbeatInterval;
20131         },
20132 
20133         /**
20134          * Gets the failover time slot assigned to an agent. It is used in the client side failover logic.
20135          * 
20136          * @returns {String} A sequence number
20137          */
20138         getFailoverTimeSlot : function() {
20139             this.isLoaded();
20140             return this.getData().failoverTimeSlot;
20141         },
20142 
20143         /**
20144          * Gets the time zone offset
20145          * 
20146          * @returns {String} time zone offset
20147          */
20148         getTimeZoneOffset: function() {
20149             this.isLoaded();
20150             return this.getData().timezoneOffset;
20151         }
20152 
20153     });
20154     
20155     SystemInfo.Statuses = /** @lends finesse.restservices.SystemInfo.Statuses.prototype */ { 
20156         /** 
20157          * Finesse is in service. 
20158          */
20159         IN_SERVICE: "IN_SERVICE",
20160         /** 
20161          * Finesse is not in service. 
20162          */
20163         OUT_OF_SERVICE: "OUT_OF_SERVICE",
20164         /**
20165          * @class SystemInfo status values.
20166          * @constructs
20167          */
20168         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
20169 
20170     };
20171     
20172     window.finesse = window.finesse || {};
20173     window.finesse.restservices = window.finesse.restservices || {};
20174     window.finesse.restservices.SystemInfo = SystemInfo;
20175     
20176     return SystemInfo;
20177 });
20178 
20179 define('restservices/DialogLogoutActions',[], function ()
20180 {
20181     var DialogLogoutActions = /** @lends finesse.restservices.DialogLogoutActions.prototype */ {
20182 
20183         /**
20184          * Set this action to close active dialogs when the agent logs out.
20185          */
20186         CLOSE: "CLOSE",
20187 
20188         /**
20189          * Set this action to transfer active dialogs when the agent logs out.
20190          */
20191         TRANSFER: "TRANSFER",
20192 
20193         /**
20194          * @class Actions used to handle tasks that are associated with a given media at logout time.
20195          *
20196          * @constructs
20197          */
20198         _fakeConstructor: function ()
20199         {
20200         }, // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
20201 
20202         /**
20203          * Is the given action a valid dialog logout action.
20204          *
20205          * @param {String} action the action to evaluate
20206          * @returns {Boolean} true if the action is valid; false otherwise
20207          */
20208         isValidAction: function(action)
20209         {
20210             if ( !action )
20211             {
20212                 return false;
20213             }
20214 
20215             return DialogLogoutActions.hasOwnProperty(action.toUpperCase());
20216         }
20217     };
20218 
20219     window.finesse = window.finesse || {};
20220     window.finesse.restservices = window.finesse.restservices || {};
20221     window.finesse.restservices.DialogLogoutActions = DialogLogoutActions;
20222 
20223     return DialogLogoutActions;
20224 });
20225 define('restservices/InterruptActions',[], function ()
20226 {
20227     var InterruptActions = /** @lends finesse.restservices.InterruptActions.prototype */
20228     {
20229         /**
20230          * The interrupt will be accepted and the agent will not work on dialogs in this media until the media is no longer interrupted.
20231          */
20232         ACCEPT: "ACCEPT",
20233 
20234         /**
20235          * the interrupt will be ignored and the agent is allowed to work on dialogs while the media is interrupted.
20236          */
20237         IGNORE: "IGNORE",
20238 
20239         /**
20240          * @class
20241          *
20242          * The action to be taken in the event this media is interrupted. The action will be one of the following:<ul>
20243          *     <li><b>ACCEPT:</b> the interrupt will be accepted and the agent will not work on dialogs in this media
20244          *     until the media is no longer interrupted.</li>
20245          *     <li><b>IGNORE:</b> the interrupt will be ignored and the agent is allowed to work on dialogs while the
20246          *     media is interrupted.</li></ul>
20247          *
20248          * @constructs
20249          */
20250         _fakeConstructor: function ()
20251         {
20252         }, // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
20253 
20254         /**
20255          * Method to check whether the given action a valid dialog logout action.
20256          *
20257          * @param {String} The action to evaluate.
20258          * @returns {Boolean} true if the action is valid; false otherwise
20259          * @example restInterruptActions.isValidAction();
20260          */
20261         isValidAction: function(action)
20262         {
20263             if ( !action )
20264             {
20265                 return false;
20266             }
20267 
20268             return InterruptActions.hasOwnProperty(action.toUpperCase());
20269         }
20270     };
20271 
20272     window.finesse = window.finesse || {};
20273     window.finesse.restservices = window.finesse.restservices || {};
20274     window.finesse.restservices.InterruptActions = InterruptActions;
20275 
20276     return InterruptActions;
20277 });
20278 /**
20279  * Utility class for looking up a ReasonCode using the code and category.
20280  *
20281  */
20282 
20283 /** @private */
20284 define('restservices/ReasonCodeLookup',['restservices/RestBase', 'utilities/Utilities', 'restservices/TeamResource'], function (RestBase, Utilities, TeamResource) {
20285     
20286     var ReasonCodeLookup = RestBase.extend(/** @lends finesse.restservices.ReasonCodeLookup.prototype */{
20287         /**
20288          * @private
20289          * Returns whether this object supports subscriptions
20290          */
20291         supportsSubscriptions: false,
20292 
20293         doNotRefresh: true,
20294         
20295         autoSubscribe: false,
20296         
20297         supportsRequests: false,
20298       
20299         /**
20300          * @class
20301          * Utility class for looking up a ReasonCode using the code and category.
20302          * 
20303          * @constructs
20304          */
20305         _fakeConstuctor: function () {
20306             /* This is here to hide the real init constructor from the public docs */
20307         },
20308         
20309          /**
20310          * @private
20311          * JavaScript representation of a ReasonCodeLookup object. Also exposes methods to operate
20312          * on the object against the server.
20313          *
20314          * @param {Object} options
20315          *     An object with the following properties:<ul>
20316          *         <li><b>id:</b> The id of the object being constructed</li>
20317          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
20318          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
20319          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
20320          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
20321          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
20322          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
20323          *             <li><b>content:</b> {String} Raw string of response</li>
20324          *             <li><b>object:</b> {Object} Parsed object of response</li>
20325          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
20326          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
20327          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
20328          *             </ul></li>
20329          *         </ul></li>
20330          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
20331          **/
20332         init: function (options){
20333             this._super(options);
20334         },
20335         
20336         /**
20337          * @private
20338          * Do get disabled.
20339          */
20340         doGet: function(handlers) {
20341             return;
20342         },
20343 
20344         /**
20345          * @private
20346          * Gets the REST class for the current object - this is the ReasonCodeLookup object.
20347          */
20348         getRestClass: function () {
20349             return ReasonCodeLookup;
20350         },
20351 
20352         /**
20353          * @private
20354          * Gets the REST type for the current object - this is a "ReasonCodeLookup".
20355          */
20356         getRestType: function () {
20357             return "ReasonCode";
20358         },
20359         
20360         
20361         /**
20362          * @private
20363          * Parses a uriString to retrieve the id portion
20364          * @param {String} uriString
20365          * @return {String} id
20366          */
20367         _parseIdFromUriString : function (uriString) {
20368             return Utilities.getId(uriString);
20369         },
20370         
20371         /**
20372          * Performs a GET against the Finesse server, for looking up the reason code 
20373          * with its reason code value, and category.
20374          * Note that there is no return value; use the success handler to process a
20375          * valid return.
20376          * 
20377          * @param {finesse.interfaces.RequestHandlers} handlers
20378          *     An object containing the handlers for the request
20379          * @param {String} reasonCodeValue The code for the reason code to lookup
20380          * @param {String} reasonCodeCategory The category for the reason code to lookup.
20381          *                 The possible values are "NOT_READY" and "LOGOUT".
20382          *
20383          * @example
20384          *      new finesse.restservices.ReasonCodeLookup().lookupReasonCode({
20385          *                                              success: _handleReasonCodeGet,
20386          *                                              error: _handleReasonCodeGetError
20387          *                                              }, '32762', 'NOT_READY');
20388          *      _handleReasonCodeGet(_reasonCode) {
20389          *          var id = _reasonCode.id;
20390          *          var uri = _reasonCode.uri;
20391          *          var label = _reasonCode.label;
20392          *          ...
20393          *      }
20394          * 
20395          */
20396         lookupReasonCode : function (handlers, reasonCodeValue, reasonCodeCategory) {
20397         	
20398         	if(!this._teamResource){
20399         		var config = finesse.container? finesse.container : finesse.gadget;
20400         		var teamId= config.Config.teamId;
20401         		this._teamResource = new TeamResource({teamId: teamId});
20402         	}
20403         	// Get all the reason codes on team, including system and global reason codes
20404         	this._teamResource.lookupReasonCode(handlers, reasonCodeValue, reasonCodeCategory);
20405         }
20406     });
20407     
20408         
20409     window.finesse = window.finesse || {};
20410     window.finesse.restservices = window.finesse.restservices || {};
20411     window.finesse.restservices.ReasonCodeLookup = ReasonCodeLookup;
20412     
20413     return ReasonCodeLookup;
20414 });
20415 
20416 /**
20417  * JavaScript representation of the Finesse ChatConfig object
20418  * @requires ClientServices
20419  * @requires finesse.FinesseBase
20420  * @requires finesse.restservices.RestBase
20421  */
20422 
20423 /** @private */
20424 define('restservices/ChatConfig',['restservices/RestBase'], function (RestBase) {
20425     
20426     var ChatConfig = RestBase.extend(/** @lends finesse.restservices.ChatConfig.prototype */{
20427 
20428 		/**
20429 		 * @class
20430 		 * JavaScript representation of a ChatConfig object. Also exposes methods to operate
20431 		 * on the object against the server.
20432 		 *
20433 		 * @constructor
20434 		 * @param {String} id
20435 		 *     Not required...
20436 		 * @param {Object} callbacks
20437 		 *     An object containing callbacks for instantiation and runtime
20438 		 * @param {Function} callbacks.onLoad(this)
20439 		 *     Callback to invoke upon successful instantiation, passes in User object
20440 		 * @param {Function} callbacks.onLoadError(rsp)
20441 		 *     Callback to invoke on instantiation REST request error
20442 		 *     as passed by finesse.clientservices.ClientServices.ajax()
20443 		 *     {
20444 		 *         status: {Number} The HTTP status code returned
20445 		 *         content: {String} Raw string of response
20446 		 *         object: {Object} Parsed object of response
20447 		 *         error: {Object} Wrapped exception that was caught
20448 		 *         error.errorType: {String} Type of error that was caught
20449 		 *         error.errorMessage: {String} Message associated with error
20450 		 *     }
20451 		 * @param {Function} callbacks.onChange(this)
20452 		 *     Callback to invoke upon successful update, passes in User object
20453 		 * @param {Function} callbacks.onError(rsp)
20454 		 *     Callback to invoke on update error (refresh or event)
20455 		 *     as passed by finesse.clientservices.ClientServices.ajax()
20456 		 *     {
20457 		 *         status: {Number} The HTTP status code returned
20458 		 *         content: {String} Raw string of response
20459 		 *         object: {Object} Parsed object of response
20460 		 *         error: {Object} Wrapped exception that was caught
20461 		 *         error.errorType: {String} Type of error that was caught
20462 		 *         error.errorMessage: {String} Message associated with error
20463 		 *     }
20464 		 * @constructs     
20465 		 */
20466 		init: function (callbacks) {
20467 		    this._super("", callbacks);
20468 		},
20469         
20470         /**
20471          * Gets the REST class for the current object - this is the ChatConfig object.
20472          */
20473         getRestClass: function () {
20474             return ChatConfig;
20475         },
20476             
20477         /**
20478          * Gets the REST type for the current object - this is a "ChatConfig".
20479          */
20480         getRestType: function () {
20481             return "ChatConfig";
20482         },
20483         
20484         /**
20485          * Overrides the parent class.  Returns the url for the ChatConfig resource
20486          */
20487         getRestUrl: function () {
20488             return ("/finesse/api/" + this.getRestType());
20489         },
20490         
20491         /**
20492          * Returns whether this object supports subscriptions
20493          */
20494         supportsSubscriptions: false,
20495         
20496         /**
20497          * Getter for the Chat primary node of the ChatConfig
20498          * @returns {String}
20499          */
20500         getPrimaryNode: function () {
20501             this.isLoaded();
20502             return this.getData().primaryNode;
20503         },
20504 
20505         /**
20506          * Getter for the Chat secondary node (if any) of the ChatConfig
20507          * @returns {String}
20508          */
20509         getSecondaryNode: function () {
20510             this.isLoaded();
20511             return this.getData().secondaryNode;
20512         },
20513 
20514         /**
20515          * Retrieve the chat config settings
20516          * @returns {finesse.restservices.ChatConfig}
20517          *     This ChatConfig object to allow cascading
20518          */
20519         get: function () {      
20520             this._synchronize();
20521             
20522             return this; // Allow cascading
20523         },
20524         
20525         /**
20526          * Closure handle updating of the internal data for the ChatConfig object
20527          * upon a successful update (PUT) request before calling the intended
20528          * success handler provided by the consumer
20529          * 
20530          * @param {Object}
20531          *            chatconfig Reference to this ChatConfig object
20532          * @param {Object}
20533          *            chatSettings Object that contains the chat server settings to be
20534          *            submitted in the api request
20535          * @param {Function}
20536          *            successHandler The success handler specified by the consumer
20537          *            of this object
20538          * @returns {finesse.restservices.ChatConfig} This ChatConfig object to allow cascading
20539          */
20540         createPutSuccessHandler: function (chatconfig, chatSettings, successHandler) {
20541             return function (rsp) {
20542                 // Update internal structure based on response. Here we
20543                 // inject the chatSettings object into the
20544                 // rsp.object.ChatConfig element as a way to take
20545                 // advantage of the existing _processResponse method.
20546                 rsp.object.ChatConfig = chatSettings;
20547                 chatconfig._processResponse(rsp);
20548 
20549                 //Remove the injected chatSettings object before cascading response
20550                 rsp.object.ChatConfig = {};
20551                 
20552                 //cascade response back to consumer's response handler
20553                 successHandler(rsp);
20554             };
20555         },
20556         
20557         /**
20558          * Update the chat config settings
20559          * @param {Object} chatSettings
20560          *     The Chat server settings you want to configure
20561          * @param {Object} handlers
20562          *     An object containing callback handlers for the request. Optional.
20563          * @param {Function} options.success(rsp)
20564          *     A callback function to be invoked for a successful request.
20565          *     {
20566          *         status: {Number} The HTTP status code returned
20567          *         content: {String} Raw string of response
20568          *         object: {Object} Parsed object of response
20569          *     }
20570          * @param {Function} options.error(rsp)
20571          *     A callback function to be invoked for an unsuccessful request.
20572          *     {
20573          *         status: {Number} The HTTP status code returned
20574          *         content: {String} Raw string of response
20575          *         object: {Object} Parsed object of response (HTTP errors)
20576          *         error: {Object} Wrapped exception that was caught
20577          *         error.errorType: {String} Type of error that was caught
20578          *         error.errorMessage: {String} Message associated with error
20579          *     }
20580          * @returns {finesse.restservices.ChatConfig}
20581          *     This ChatConfig object to allow cascading
20582          */
20583         update: function (chatSettings, handlers) {
20584             this.isLoaded();
20585             var contentBody = {};
20586             
20587             contentBody[this.getRestType()] = {
20588                 "primaryNode": chatSettings.primaryNode,
20589                 "secondaryNode": chatSettings.secondaryNode
20590             };
20591             
20592             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
20593             handlers = handlers || {};
20594             
20595             this.restRequest(this.getRestUrl(), {
20596                 method: 'PUT',
20597                 success: this.createPutSuccessHandler(this, chatSettings, handlers.success),
20598                 error: handlers.error,
20599                 content: contentBody
20600             });
20601             
20602             return this; // Allow cascading
20603         }   
20604     });
20605     
20606     window.finesse = window.finesse || {};
20607     window.finesse.restservices = window.finesse.restservices || {};
20608     window.finesse.restservices.ChatConfig = ChatConfig;
20609     
20610     return ChatConfig;
20611 });
20612 
20613 /**
20614  * JavaScript representation of the Finesse ECCVariableConfig object
20615  * @requires ClientServices
20616  * @requires finesse.FinesseBase
20617  * @requires finesse.restservices.RestBase
20618  */
20619 
20620 /** The following comment is to prevent jslint errors about 
20621  * using variables before they are defined.
20622  */
20623 /*global finesse*/
20624 
20625 /** @private */
20626 define('restservices/ECCVariableConfig.js',['restservices/RestBase'], function (RestBase) {
20627     var ECCVariableConfig = RestBase.extend(/** @lends finesse.restservices.ECCVariableConfig.prototype */{
20628 
20629 	/**
20630 	 * @class
20631 	 * JavaScript representation of a ECCVariableConfig object. Also exposes methods to operate
20632 	 * on the object against the server.
20633 	 *
20634 	 * @constructor
20635 	 * @param {String} id
20636 	 *     Not required...
20637 	 * @param {Object} callbacks
20638 	 *     An object containing callbacks for instantiation and runtime
20639 	 * @param {Function} callbacks.onLoad(this)
20640 	 *     Callback to invoke upon successful instantiation
20641 	 * @param {Function} callbacks.onLoadError(rsp)
20642 	 *     Callback to invoke on instantiation REST request error
20643 	 *     as passed by finesse.clientservices.ClientServices.ajax()
20644 	 *     {
20645 	 *         status: {Number} The HTTP status code returned
20646 	 *         content: {String} Raw string of response
20647 	 *         object: {Object} Parsed object of response
20648 	 *         error: {Object} Wrapped exception that was caught
20649 	 *         error.errorType: {String} Type of error that was caught
20650 	 *         error.errorMessage: {String} Message associated with error
20651 	 *     }
20652 	 * @param {Function} callbacks.onChange(this)
20653 	 *     Callback to invoke upon successful update
20654 	 * @param {Function} callbacks.onError(rsp)
20655 	 *     Callback to invoke on update error (refresh or event)
20656 	 *     as passed by finesse.clientservices.ClientServices.ajax()
20657 	 *     {
20658 	 *         status: {Number} The HTTP status code returned
20659 	 *         content: {String} Raw string of response
20660 	 *         object: {Object} Parsed object of response
20661 	 *         error: {Object} Wrapped exception that was caught
20662 	 *         error.errorType: {String} Type of error that was caught
20663 	 *         error.errorMessage: {String} Message associated with error
20664 	 *     }
20665 	 *  
20666      * @constructs
20667 	 */
20668     init: function (callbacks) {
20669         this._super("", callbacks);
20670     },
20671 
20672     /**
20673      * Gets the REST class for the current object - this is the
20674      * ECCVariableConfig object.
20675      */
20676     getRestClass: function () {
20677         return  ECCVariableConfig;
20678     },
20679 
20680     /**
20681      * Gets the REST type for the current object - this is a
20682      * "ECCVariableConfig".
20683      */
20684     getRestType: function () {
20685         return "ECCVariableConfig";
20686     },
20687 
20688     /**
20689      * Overrides the parent class.  Returns the url for the
20690      * ECCVariableConfig resource.
20691      * @returns {String}
20692      */
20693     getRestUrl: function () {
20694         return ("/finesse/api/" + this.getRestType());
20695     },
20696 
20697     /**
20698      * Returns whether this object supports subscriptions
20699      */
20700     supportsSubscriptions: false,
20701 
20702     /**
20703      * Retrieve the ECCVariableConfig settings.
20704      * @returns {ECCVariableConfig}
20705      *     This ECCVariableConfig object to allow cascading.
20706      */
20707     get: function () {
20708         this._synchronize();
20709         return this;
20710     }
20711 	});
20712 	
20713 	window.finesse = window.finesse || {};
20714     window.finesse.restservices = window.finesse.restservices || {};
20715     window.finesse.restservices.ECCVariableConfig = ECCVariableConfig;
20716     
20717     return ECCVariableConfig; 
20718 });
20719 
20720 /**
20721  * Provides standard way resolve message keys with substitution
20722  *
20723  * @requires finesse.container.I18n or gadgets.Prefs
20724  */
20725 
20726 // Add Utilities to the finesse.utilities namespace
20727 define('utilities/I18n',['utilities/Utilities'], function (Utilities) {
20728     var I18n = (function () {
20729 
20730         /**
20731          * Shortcut to finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
20732          * @private
20733          */
20734         var _getMsg;
20735 
20736         return {
20737             /**
20738              * Provides a message resolver for this utility singleton.
20739              * @param {Function} getMsg
20740              *     A function that returns a string given a message key.
20741              *     If the key is not found, this function must return 
20742              *     something that tests false (i.e. undefined or "").
20743              */
20744             setGetter : function (getMsg) {
20745                 _getMsg = getMsg;
20746             },
20747 
20748             /**
20749              * Resolves the given message key, also performing substitution.
20750              * This generic utility will use a custom function to resolve the key
20751              * provided by finesse.utilities.I18n.setGetter. Otherwise, it will 
20752              * discover either finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
20753              * upon the first invocation and store that reference for efficiency.
20754              * 
20755              * Since this will construct a new gadgets.Prefs object, it is recommended
20756              * for gadgets to explicitly provide the setter to prevent duplicate
20757              * gadgets.Prefs objects. This does not apply if your gadget does not need
20758              * access to gadgets.Prefs other than getMsg. 
20759              * 
20760              * @param {String} key
20761              *     The key to lookup
20762              * @param {String} arguments
20763              *     Arguments for substitution
20764              * @returns {String/Function}
20765              *     The resolved string if successful, otherwise a function that returns
20766              *     a '???' string that can also be casted into a string.
20767              */
20768             getString : function (key) {
20769                 var prefs, i, retStr, noMsg, getFailed = "", args;
20770                 if (!_getMsg) {
20771                     if (finesse.container && finesse.container.I18n) {
20772                         _getMsg = finesse.container.I18n.getMsg;
20773                     } else if (gadgets) {
20774                         prefs = new gadgets.Prefs();
20775                         _getMsg = prefs.getMsg;
20776                     }
20777                 }
20778                 
20779                 try {
20780 					var keyValue = _getMsg(key);
20781 					if (typeof keyValue === "string") {
20782 						retStr = keyValue.replace(/'/g, "'"); // replacing ' character to ' to fix localization issue
20783 					} else {
20784 						retStr = keyValue;
20785 					}
20786                 } catch (e) {
20787                     getFailed = "finesse.utilities.I18n.getString(): invalid _getMsg";
20788                 }
20789 
20790                 if (retStr) {
20791                     // Lookup was successful, perform substitution (if any)
20792                     args = [ retStr ];
20793                     for (i = 1; i < arguments.length; i += 1) {
20794                         args.push(arguments[i]);
20795                     }
20796                     return Utilities.formatString.apply(this, args);
20797                 }
20798                 // We want a function because jQuery.html() and jQuery.text() is smart enough to invoke it.
20799                 /** @private */
20800                 noMsg = function () {
20801                     return "???" + key + "???" + getFailed;
20802                 };
20803                 // We overload the toString() of this "function" to allow JavaScript to cast it into a string
20804                 // For example, var myMsg = "something " + finesse.utilities.I18n.getMsg("unresolvable.key");
20805                 /** @private */
20806                 noMsg.toString = function () {
20807                     return "???" + key + "???" + getFailed;
20808                 };
20809                 return noMsg;
20810 
20811             }
20812         };
20813     }());
20814     
20815     window.finesse = window.finesse || {};
20816     window.finesse.utilities = window.finesse.utilities || {};
20817     window.finesse.utilities.I18n = I18n;
20818 
20819     return I18n;
20820 });
20821 
20822 /**
20823  * Logging.js: provides simple logging for clients to use and overrides synchronous native methods: alert(), confirm(), and prompt().
20824  * 
20825  * On Firefox, it will hook into console for logging.  On IE, it will log to the status bar. 
20826  */
20827 // Add Utilities to the finesse.utilities namespace
20828 define('utilities/Logger',[], function () {
20829     var Logger = (function () {
20830         
20831         var
20832         
20833         /** @private **/
20834         debugOn,
20835         
20836         /**
20837          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
20838          * @param num is the number to pad to 2 digits
20839          * @returns a two digit padded string
20840          * @private
20841          */
20842         padTwoDigits = function (num) {        
20843             return (num < 10) ? '0' + num : num;  
20844         },
20845         
20846         /**
20847          * Checks to see if we have a console - this allows us to support Firefox or IE.
20848          * @returns {Boolean} True for Firefox, False for IE
20849          * @private
20850          */
20851         hasConsole = function () {
20852             var retval = false;
20853             try
20854             {
20855                 if (window.console !== undefined) 
20856                 {
20857                     retval = true;
20858                 }
20859             } 
20860             catch (err)
20861             {
20862                 retval = false;
20863             }
20864               
20865             return retval;
20866         },
20867         
20868         /**
20869          * Gets a timestamp.
20870          * @returns {String} is a timestamp in the following format: HH:MM:SS
20871          * @private
20872          */
20873         getTimeStamp = function () {
20874             var date = new Date(), timeStr;
20875             timeStr = padTwoDigits(date.getHours()) + ":" + padTwoDigits(date.getMinutes()) + ":" + padTwoDigits(date.getSeconds());
20876 
20877             return timeStr;
20878         };
20879         
20880         return {
20881             /**
20882              * Enable debug mode. Debug mode may impact performance on the UI.
20883              *
20884              * @param {Boolean} enable
20885              *      True to enable debug logging.
20886              * @private
20887              */
20888             setDebug : function (enable) {
20889                 debugOn = enable;
20890             },
20891             
20892             /**
20893              * Logs a string as DEBUG.
20894              * 
20895              * @param str is the string to log. 
20896              * @private
20897              */
20898             log : function (str) {
20899                 var timeStr = getTimeStamp();
20900                 
20901                 if (debugOn) {
20902                     if (hasConsole())
20903                     {
20904                         window.console.log(timeStr + ": " + "DEBUG" + " - " + str);
20905                     }
20906                 }
20907             },
20908             
20909             /**
20910              * Logs a string as INFO.
20911              * 
20912              * @param str is the string to log. 
20913              * @private
20914              */
20915             info : function (str) {
20916                 var timeStr = getTimeStamp();
20917                 
20918                 if (hasConsole())
20919                 {
20920                     window.console.info(timeStr + ": " + "INFO" + " - " + str);
20921                 }
20922             },
20923             
20924             /**
20925              * Logs a string as WARN.
20926              * 
20927              * @param str is the string to log. 
20928              * @private
20929              */
20930             warn : function (str) {
20931                 var timeStr = getTimeStamp();
20932                 
20933                 if (hasConsole())
20934                 {
20935                     window.console.warn(timeStr + ": " + "WARN" + " - " + str);
20936                 }
20937             },
20938             /**
20939              * Logs a string as ERROR.
20940              * 
20941              * @param str is the string to log. 
20942              * @private
20943              */
20944             error : function (str) {
20945                 var timeStr = getTimeStamp();
20946                 
20947                 if (hasConsole())
20948                 {
20949                     window.console.error(timeStr + ": " + "ERROR" + " - " + str);
20950                 }
20951             }
20952         };
20953     }());
20954     
20955     return Logger;
20956 });
20957 
20958 /**
20959  * BackSpaceHandler.js: provides functionality to prevent the page from navigating back and hence losing the unsaved data when backspace is pressed.
20960  * 
20961  */
20962 define('utilities/BackSpaceHandler',[], function () {
20963 			var eventCallback = function(event) {
20964 				var doPrevent = false, d = event.srcElement || event.target;
20965 				if (event.keyCode === 8) {
20966 					if ((d.tagName.toUpperCase() === 'INPUT' && (d.type
20967 							.toUpperCase() === 'TEXT'
20968 							|| d.type.toUpperCase() === 'PASSWORD'
20969 							|| d.type.toUpperCase() === 'FILE'
20970 							|| d.type.toUpperCase() === 'SEARCH'
20971 							|| d.type.toUpperCase() === 'EMAIL'
20972 							|| d.type.toUpperCase() === 'NUMBER' || d.type
20973 							.toUpperCase() === 'DATE'))
20974 							|| d.tagName.toUpperCase() === 'TEXTAREA') {
20975 						doPrevent = d.readOnly || d.disabled;
20976 					} else {
20977 						//if HTML content is editable doPrevent will be false and vice versa
20978 						doPrevent = (!d.isContentEditable);
20979 					}
20980 				}
20981 
20982 				if (doPrevent) {
20983 					event.preventDefault();
20984 				}
20985 			};
20986 
20987 			if (window.addEventListener) {
20988 				window.addEventListener('keydown', eventCallback);
20989 			} else if (window.attachEvent) {
20990 				window.attachEvent('onkeydown', eventCallback);
20991 			} else {
20992 				window.console.error("Unable to attach backspace handler event ");
20993 			}
20994 });
20995 !function(a,b){"function"==typeof define&&define.amd?define('utilities/../../thirdparty/tv4/tv4.min.js',[],b):"undefined"!=typeof module&&module.exports?module.exports=b():a.tv4=b()}(this,function(){function a(a){return encodeURI(a).replace(/%25[0-9][0-9]/g,function(a){return"%"+a.substring(3)})}function b(b){var c="";m[b.charAt(0)]&&(c=b.charAt(0),b=b.substring(1));var d="",e="",f=!0,g=!1,h=!1;"+"===c?f=!1:"."===c?(e=".",d="."):"/"===c?(e="/",d="/"):"#"===c?(e="#",f=!1):";"===c?(e=";",d=";",g=!0,h=!0):"?"===c?(e="?",d="&",g=!0):"&"===c&&(e="&",d="&",g=!0);for(var i=[],j=b.split(","),k=[],l={},o=0;o<j.length;o++){var p=j[o],q=null;if(-1!==p.indexOf(":")){var r=p.split(":");p=r[0],q=parseInt(r[1],10)}for(var s={};n[p.charAt(p.length-1)];)s[p.charAt(p.length-1)]=!0,p=p.substring(0,p.length-1);var t={truncate:q,name:p,suffices:s};k.push(t),l[p]=t,i.push(p)}var u=function(b){for(var c="",i=0,j=0;j<k.length;j++){var l=k[j],m=b(l.name);if(null===m||void 0===m||Array.isArray(m)&&0===m.length||"object"==typeof m&&0===Object.keys(m).length)i++;else if(c+=j===i?e:d||",",Array.isArray(m)){g&&(c+=l.name+"=");for(var n=0;n<m.length;n++)n>0&&(c+=l.suffices["*"]?d||",":",",l.suffices["*"]&&g&&(c+=l.name+"=")),c+=f?encodeURIComponent(m[n]).replace(/!/g,"%21"):a(m[n])}else if("object"==typeof m){g&&!l.suffices["*"]&&(c+=l.name+"=");var o=!0;for(var p in m)o||(c+=l.suffices["*"]?d||",":","),o=!1,c+=f?encodeURIComponent(p).replace(/!/g,"%21"):a(p),c+=l.suffices["*"]?"=":",",c+=f?encodeURIComponent(m[p]).replace(/!/g,"%21"):a(m[p])}else g&&(c+=l.name,h&&""===m||(c+="=")),null!=l.truncate&&(m=m.substring(0,l.truncate)),c+=f?encodeURIComponent(m).replace(/!/g,"%21"):a(m)}return c};return u.varNames=i,{prefix:e,substitution:u}}function c(a){if(!(this instanceof c))return new c(a);for(var d=a.split("{"),e=[d.shift()],f=[],g=[],h=[];d.length>0;){var i=d.shift(),j=i.split("}")[0],k=i.substring(j.length+1),l=b(j);g.push(l.substitution),f.push(l.prefix),e.push(k),h=h.concat(l.substitution.varNames)}this.fill=function(a){for(var b=e[0],c=0;c<g.length;c++){var d=g[c];b+=d(a),b+=e[c+1]}return b},this.varNames=h,this.template=a}function d(a,b){if(a===b)return!0;if(a&&b&&"object"==typeof a&&"object"==typeof b){if(Array.isArray(a)!==Array.isArray(b))return!1;if(Array.isArray(a)){if(a.length!==b.length)return!1;for(var c=0;c<a.length;c++)if(!d(a[c],b[c]))return!1}else{var e;for(e in a)if(void 0===b[e]&&void 0!==a[e])return!1;for(e in b)if(void 0===a[e]&&void 0!==b[e])return!1;for(e in a)if(!d(a[e],b[e]))return!1}return!0}return!1}function e(a){var b=String(a).replace(/^\s+|\s+$/g,"").match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);return b?{href:b[0]||"",protocol:b[1]||"",authority:b[2]||"",host:b[3]||"",hostname:b[4]||"",port:b[5]||"",pathname:b[6]||"",search:b[7]||"",hash:b[8]||""}:null}function f(a,b){function c(a){var b=[];return a.replace(/^(\.\.?(\/|$))+/,"").replace(/\/(\.(\/|$))+/g,"/").replace(/\/\.\.$/,"/../").replace(/\/?[^\/]*/g,function(a){"/.."===a?b.pop():b.push(a)}),b.join("").replace(/^\//,"/"===a.charAt(0)?"/":"")}return b=e(b||""),a=e(a||""),b&&a?(b.protocol||a.protocol)+(b.protocol||b.authority?b.authority:a.authority)+c(b.protocol||b.authority||"/"===b.pathname.charAt(0)?b.pathname:b.pathname?(a.authority&&!a.pathname?"/":"")+a.pathname.slice(0,a.pathname.lastIndexOf("/")+1)+b.pathname:a.pathname)+(b.protocol||b.authority||b.pathname?b.search:b.search||a.search)+b.hash:null}function g(a){return a.split("#")[0]}function h(a,b){if(a&&"object"==typeof a)if(void 0===b?b=a.id:"string"==typeof a.id&&(b=f(b,a.id),a.id=b),Array.isArray(a))for(var c=0;c<a.length;c++)h(a[c],b);else{"string"==typeof a.$ref&&(a.$ref=f(b,a.$ref));for(var d in a)"enum"!==d&&h(a[d],b)}}function i(a){a=a||"en";var b=v[a];return function(a){var c=b[a.code]||u[a.code];if("string"!=typeof c)return"Unknown error code "+a.code+": "+JSON.stringify(a.messageParams);var d=a.params;return c.replace(/\{([^{}]*)\}/g,function(a,b){var c=d[b];return"string"==typeof c||"number"==typeof c?c:a})}}function j(a,b,c,d,e){if(Error.call(this),void 0===a)throw new Error("No error code supplied: "+d);this.message="",this.params=b,this.code=a,this.dataPath=c||"",this.schemaPath=d||"",this.subErrors=e||null;var f=new Error(this.message);if(this.stack=f.stack||f.stacktrace,!this.stack)try{throw f}catch(f){this.stack=f.stack||f.stacktrace}}function k(a,b){if(b.substring(0,a.length)===a){var c=b.substring(a.length);if(b.length>0&&"/"===b.charAt(a.length-1)||"#"===c.charAt(0)||"?"===c.charAt(0))return!0}return!1}function l(a){var b,c,d=new o,e={setErrorReporter:function(a){return"string"==typeof a?this.language(a):(c=a,!0)},addFormat:function(){d.addFormat.apply(d,arguments)},language:function(a){return a?(v[a]||(a=a.split("-")[0]),v[a]?(b=a,a):!1):b},addLanguage:function(a,b){var c;for(c in r)b[c]&&!b[r[c]]&&(b[r[c]]=b[c]);var d=a.split("-")[0];if(v[d]){v[a]=Object.create(v[d]);for(c in b)"undefined"==typeof v[d][c]&&(v[d][c]=b[c]),v[a][c]=b[c]}else v[a]=b,v[d]=b;return this},freshApi:function(a){var b=l();return a&&b.language(a),b},validate:function(a,e,f,g){var h=i(b),j=c?function(a,b,d){return c(a,b,d)||h(a,b,d)}:h,k=new o(d,!1,j,f,g);"string"==typeof e&&(e={$ref:e}),k.addSchema("",e);var l=k.validateAll(a,e,null,null,"");return!l&&g&&(l=k.banUnknownProperties(a,e)),this.error=l,this.missing=k.missing,this.valid=null===l,this.valid},validateResult:function(){var a={};return this.validate.apply(a,arguments),a},validateMultiple:function(a,e,f,g){var h=i(b),j=c?function(a,b,d){return c(a,b,d)||h(a,b,d)}:h,k=new o(d,!0,j,f,g);"string"==typeof e&&(e={$ref:e}),k.addSchema("",e),k.validateAll(a,e,null,null,""),g&&k.banUnknownProperties(a,e);var l={};return l.errors=k.errors,l.missing=k.missing,l.valid=0===l.errors.length,l},addSchema:function(){return d.addSchema.apply(d,arguments)},getSchema:function(){return d.getSchema.apply(d,arguments)},getSchemaMap:function(){return d.getSchemaMap.apply(d,arguments)},getSchemaUris:function(){return d.getSchemaUris.apply(d,arguments)},getMissingUris:function(){return d.getMissingUris.apply(d,arguments)},dropSchemas:function(){d.dropSchemas.apply(d,arguments)},defineKeyword:function(){d.defineKeyword.apply(d,arguments)},defineError:function(a,b,c){if("string"!=typeof a||!/^[A-Z]+(_[A-Z]+)*$/.test(a))throw new Error("Code name must be a string in UPPER_CASE_WITH_UNDERSCORES");if("number"!=typeof b||b%1!==0||1e4>b)throw new Error("Code number must be an integer > 10000");if("undefined"!=typeof r[a])throw new Error("Error already defined: "+a+" as "+r[a]);if("undefined"!=typeof s[b])throw new Error("Error code already used: "+s[b]+" as "+b);r[a]=b,s[b]=a,u[a]=u[b]=c;for(var d in v){var e=v[d];e[a]&&(e[b]=e[b]||e[a])}},reset:function(){d.reset(),this.error=null,this.missing=[],this.valid=!0},missing:[],error:null,valid:!0,normSchema:h,resolveUrl:f,getDocumentUri:g,errorCodes:r};return e.language(a||"en"),e}Object.keys||(Object.keys=function(){var a=Object.prototype.hasOwnProperty,b=!{toString:null}.propertyIsEnumerable("toString"),c=["toString","toLocaleString","valueOf","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","constructor"],d=c.length;return function(e){if("object"!=typeof e&&"function"!=typeof e||null===e)throw new TypeError("Object.keys called on non-object");var f=[];for(var g in e)a.call(e,g)&&f.push(g);if(b)for(var h=0;d>h;h++)a.call(e,c[h])&&f.push(c[h]);return f}}()),Object.create||(Object.create=function(){function a(){}return function(b){if(1!==arguments.length)throw new Error("Object.create implementation only accepts one parameter.");return a.prototype=b,new a}}()),Array.isArray||(Array.isArray=function(a){return"[object Array]"===Object.prototype.toString.call(a)}),Array.prototype.indexOf||(Array.prototype.indexOf=function(a){if(null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if(0===c)return-1;var d=0;if(arguments.length>1&&(d=Number(arguments[1]),d!==d?d=0:0!==d&&d!==1/0&&d!==-(1/0)&&(d=(d>0||-1)*Math.floor(Math.abs(d)))),d>=c)return-1;for(var e=d>=0?d:Math.max(c-Math.abs(d),0);c>e;e++)if(e in b&&b[e]===a)return e;return-1}),Object.isFrozen||(Object.isFrozen=function(a){for(var b="tv4_test_frozen_key";a.hasOwnProperty(b);)b+=Math.random();try{return a[b]=!0,delete a[b],!1}catch(c){return!0}});var m={"+":!0,"#":!0,".":!0,"/":!0,";":!0,"?":!0,"&":!0},n={"*":!0};c.prototype={toString:function(){return this.template},fillFromObject:function(a){return this.fill(function(b){return a[b]})}};var o=function(a,b,c,d,e){if(this.missing=[],this.missingMap={},this.formatValidators=a?Object.create(a.formatValidators):{},this.schemas=a?Object.create(a.schemas):{},this.collectMultiple=b,this.errors=[],this.handleError=b?this.collectError:this.returnError,d&&(this.checkRecursive=!0,this.scanned=[],this.scannedFrozen=[],this.scannedFrozenSchemas=[],this.scannedFrozenValidationErrors=[],this.validatedSchemasKey="tv4_validation_id",this.validationErrorsKey="tv4_validation_errors_id"),e&&(this.trackUnknownProperties=!0,this.knownPropertyPaths={},this.unknownPropertyPaths={}),this.errorReporter=c||i("en"),"string"==typeof this.errorReporter)throw new Error("debug");if(this.definedKeywords={},a)for(var f in a.definedKeywords)this.definedKeywords[f]=a.definedKeywords[f].slice(0)};o.prototype.defineKeyword=function(a,b){this.definedKeywords[a]=this.definedKeywords[a]||[],this.definedKeywords[a].push(b)},o.prototype.createError=function(a,b,c,d,e,f,g){var h=new j(a,b,c,d,e);return h.message=this.errorReporter(h,f,g),h},o.prototype.returnError=function(a){return a},o.prototype.collectError=function(a){return a&&this.errors.push(a),null},o.prototype.prefixErrors=function(a,b,c){for(var d=a;d<this.errors.length;d++)this.errors[d]=this.errors[d].prefixWith(b,c);return this},o.prototype.banUnknownProperties=function(a,b){for(var c in this.unknownPropertyPaths){var d=this.createError(r.UNKNOWN_PROPERTY,{path:c},c,"",null,a,b),e=this.handleError(d);if(e)return e}return null},o.prototype.addFormat=function(a,b){if("object"==typeof a){for(var c in a)this.addFormat(c,a[c]);return this}this.formatValidators[a]=b},o.prototype.resolveRefs=function(a,b){if(void 0!==a.$ref){if(b=b||{},b[a.$ref])return this.createError(r.CIRCULAR_REFERENCE,{urls:Object.keys(b).join(", ")},"","",null,void 0,a);b[a.$ref]=!0,a=this.getSchema(a.$ref,b)}return a},o.prototype.getSchema=function(a,b){var c;if(void 0!==this.schemas[a])return c=this.schemas[a],this.resolveRefs(c,b);var d=a,e="";if(-1!==a.indexOf("#")&&(e=a.substring(a.indexOf("#")+1),d=a.substring(0,a.indexOf("#"))),"object"==typeof this.schemas[d]){c=this.schemas[d];var f=decodeURIComponent(e);if(""===f)return this.resolveRefs(c,b);if("/"!==f.charAt(0))return void 0;for(var g=f.split("/").slice(1),h=0;h<g.length;h++){var i=g[h].replace(/~1/g,"/").replace(/~0/g,"~");if(void 0===c[i]){c=void 0;break}c=c[i]}if(void 0!==c)return this.resolveRefs(c,b)}void 0===this.missing[d]&&(this.missing.push(d),this.missing[d]=d,this.missingMap[d]=d)},o.prototype.searchSchemas=function(a,b){if(Array.isArray(a))for(var c=0;c<a.length;c++)this.searchSchemas(a[c],b);else if(a&&"object"==typeof a){"string"==typeof a.id&&k(b,a.id)&&void 0===this.schemas[a.id]&&(this.schemas[a.id]=a);for(var d in a)if("enum"!==d)if("object"==typeof a[d])this.searchSchemas(a[d],b);else if("$ref"===d){var e=g(a[d]);e&&void 0===this.schemas[e]&&void 0===this.missingMap[e]&&(this.missingMap[e]=e)}}},o.prototype.addSchema=function(a,b){if("string"!=typeof a||"undefined"==typeof b){if("object"!=typeof a||"string"!=typeof a.id)return;b=a,a=b.id}a===g(a)+"#"&&(a=g(a)),this.schemas[a]=b,delete this.missingMap[a],h(b,a),this.searchSchemas(b,a)},o.prototype.getSchemaMap=function(){var a={};for(var b in this.schemas)a[b]=this.schemas[b];return a},o.prototype.getSchemaUris=function(a){var b=[];for(var c in this.schemas)(!a||a.test(c))&&b.push(c);return b},o.prototype.getMissingUris=function(a){var b=[];for(var c in this.missingMap)(!a||a.test(c))&&b.push(c);return b},o.prototype.dropSchemas=function(){this.schemas={},this.reset()},o.prototype.reset=function(){this.missing=[],this.missingMap={},this.errors=[]},o.prototype.validateAll=function(a,b,c,d,e){var f;if(b=this.resolveRefs(b),!b)return null;if(b instanceof j)return this.errors.push(b),b;var g,h=this.errors.length,i=null,k=null;if(this.checkRecursive&&a&&"object"==typeof a){if(f=!this.scanned.length,a[this.validatedSchemasKey]){var l=a[this.validatedSchemasKey].indexOf(b);if(-1!==l)return this.errors=this.errors.concat(a[this.validationErrorsKey][l]),null}if(Object.isFrozen(a)&&(g=this.scannedFrozen.indexOf(a),-1!==g)){var m=this.scannedFrozenSchemas[g].indexOf(b);if(-1!==m)return this.errors=this.errors.concat(this.scannedFrozenValidationErrors[g][m]),null}if(this.scanned.push(a),Object.isFrozen(a))-1===g&&(g=this.scannedFrozen.length,this.scannedFrozen.push(a),this.scannedFrozenSchemas.push([])),i=this.scannedFrozenSchemas[g].length,this.scannedFrozenSchemas[g][i]=b,this.scannedFrozenValidationErrors[g][i]=[];else{if(!a[this.validatedSchemasKey])try{Object.defineProperty(a,this.validatedSchemasKey,{value:[],configurable:!0}),Object.defineProperty(a,this.validationErrorsKey,{value:[],configurable:!0})}catch(n){a[this.validatedSchemasKey]=[],a[this.validationErrorsKey]=[]}k=a[this.validatedSchemasKey].length,a[this.validatedSchemasKey][k]=b,a[this.validationErrorsKey][k]=[]}}var o=this.errors.length,p=this.validateBasic(a,b,e)||this.validateNumeric(a,b,e)||this.validateString(a,b,e)||this.validateArray(a,b,e)||this.validateObject(a,b,e)||this.validateCombinations(a,b,e)||this.validateHypermedia(a,b,e)||this.validateFormat(a,b,e)||this.validateDefinedKeywords(a,b,e)||null;if(f){for(;this.scanned.length;){var q=this.scanned.pop();delete q[this.validatedSchemasKey]}this.scannedFrozen=[],this.scannedFrozenSchemas=[]}if(p||o!==this.errors.length)for(;c&&c.length||d&&d.length;){var r=c&&c.length?""+c.pop():null,s=d&&d.length?""+d.pop():null;p&&(p=p.prefixWith(r,s)),this.prefixErrors(o,r,s)}return null!==i?this.scannedFrozenValidationErrors[g][i]=this.errors.slice(h):null!==k&&(a[this.validationErrorsKey][k]=this.errors.slice(h)),this.handleError(p)},o.prototype.validateFormat=function(a,b){if("string"!=typeof b.format||!this.formatValidators[b.format])return null;var c=this.formatValidators[b.format].call(null,a,b);return"string"==typeof c||"number"==typeof c?this.createError(r.FORMAT_CUSTOM,{message:c},"","/format",null,a,b):c&&"object"==typeof c?this.createError(r.FORMAT_CUSTOM,{message:c.message||"?"},c.dataPath||"",c.schemaPath||"/format",null,a,b):null},o.prototype.validateDefinedKeywords=function(a,b,c){for(var d in this.definedKeywords)if("undefined"!=typeof b[d])for(var e=this.definedKeywords[d],f=0;f<e.length;f++){var g=e[f],h=g(a,b[d],b,c);if("string"==typeof h||"number"==typeof h)return this.createError(r.KEYWORD_CUSTOM,{key:d,message:h},"","",null,a,b).prefixWith(null,d);if(h&&"object"==typeof h){var i=h.code;if("string"==typeof i){if(!r[i])throw new Error("Undefined error code (use defineError): "+i);i=r[i]}else"number"!=typeof i&&(i=r.KEYWORD_CUSTOM);var j="object"==typeof h.message?h.message:{key:d,message:h.message||"?"},k=h.schemaPath||"/"+d.replace(/~/g,"~0").replace(/\//g,"~1");return this.createError(i,j,h.dataPath||null,k,null,a,b)}}return null},o.prototype.validateBasic=function(a,b,c){var d;return(d=this.validateType(a,b,c))?d.prefixWith(null,"type"):(d=this.validateEnum(a,b,c))?d.prefixWith(null,"type"):null},o.prototype.validateType=function(a,b){if(void 0===b.type)return null;var c=typeof a;null===a?c="null":Array.isArray(a)&&(c="array");var d=b.type;Array.isArray(d)||(d=[d]);for(var e=0;e<d.length;e++){var f=d[e];if(f===c||"integer"===f&&"number"===c&&a%1===0)return null}return this.createError(r.INVALID_TYPE,{type:c,expected:d.join("/")},"","",null,a,b)},o.prototype.validateEnum=function(a,b){if(void 0===b["enum"])return null;for(var c=0;c<b["enum"].length;c++){var e=b["enum"][c];if(d(a,e))return null}return this.createError(r.ENUM_MISMATCH,{value:"undefined"!=typeof JSON?JSON.stringify(a):a},"","",null,a,b)},o.prototype.validateNumeric=function(a,b,c){return this.validateMultipleOf(a,b,c)||this.validateMinMax(a,b,c)||this.validateNaN(a,b,c)||null};var p=Math.pow(2,-51),q=1-p;o.prototype.validateMultipleOf=function(a,b){var c=b.multipleOf||b.divisibleBy;if(void 0===c)return null;if("number"==typeof a){var d=a/c%1;if(d>=p&&q>d)return this.createError(r.NUMBER_MULTIPLE_OF,{value:a,multipleOf:c},"","",null,a,b)}return null},o.prototype.validateMinMax=function(a,b){if("number"!=typeof a)return null;if(void 0!==b.minimum){if(a<b.minimum)return this.createError(r.NUMBER_MINIMUM,{value:a,minimum:b.minimum},"","/minimum",null,a,b);if(b.exclusiveMinimum&&a===b.minimum)return this.createError(r.NUMBER_MINIMUM_EXCLUSIVE,{value:a,minimum:b.minimum},"","/exclusiveMinimum",null,a,b)}if(void 0!==b.maximum){if(a>b.maximum)return this.createError(r.NUMBER_MAXIMUM,{value:a,maximum:b.maximum},"","/maximum",null,a,b);if(b.exclusiveMaximum&&a===b.maximum)return this.createError(r.NUMBER_MAXIMUM_EXCLUSIVE,{value:a,maximum:b.maximum},"","/exclusiveMaximum",null,a,b)}return null},o.prototype.validateNaN=function(a,b){return"number"!=typeof a?null:isNaN(a)===!0||a===1/0||a===-(1/0)?this.createError(r.NUMBER_NOT_A_NUMBER,{value:a},"","/type",null,a,b):null},o.prototype.validateString=function(a,b,c){return this.validateStringLength(a,b,c)||this.validateStringPattern(a,b,c)||null},o.prototype.validateStringLength=function(a,b){return"string"!=typeof a?null:void 0!==b.minLength&&a.length<b.minLength?this.createError(r.STRING_LENGTH_SHORT,{length:a.length,minimum:b.minLength},"","/minLength",null,a,b):void 0!==b.maxLength&&a.length>b.maxLength?this.createError(r.STRING_LENGTH_LONG,{length:a.length,maximum:b.maxLength},"","/maxLength",null,a,b):null},o.prototype.validateStringPattern=function(a,b){if("string"!=typeof a||"string"!=typeof b.pattern&&!(b.pattern instanceof RegExp))return null;var c;if(b.pattern instanceof RegExp)c=b.pattern;else{var d,e="",f=b.pattern.match(/^\/(.+)\/([img]*)$/);f?(d=f[1],e=f[2]):d=b.pattern,c=new RegExp(d,e)}return c.test(a)?null:this.createError(r.STRING_PATTERN,{pattern:b.pattern},"","/pattern",null,a,b)},o.prototype.validateArray=function(a,b,c){return Array.isArray(a)?this.validateArrayLength(a,b,c)||this.validateArrayUniqueItems(a,b,c)||this.validateArrayItems(a,b,c)||null:null},o.prototype.validateArrayLength=function(a,b){var c;return void 0!==b.minItems&&a.length<b.minItems&&(c=this.createError(r.ARRAY_LENGTH_SHORT,{length:a.length,minimum:b.minItems},"","/minItems",null,a,b),this.handleError(c))?c:void 0!==b.maxItems&&a.length>b.maxItems&&(c=this.createError(r.ARRAY_LENGTH_LONG,{length:a.length,maximum:b.maxItems},"","/maxItems",null,a,b),this.handleError(c))?c:null},o.prototype.validateArrayUniqueItems=function(a,b){if(b.uniqueItems)for(var c=0;c<a.length;c++)for(var e=c+1;e<a.length;e++)if(d(a[c],a[e])){var f=this.createError(r.ARRAY_UNIQUE,{match1:c,match2:e},"","/uniqueItems",null,a,b);if(this.handleError(f))return f}return null},o.prototype.validateArrayItems=function(a,b,c){if(void 0===b.items)return null;var d,e;if(Array.isArray(b.items)){for(e=0;e<a.length;e++)if(e<b.items.length){if(d=this.validateAll(a[e],b.items[e],[e],["items",e],c+"/"+e))return d}else if(void 0!==b.additionalItems)if("boolean"==typeof b.additionalItems){if(!b.additionalItems&&(d=this.createError(r.ARRAY_ADDITIONAL_ITEMS,{},"/"+e,"/additionalItems",null,a,b),this.handleError(d)))return d}else if(d=this.validateAll(a[e],b.additionalItems,[e],["additionalItems"],c+"/"+e))return d}else for(e=0;e<a.length;e++)if(d=this.validateAll(a[e],b.items,[e],["items"],c+"/"+e))return d;return null},o.prototype.validateObject=function(a,b,c){return"object"!=typeof a||null===a||Array.isArray(a)?null:this.validateObjectMinMaxProperties(a,b,c)||this.validateObjectRequiredProperties(a,b,c)||this.validateObjectProperties(a,b,c)||this.validateObjectDependencies(a,b,c)||null},o.prototype.validateObjectMinMaxProperties=function(a,b){var c,d=Object.keys(a);return void 0!==b.minProperties&&d.length<b.minProperties&&(c=this.createError(r.OBJECT_PROPERTIES_MINIMUM,{propertyCount:d.length,minimum:b.minProperties},"","/minProperties",null,a,b),this.handleError(c))?c:void 0!==b.maxProperties&&d.length>b.maxProperties&&(c=this.createError(r.OBJECT_PROPERTIES_MAXIMUM,{propertyCount:d.length,maximum:b.maxProperties},"","/maxProperties",null,a,b),this.handleError(c))?c:null},o.prototype.validateObjectRequiredProperties=function(a,b){if(void 0!==b.required)for(var c=0;c<b.required.length;c++){var d=b.required[c];if(void 0===a[d]){var e=this.createError(r.OBJECT_REQUIRED,{key:d},"","/required/"+c,null,a,b);if(this.handleError(e))return e}}return null},o.prototype.validateObjectProperties=function(a,b,c){var d;for(var e in a){var f=c+"/"+e.replace(/~/g,"~0").replace(/\//g,"~1"),g=!1;if(void 0!==b.properties&&void 0!==b.properties[e]&&(g=!0,d=this.validateAll(a[e],b.properties[e],[e],["properties",e],f)))return d;if(void 0!==b.patternProperties)for(var h in b.patternProperties){var i=new RegExp(h);if(i.test(e)&&(g=!0,d=this.validateAll(a[e],b.patternProperties[h],[e],["patternProperties",h],f)))return d}if(g)this.trackUnknownProperties&&(this.knownPropertyPaths[f]=!0,delete this.unknownPropertyPaths[f]);else if(void 0!==b.additionalProperties){if(this.trackUnknownProperties&&(this.knownPropertyPaths[f]=!0,delete this.unknownPropertyPaths[f]),"boolean"==typeof b.additionalProperties){if(!b.additionalProperties&&(d=this.createError(r.OBJECT_ADDITIONAL_PROPERTIES,{key:e},"","/additionalProperties",null,a,b).prefixWith(e,null),this.handleError(d)))return d}else if(d=this.validateAll(a[e],b.additionalProperties,[e],["additionalProperties"],f))return d}else this.trackUnknownProperties&&!this.knownPropertyPaths[f]&&(this.unknownPropertyPaths[f]=!0)}return null},o.prototype.validateObjectDependencies=function(a,b,c){var d;if(void 0!==b.dependencies)for(var e in b.dependencies)if(void 0!==a[e]){var f=b.dependencies[e];if("string"==typeof f){if(void 0===a[f]&&(d=this.createError(r.OBJECT_DEPENDENCY_KEY,{key:e,missing:f},"","",null,a,b).prefixWith(null,e).prefixWith(null,"dependencies"),this.handleError(d)))return d}else if(Array.isArray(f))for(var g=0;g<f.length;g++){var h=f[g];if(void 0===a[h]&&(d=this.createError(r.OBJECT_DEPENDENCY_KEY,{key:e,missing:h},"","/"+g,null,a,b).prefixWith(null,e).prefixWith(null,"dependencies"),this.handleError(d)))return d}else if(d=this.validateAll(a,f,[],["dependencies",e],c))return d}return null},o.prototype.validateCombinations=function(a,b,c){return this.validateAllOf(a,b,c)||this.validateAnyOf(a,b,c)||this.validateOneOf(a,b,c)||this.validateNot(a,b,c)||null},o.prototype.validateAllOf=function(a,b,c){if(void 0===b.allOf)return null;for(var d,e=0;e<b.allOf.length;e++){var f=b.allOf[e];if(d=this.validateAll(a,f,[],["allOf",e],c))return d}return null},o.prototype.validateAnyOf=function(a,b,c){if(void 0===b.anyOf)return null;var d,e,f=[],g=this.errors.length;this.trackUnknownProperties&&(d=this.unknownPropertyPaths,e=this.knownPropertyPaths);for(var h=!0,i=0;i<b.anyOf.length;i++){this.trackUnknownProperties&&(this.unknownPropertyPaths={},this.knownPropertyPaths={});var j=b.anyOf[i],k=this.errors.length,l=this.validateAll(a,j,[],["anyOf",i],c);if(null===l&&k===this.errors.length){if(this.errors=this.errors.slice(0,g),this.trackUnknownProperties){for(var m in this.knownPropertyPaths)e[m]=!0,delete d[m];for(var n in this.unknownPropertyPaths)e[n]||(d[n]=!0);h=!1;continue}return null}l&&f.push(l.prefixWith(null,""+i).prefixWith(null,"anyOf"))}return this.trackUnknownProperties&&(this.unknownPropertyPaths=d,this.knownPropertyPaths=e),h?(f=f.concat(this.errors.slice(g)),this.errors=this.errors.slice(0,g),this.createError(r.ANY_OF_MISSING,{},"","/anyOf",f,a,b)):void 0},o.prototype.validateOneOf=function(a,b,c){if(void 0===b.oneOf)return null;var d,e,f=null,g=[],h=this.errors.length;this.trackUnknownProperties&&(d=this.unknownPropertyPaths,e=this.knownPropertyPaths);for(var i=0;i<b.oneOf.length;i++){this.trackUnknownProperties&&(this.unknownPropertyPaths={},this.knownPropertyPaths={});var j=b.oneOf[i],k=this.errors.length,l=this.validateAll(a,j,[],["oneOf",i],c);if(null===l&&k===this.errors.length){if(null!==f)return this.errors=this.errors.slice(0,h),this.createError(r.ONE_OF_MULTIPLE,{index1:f,index2:i},"","/oneOf",null,a,b);if(f=i,this.trackUnknownProperties){for(var m in this.knownPropertyPaths)e[m]=!0,delete d[m];for(var n in this.unknownPropertyPaths)e[n]||(d[n]=!0)}}else l&&g.push(l)}return this.trackUnknownProperties&&(this.unknownPropertyPaths=d,this.knownPropertyPaths=e),null===f?(g=g.concat(this.errors.slice(h)),this.errors=this.errors.slice(0,h),this.createError(r.ONE_OF_MISSING,{},"","/oneOf",g,a,b)):(this.errors=this.errors.slice(0,h),null)},o.prototype.validateNot=function(a,b,c){if(void 0===b.not)return null;var d,e,f=this.errors.length;this.trackUnknownProperties&&(d=this.unknownPropertyPaths,e=this.knownPropertyPaths,this.unknownPropertyPaths={},this.knownPropertyPaths={});var g=this.validateAll(a,b.not,null,null,c),h=this.errors.slice(f);return this.errors=this.errors.slice(0,f),this.trackUnknownProperties&&(this.unknownPropertyPaths=d,this.knownPropertyPaths=e),null===g&&0===h.length?this.createError(r.NOT_PASSED,{},"","/not",null,a,b):null},o.prototype.validateHypermedia=function(a,b,d){if(!b.links)return null;for(var e,f=0;f<b.links.length;f++){var g=b.links[f];if("describedby"===g.rel){for(var h=new c(g.href),i=!0,j=0;j<h.varNames.length;j++)if(!(h.varNames[j]in a)){i=!1;break}if(i){var k=h.fillFromObject(a),l={$ref:k};if(e=this.validateAll(a,l,[],["links",f],d))return e}}}};var r={INVALID_TYPE:0,ENUM_MISMATCH:1,ANY_OF_MISSING:10,ONE_OF_MISSING:11,ONE_OF_MULTIPLE:12,NOT_PASSED:13,NUMBER_MULTIPLE_OF:100,NUMBER_MINIMUM:101,NUMBER_MINIMUM_EXCLUSIVE:102,NUMBER_MAXIMUM:103,NUMBER_MAXIMUM_EXCLUSIVE:104,NUMBER_NOT_A_NUMBER:105,STRING_LENGTH_SHORT:200,STRING_LENGTH_LONG:201,STRING_PATTERN:202,OBJECT_PROPERTIES_MINIMUM:300,OBJECT_PROPERTIES_MAXIMUM:301,OBJECT_REQUIRED:302,OBJECT_ADDITIONAL_PROPERTIES:303,OBJECT_DEPENDENCY_KEY:304,ARRAY_LENGTH_SHORT:400,ARRAY_LENGTH_LONG:401,ARRAY_UNIQUE:402,ARRAY_ADDITIONAL_ITEMS:403,FORMAT_CUSTOM:500,KEYWORD_CUSTOM:501,CIRCULAR_REFERENCE:600,UNKNOWN_PROPERTY:1e3},s={};for(var t in r)s[r[t]]=t;var u={INVALID_TYPE:"Invalid type: {type} (expected {expected})",ENUM_MISMATCH:"No enum match for: {value}",ANY_OF_MISSING:'Data does not match any schemas from "anyOf"',ONE_OF_MISSING:'Data does not match any schemas from "oneOf"',ONE_OF_MULTIPLE:'Data is valid against more than one schema from "oneOf": indices {index1} and {index2}',NOT_PASSED:'Data matches schema from "not"',NUMBER_MULTIPLE_OF:"Value {value} is not a multiple of {multipleOf}",NUMBER_MINIMUM:"Value {value} is less than minimum {minimum}",NUMBER_MINIMUM_EXCLUSIVE:"Value {value} is equal to exclusive minimum {minimum}",NUMBER_MAXIMUM:"Value {value} is greater than maximum {maximum}",NUMBER_MAXIMUM_EXCLUSIVE:"Value {value} is equal to exclusive maximum {maximum}",NUMBER_NOT_A_NUMBER:"Value {value} is not a valid number",STRING_LENGTH_SHORT:"String is too short ({length} chars), minimum {minimum}",STRING_LENGTH_LONG:"String is too long ({length} chars), maximum {maximum}",STRING_PATTERN:"String does not match pattern: {pattern}",OBJECT_PROPERTIES_MINIMUM:"Too few properties defined ({propertyCount}), minimum {minimum}",OBJECT_PROPERTIES_MAXIMUM:"Too many properties defined ({propertyCount}), maximum {maximum}",OBJECT_REQUIRED:"Missing required property: {key}",OBJECT_ADDITIONAL_PROPERTIES:"Additional properties not allowed",OBJECT_DEPENDENCY_KEY:"Dependency failed - key must exist: {missing} (due to key: {key})",ARRAY_LENGTH_SHORT:"Array is too short ({length}), minimum {minimum}",ARRAY_LENGTH_LONG:"Array is too long ({length}), maximum {maximum}",ARRAY_UNIQUE:"Array items are not unique (indices {match1} and {match2})",ARRAY_ADDITIONAL_ITEMS:"Additional items not allowed",FORMAT_CUSTOM:"Format validation failed ({message})",KEYWORD_CUSTOM:"Keyword failed: {key} ({message})",CIRCULAR_REFERENCE:"Circular $refs: {urls}",UNKNOWN_PROPERTY:"Unknown property (not in schema)"};j.prototype=Object.create(Error.prototype),j.prototype.constructor=j,j.prototype.name="ValidationError",j.prototype.prefixWith=function(a,b){if(null!==a&&(a=a.replace(/~/g,"~0").replace(/\//g,"~1"),this.dataPath="/"+a+this.dataPath),null!==b&&(b=b.replace(/~/g,"~0").replace(/\//g,"~1"),this.schemaPath="/"+b+this.schemaPath),null!==this.subErrors)for(var c=0;c<this.subErrors.length;c++)this.subErrors[c].prefixWith(a,b);return this};var v={},w=l();return w.addLanguage("en-gb",u),w.tv4=w,w});
20996 define('utilities/JsonValidator',[
20997 	"../../thirdparty/tv4/tv4.min.js"
20998 ],
20999 function (tv4) {
21000 
21001 	var jsonValdiator = /** @lends finesse.utilities.JsonValidator */ {
21002 		
21003         /**
21004          * @class
21005          * For JSON validation
21006          * 
21007          * @constructs
21008          */
21009         _fakeConstuctor: function () {
21010             /* This is here for jsdocs. */
21011 		},
21012 
21013 		/**
21014 		 * Validates JSON data by applying a specific schema
21015 		 * 
21016 		 * @param jsonData - JSON data
21017 		 * @param schema - JSON schema that would validate the parameter jsonData. 
21018 		 * It needs to follow the <a href="http://json-schema.org">JSON schema definition standard</a>
21019 		 * @returns - JSON Result that is of the below format
21020 		 * <pre>
21021 		 *  {
21022 		 *    "valid": [true/false], 
21023 		 *    "error": [tv4 error object if schema is not valid]
21024 		 *  }
21025 		 * </pre>
21026 		 * The error object will look something like this:
21027 		 * <pre>
21028 		 * {
21029 		 *    "code": 0,
21030 		 *    "message": "Invalid type: string",
21031 		 *    "dataPath": "/intKey",
21032 		 *    "schemaPath": "/properties/intKey/type"
21033 		 * }
21034 		 * </pre>
21035 		 */
21036 		validateJson: function (jsonData, schema) {
21037 			//window.console.info("To validate schema");
21038 			var valid = tv4.validate(jsonData, schema);
21039 			var result = {};
21040 			result.valid = valid;
21041 			if (!valid) {
21042 				result.error = tv4.error;
21043 			}
21044 			return result;
21045         }
21046 	}
21047 
21048 	
21049 
21050 	window.finesse = window.finesse || {};
21051     window.finesse.utilities = window.finesse.utilities || {};
21052     window.finesse.utilities.JsonValidator = jsonValdiator;
21053     
21054     return jsonValdiator;
21055 });
21056 /* using variables before they are defined.
21057  */
21058 /*global navigator,unescape,sessionStorage,localStorage,_initSessionList,_initSessionListComplete */
21059 
21060 /**
21061  * Allows each gadget to communicate with the server to send logs.
21062  */
21063 
21064 /**
21065  * @class
21066  * @private
21067  * Allows each product to initialize its method of storage
21068  */
21069 define('cslogger/FinesseLogger',["clientservices/ClientServices", "utilities/Utilities"], function (ClientServices, Utilities) {
21070     
21071     var FinesseLogger = (function () { 
21072 
21073         var
21074 
21075         /**
21076          * Array use to collect ongoing logs in memory
21077          * @private
21078          */
21079         _logArray = [],
21080 
21081 		_triedLogPostOnceOnOverload = false;
21082 
21083 		/**
21084          * The final data string sent to the server, =_logArray.join
21085          * @private
21086          */
21087         _logStr = "",
21088 
21089         /**
21090          * Keep track of size of log
21091          * @private
21092          */
21093         _logSize = 0,
21094 
21095         /**
21096          * Flag to keep track show/hide of send log link
21097          * @private
21098          */
21099         _sendLogShown = false,
21100 
21101         /**
21102          * Flag to keep track if local log initialized
21103          * @private
21104          */
21105         _loggingInitialized = false,
21106         
21107 
21108         /**
21109          * local log size limit
21110          * @private
21111          */
21112         _maxLocalStorageSize = 5000000,
21113 
21114         /**
21115          * half local log size limit
21116          * @private
21117          */
21118         _halfMaxLocalStorageSize = 0.5*_maxLocalStorageSize,
21119 
21120         
21121         /**
21122          * threshold for purge 
21123          * @private
21124          */
21125         _purgeStartPercent = 0.75,
21126         
21127         /**
21128          * log item prefix 
21129          * @private
21130          */
21131         _linePrefix = null,
21132         
21133         /**
21134          * locallog session 
21135          * @private
21136          */
21137         _session = null,
21138         
21139         /**
21140          * Flag to keep track show/hide of send log link
21141          * @private
21142          */
21143         _sessionKey = null,
21144         /**
21145          * Log session metadata 
21146          * @private
21147          */
21148         _logInfo = {},
21149         
21150         /**
21151          * Flag to find sessions 
21152          * @private
21153          */
21154 		_findSessionsObj = null,
21155 		
21156 		_userObject = null,
21157 
21158         /**
21159          * Wrap up console.log esp. for IE9 
21160          * @private
21161          */
21162         _myConsoleLog = function (str) {
21163             if (window.console !== undefined) {
21164               window.console.log(str);
21165             }
21166         },
21167         /**
21168          * Initialize the Local Logging
21169          * @private
21170          */
21171         _initLogging = function () {
21172             if (_loggingInitialized) {
21173                 return;
21174             }
21175             //Build a new store
21176             _session = sessionStorage.getItem("finSessKey");
21177             //if the _session is null or empty, skip the init
21178             if (!_session) {
21179               return;
21180             }
21181             _sessionKey = "Fi"+_session;
21182             _linePrefix = _sessionKey + "_";
21183             _logInfo = {};
21184             _logInfo.name = _session;
21185             _logInfo.size = 0;
21186             _logInfo.head = 0;
21187             _logInfo.tail = 0;
21188             _logInfo.startTime = new Date().getTime();
21189             _loggingInitialized = true;
21190             _initSessionList();
21191         },
21192         
21193         /**
21194          * get total data size 
21195          *
21196          * @return {Integer} which is the amount of data stored in local storage.
21197          * @private
21198          */
21199         _getTotalData = function ()
21200         {
21201             var sessName, sessLogInfoStr,sessLogInfoObj, sessionsInfoObj, totalData = 0,
21202             sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
21203             if (!sessionsInfoStr) {
21204                  return 0;
21205             }
21206             sessionsInfoObj = JSON.parse(sessionsInfoStr);
21207 
21208             for (sessName in sessionsInfoObj.sessions)
21209             {
21210                 if (sessionsInfoObj.sessions.hasOwnProperty(sessName)) {
21211                     sessLogInfoStr = localStorage.getItem("Fi" + sessName);
21212                     if (!sessLogInfoStr) {
21213                         _myConsoleLog("_getTotalData failed to get log info for "+sessName);
21214                     }
21215                     else {
21216                        sessLogInfoObj = JSON.parse(sessLogInfoStr);
21217                        totalData = totalData + sessLogInfoObj.size;
21218                     }
21219                 }
21220             }
21221 
21222               return totalData;
21223         },
21224         
21225         /**
21226          * Remove lines from tail up until store size decreases to half of max size limit.
21227          *
21228          * @private
21229          */
21230         _purgeCurrentSession = function() {
21231             var curStoreSize, purgedSize=0, line, tailKey, secLogInfoStr, logInfoStr, theLogInfo;
21232             curStoreSize = _getTotalData();
21233             if (curStoreSize < _halfMaxLocalStorageSize) {
21234                return;
21235             }
21236             logInfoStr = localStorage.getItem(_sessionKey);
21237             if (!logInfoStr) {
21238                return;
21239             }
21240             theLogInfo = JSON.parse(logInfoStr);
21241             //_myConsoleLog("Starting _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
21242             while(curStoreSize > _halfMaxLocalStorageSize) {
21243                try {
21244                    tailKey = _sessionKey+"_"+theLogInfo.tail;
21245                    line = localStorage.getItem(tailKey);
21246                    if (line) {
21247                        purgedSize = purgedSize +line.length;
21248                        localStorage.removeItem(tailKey);
21249                        curStoreSize = curStoreSize - line.length;
21250                        theLogInfo.size = theLogInfo.size - line.length;
21251                    }
21252                }
21253                catch (err) {
21254                    _myConsoleLog("purgeCurrentSession encountered err="+err);
21255                }
21256                if (theLogInfo.tail < theLogInfo.head) {
21257                    theLogInfo.tail = theLogInfo.tail  + 1;
21258                }
21259                else {
21260                    break;
21261                }
21262             }
21263             //purge stops here, we need to update session's meta data in storage
21264             secLogInfoStr = localStorage.getItem(_sessionKey);
21265             if (!secLogInfoStr) {
21266                 //somebody cleared the localStorage
21267                 return;
21268             }
21269             
21270             //_myConsoleLog("In _purgeCurrentSession() - after purging current session, currentStoreSize=" + curStoreSize);
21271             //_myConsoleLog("In _purgeCurrentSession() - after purging purgedSize=" + purgedSize);
21272             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.size=" + theLogInfo.size);
21273             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.tail=" + theLogInfo.tail);
21274             localStorage.setItem(_sessionKey, JSON.stringify(theLogInfo));
21275             _myConsoleLog("Done _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
21276         },
21277        
21278         /**
21279          * Purge a session 
21280          *
21281          * @param sessionName is the name of the session
21282          * @return {Integer} which is the current amount of data purged
21283          * @private
21284          */
21285         _purgeSession = function (sessionName) {
21286               var theLogInfo, logInfoStr, sessionsInfoStr, sessionsInfoObj;
21287               //Get the session logInfo
21288               logInfoStr = localStorage.getItem("Fi" + sessionName);
21289               if (!logInfoStr) {
21290                  _myConsoleLog("_purgeSession failed to get logInfo for "+sessionName);
21291                  return 0;
21292               }
21293               theLogInfo = JSON.parse(logInfoStr);
21294               
21295               //Note: This assumes that we don't crash in the middle of purging
21296               //=> if we do then it should get deleted next time
21297               //Purge tail->head
21298               while (theLogInfo.tail <= theLogInfo.head)
21299               {
21300                   try {
21301                       localStorage.removeItem("Fi" + sessionName + "_" + theLogInfo.tail);
21302                       theLogInfo.tail = theLogInfo.tail + 1;
21303                   }
21304                   catch (err) {
21305                       _myConsoleLog("In _purgeSession err="+err);
21306                       break;
21307                   }
21308               }
21309 
21310               //Remove the entire session
21311               localStorage.removeItem("Fi" + sessionName);
21312 
21313               //Update FinesseSessionsInfo
21314               sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
21315               if (!sessionsInfoStr) {
21316                  _myConsoleLog("_purgeSession could not get sessions Info, it was cleared?");
21317                  return 0;
21318               }
21319               sessionsInfoObj = JSON.parse(sessionsInfoStr);
21320               if (sessionsInfoObj.sessions !== null)
21321               {
21322                  delete sessionsInfoObj.sessions[sessionName];
21323               
21324                  sessionsInfoObj.total = sessionsInfoObj.total - 1;
21325                  sessionsInfoObj.lastWrittenBy = _session;
21326                  localStorage.setItem("FinesseSessionsInfo", JSON.stringify(sessionsInfoObj));
21327               }
21328               
21329               return theLogInfo.size;
21330         },
21331         
21332          /**
21333           * purge old sessions
21334           * 
21335           * @param storeSize
21336 	  * @return {Boolean} whether purging reaches its target
21337           * @private
21338          */
21339          _purgeOldSessions = function (storeSize) {
21340              var sessionsInfoStr, purgedSize = 0, sessName, sessions, curStoreSize, activeSession, sessionsInfoObj;
21341              sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
21342              if (!sessionsInfoStr) {
21343                 _myConsoleLog("Could not get FinesseSessionsInfo");
21344                 return true;
21345              }
21346              sessionsInfoObj = JSON.parse(sessionsInfoStr);
21347              curStoreSize = _getTotalData();
21348              
21349              activeSession = _session;
21350              sessions = sessionsInfoObj.sessions;
21351              for (sessName in sessions) {
21352                 if (sessions.hasOwnProperty(sessName)) {
21353                     if (sessName !== activeSession) {
21354                         purgedSize = purgedSize + _purgeSession(sessName);
21355                         if ((curStoreSize-purgedSize) < _halfMaxLocalStorageSize) {
21356                             return true;
21357                         }
21358                     }
21359                 }
21360              }
21361             //purge is not done, so return false
21362             return false;
21363          },
21364          
21365        /**
21366         * handle insert error
21367         *
21368         * @param error
21369         * @private
21370         */
21371         _insertLineHandleError = function (error) {
21372             _myConsoleLog(error);
21373         },
21374 
21375         /**
21376          * check storage data size and if need purge
21377          * @private
21378          */
21379         _checkSizeAndPurge = function () {
21380             var purgeIsDone=false, totalSize = _getTotalData();
21381             if (totalSize > 0.75*_maxLocalStorageSize) {
21382                _myConsoleLog("in _checkSizeAndPurge, totalSize ("+totalSize+") exceeds limit");
21383                purgeIsDone = _purgeOldSessions(totalSize);
21384                if (purgeIsDone) {
21385                   _myConsoleLog("in _checkSizeAndPurge after purging old session, purge is done");
21386                }
21387                else {
21388                   //after all old sessions purged, still need purge
21389                   totalSize = _getTotalData();
21390                   if (totalSize > 0.75*_maxLocalStorageSize) {
21391                       _myConsoleLog("in _checkSizeAndPurge after purging old session,still needs purging, now storeSize ("+totalSize+")");
21392                      _purgeCurrentSession();
21393                      _myConsoleLog("in _checkSizeAndPurge done purging current session.");
21394                   }
21395                }
21396             }
21397         },
21398         
21399         /**
21400          * check if the session is already in meta data  
21401          * 
21402          * @param metaData
21403          * @param sessionName
21404          * @return {Boolean} true if session has metaData (false otherwise)
21405          * @private
21406          */
21407         _sessionsInfoContains = function (metaData, sessionName) {
21408            if (metaData && metaData.sessions && metaData.sessions.hasOwnProperty(sessionName)) {
21409               return true;
21410            }
21411            return false;
21412         },
21413         
21414         
21415         /**
21416          * setup sessions in local storage 
21417          * 
21418          * @param logInfo
21419          * @private
21420          */
21421         _getAndSetNumberOfSessions = function (logInfo) {
21422             var numOfSessionsPass1, numOfSessionsPass2, l;
21423             numOfSessionsPass1 = localStorage.getItem("FinesseSessionsInfo");
21424             if (numOfSessionsPass1 === null) {
21425                 //Init first time
21426                 numOfSessionsPass1 = {};
21427                 numOfSessionsPass1.total = 1;
21428                 numOfSessionsPass1.sessions = {};
21429                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
21430                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
21431                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
21432             }
21433             else {
21434                 numOfSessionsPass1 = JSON.parse(numOfSessionsPass1);
21435                 //check if the session is already in the FinesseSessionSInfo
21436                 if (_sessionsInfoContains(numOfSessionsPass1, logInfo.name)) {
21437                     return;
21438                 }             
21439                 //Save numOfSessionsPass1
21440                 numOfSessionsPass1.total = parseInt(numOfSessionsPass1.total, 10) + 1;
21441                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
21442                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
21443                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
21444                 numOfSessionsPass2 = localStorage.getItem("FinesseSessionsInfo");
21445                 if (!numOfSessionsPass2) {
21446                    _myConsoleLog("Could not get FinesseSessionsInfo");
21447                    return;
21448                 }
21449                 numOfSessionsPass2 = JSON.parse(numOfSessionsPass2);
21450                 //in future we need to confirm the numOfSessionsPass2 is the same as numOfSessionsPass1
21451                 ////if (numOfSessionsPass1.lastWrittenBy !== numOfSessionsPass2.lastWrittenBy) {
21452                 ////    _myConsoleLog("Rebuild sessions");
21453                 ////    _sessionTimerId = setTimeout(_initSessionList, 10000);
21454                 ////}
21455                 ////else {
21456                 ////    _sessionTimerId = null;
21457                 ////callback(numOfSessionsPass2.sessions);
21458                 ////}
21459             }
21460             if (!localStorage.getItem(_sessionKey)) {
21461                 localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
21462             }
21463         },
21464         
21465         
21466         /**
21467          * init session list 
21468          * @private
21469          */
21470         _initSessionList = function () {
21471             _getAndSetNumberOfSessions(_logInfo);
21472         },
21473         
21474        /**
21475         * do the real store of log line
21476         * 
21477         * @param line
21478         * @private
21479         */
21480         _persistLine = function (line) {
21481             var key, logInfoStr;
21482             logInfoStr = localStorage.getItem(_sessionKey);
21483             if (logInfoStr === null) {
21484                return;
21485             }
21486             _logInfo = JSON.parse(logInfoStr);
21487             _logInfo.head = _logInfo.head + 1;
21488             key = _linePrefix + _logInfo.head;
21489             localStorage.setItem(key, line);
21490             //Save the size
21491             _logInfo.size = _logInfo.size + line.length;
21492             if (_logInfo.tail === 0) {
21493                 _logInfo.tail = _logInfo.head;
21494             }
21495         
21496             localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
21497             _checkSizeAndPurge();
21498         },
21499         
21500         /**
21501          * Insert a line into the localStorage.
21502          *
21503          * @param line line to be inserted 
21504          * @private
21505         */
21506         _insertLine = function (line) {
21507             //_myConsoleLog("_insertLine: [" + line + "]");
21508             //Write the next line to localStorage
21509             try {
21510                //Persist the line 
21511                _persistLine(line);
21512             }
21513             catch (err) {
21514                _myConsoleLog("error in _insertLine(), err="+err);
21515                //_insertLineHandleError(err);
21516             }
21517         },
21518          
21519         
21520         /**
21521          * Clear the local storage
21522          * @private
21523          */
21524         _clearLocalStorage = function() {
21525             localStorage.clear();
21526 
21527         },
21528         
21529         getIntValue = function (strVal, defaultVal) {
21530 		try {
21531 			return parseInt(strVal);
21532 		} catch (e) {
21533 			return defaultVal;
21534 		}
21535 		},
21536 
21537 		/**
21538 		 * To check if the log collection data is present in session
21539 		 * Storing the log schedule data as 3 keys in session
21540 		 * 1.isDesktopAutoLogScheduled: flag which indicates if log collection is scheduled
21541 		 * 2.logSchedule: schedule data stored as JSON object
21542 		 * 3.scheduledLogArrayData: contains the log data of primary server during failover,
21543 		 * which needs to be posted to secondary server as soon as failover is completed
21544 		 */
21545 		checkIfLogCollectionSchedulePresentInSession = function () {
21546 			logCollDataObj = JSON.parse(sessionStorage.getItem(window.finesse.utilities.Utilities.CONSTANTS.LOG_COLL_DATA) || null);
21547 			var ifLogCollScheduled = (logCollDataObj && logCollDataObj.isDesktopAutoLogScheduled === true && (logCollDataObj.logSchedule != null));
21548 			return ifLogCollScheduled;
21549 		},
21550 
21551         /**
21552          * Collect logs when onCollect called
21553          *
21554          * @param data
21555          * @private
21556          */
21557         _collectMethod = function(data) {
21558           //Size of log should not exceed 1.5MB
21559           
21560           var info,maxLength =  getIntValue(window.finesse.container.Config.clientLogSize, 1572864);
21561 
21562           //add size buffer equal to the size of info to be added when publish
21563           info = Utilities.getSanitizedUserAgentString() + "
";
21564           info = escape(info);
21565 
21566             //If log was empty previously, fade in buttons
21567             if (!_sendLogShown) {
21568                 _sendLogShown = true;
21569                 _logSize = info.length;
21570             }
21571             
21572             //if local storage logging is enabled, then insert the log into local storage
21573             if (window.sessionStorage.getItem('enableLocalLog')==='true') {
21574                 if (data) {
21575                    if (data.length>0 && data.substring(0,1) === '\n') {
21576                       _insertLine(data.substring(1));
21577                    }
21578                    else {
21579                       _insertLine(data);
21580                    }
21581                 }
21582             }
21583               
21584             //escape all data to get accurate size (shindig will escape when it builds request)
21585             //escape 6 special chars for XML: &<>"'\n
21586             data = data.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/>/g, ">").replace(/</g, "<").replace(/\n/g, "
");
21587             //Send Error Report crashes with Control characters. Replacing the control characters with empty string
21588             data = data.replace(/[\x00-\x1F\x7F-\x9F]/g, "");
21589             data = escape(data+"\n");
21590 
21591 			
21592             if (data.length < maxLength){
21593                 //make room for new data if log is exceeding max length
21594                 while (_logSize + data.length > maxLength) {
21595 					// If there is a log schedule, post the logs once to server
21596 					if (checkIfLogCollectionSchedulePresentInSession() && !_triedLogPostOnceOnOverload && _userObject) {
21597 						ClientServices.log('FinesseLogger._collectMethod(): Posting the logs once to server as the memory logs exceeded maximum allowed length');
21598 						var callbacks = {success: FinesseLogger.logPostedOnOverloadSuccess, error: FinesseLogger.logPostedOnOverloadFailure};
21599 						ClientServices.log('FinesseLogger._collectMethod(): Setting the flag _triedLogPostOnceOnOverload to true as there will be no retry posting in case of error');
21600 						_triedLogPostOnceOnOverload = true;
21601 						FinesseLogger.publishCompressedClientLog(_userObject, {}, callbacks);
21602 					}
21603 				_logSize -= (_logArray.shift()).length;
21604             }
21605         }
21606 
21607             //Else push the log into memory, increment the log size
21608             _logArray.push(data);
21609 
21610             //inc the size accordingly
21611             _logSize+=data.length;
21612 
21613         };
21614 
21615         return {
21616 
21617             /**
21618              * @private
21619              * Initiate FinesseLogger.
21620              */
21621             init: function () {
21622                 ClientServices.subscribe("finesse.clientLogging.*", _collectMethod);
21623                 _initLogging();
21624             },
21625 
21626             /**
21627              * @param {*} user 
21628              * @param {*} options 
21629              * Initiate subscription for the user
21630              */
21631             initSubscription: function(user, options) {
21632                 options = options || {};
21633 				_userObject = user || null;
21634                 var tmpOnAdd = (options.onAdd && typeof options.onAdd === "function")? options.onAdd : function(){};
21635                 /** @private */
21636                 options.onAdd = function(clientLogObj){
21637                     tmpOnAdd(clientLogObj);
21638                     // Do not clear logs in case of schedule event
21639                     if (clientLogObj && clientLogObj._data && clientLogObj._data.schedule){
21640                     	return;
21641                     }
21642                     _logArray.length = 0; _logSize =0;
21643                     _sendLogShown = false;
21644                 };
21645                 user.getClientLog(options);
21646             },
21647 
21648             /**
21649              * @private
21650              * Clear all items stored in localStorage.
21651             */
21652             clear : function () {
21653                _clearLocalStorage();
21654             },
21655 
21656             /**
21657              * @private
21658              * Initialize the local storage logging.
21659             */
21660             initLocalLog: function () {
21661                _initLogging();
21662             },
21663 
21664             /**
21665              * @private
21666              * Inserts a line into the localStorage.
21667              * @param line to insert
21668             */
21669             localLog : function (line) {
21670                _insertLine(line);
21671             },
21672 
21673 			/**
21674              * @private
21675              * Method to store the log collection data in session storage before failover so that it can be passed
21676 			 * to secondary server during failover and the log collection continues to happen.
21677              * @param line to insert
21678             */
21679 			addLogDataToSessionStorage: function () {
21680 				var cachedLogData = JSON.parse(sessionStorage.getItem(finesse.utilities.Utilities.CONSTANTS.LOG_COLL_DATA) || null);
21681 
21682 				if (checkIfLogCollectionSchedulePresentInSession()) {
21683 					cachedLogData["scheduledLogArrayData"] = _logArray || [];
21684 					sessionStorage.setItem(finesse.utilities.Utilities.CONSTANTS.LOG_COLL_DATA, JSON.stringify(cachedLogData));
21685 				}
21686 			},
21687 			
21688 			/**
21689              * @private
21690              * Method to set the flag _triedLogPostOnceOnOverload to false so that the 
21691 			 * logs can be posted again if the log in memory overflows
21692 			 * for a new schedule
21693 			 * 
21694             */
21695 			setLogPostedFalse: function () {
21696 				_triedLogPostOnceOnOverload = false;
21697 			},
21698 
21699 			/**
21700              * @private
21701              * Callback Method when log posting after memory log overload is success
21702 			 * 
21703             */
21704 		   logPostedOnOverloadSuccess: function () {
21705 				ClientServices.log('FinesseLogger.logPostedOnOverloadSuccess(): posted logs successfully');
21706 			},
21707 
21708 			/**
21709 			 * @private
21710              * Callback Method when log posting after memory log overload is Failure
21711 			 * @param error
21712 			 */
21713 			logPostedOnOverloadFailure: function (err) {
21714 				ClientServices.log('FinesseLogger.logPostedOnOverloadFailure(): logs posting failed:Status:'+err.status+",Content:"+err.content);
21715 			},
21716 
21717 			/**
21718             * @ignore
21719             * Publish Local storage compressed client logs to server and clear the memory
21720             *
21721             * @param userObj
21722             * @param options
21723             * @param callBack
21724             */
21725 			publishLocalStorageCompressedFile: function(userObj, options, callBack) {
21726                 // Avoid null references.
21727                 options = options || {};
21728                 callBack = callBack || {};
21729                 
21730               	var logZip = new JSZip();
21731               	var filename = 'Desktop-Client-LocalStorageLog' + '.' + userObj.getId() +  (options.logScheduleId? '.schedule' + options.logScheduleId: '') + '.' + new Date().toISOString() + '.txt';
21732 				logZip.file(filename, window.finesse.modules.LocalStorageViewer.getContents(null, null));
21733 				logZip.generateAsync({
21734 				type: 'blob',
21735 				compression: 'DEFLATE',
21736 				compressionOptions: {
21737 					level: 9 // level 1 is for (best speed) and 9 is for (best compression)).
21738 				}
21739 				}).then(function (content) {
21740 					options.uploadzipfile = content;
21741                 options.onLoad = function (clientLogObj) {
21742                 	clientLogObj.sendLogs(options.uploadzipfile,{
21743                             error: callBack.error,
21744                             success: callBack.success,
21745                             isZip: true
21746                         });
21747                  };
21748                 userObj.getClientLog(options);
21749 				});
21750             },
21751 
21752 			
21753 			/**
21754             * @ignore
21755             * Publish compressed client logs to server and clear the memory
21756             *
21757             * @param userObj
21758             * @param options
21759             * @param callBack
21760             * @param newLogData
21761             */
21762         	publishCompressedClientLog: function(userObj, options, callBack, newLogData) {
21763                 // Avoid null references.
21764                 options = options || {};
21765                 callBack = callBack || {};
21766 
21767                 if (callBack.sending === "function") {
21768                     callBack.sending();
21769                 }
21770                 
21771                 _logStr = Utilities.getSanitizedUserAgentString() + "
";
21772 				if(newLogData && newLogData.length > 0) {
21773 					_logStr += unescape(newLogData.join(""));
21774 				} else {
21775 					//join the logs to correct string format
21776 					_logStr += unescape(_logArray.join(""));
21777 				}
21778 				
21779   				var filename = 'Desktop-ClientLog' + '.' + userObj.getId() + (options.logScheduleId? '.schedule' + options.logScheduleId: '') + '.' + new Date().toISOString() + '.txt';
21780 
21781 				_logStr = _logStr.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/
/g, "");
21782 
21783 				var logZip = new JSZip();
21784 				logZip.file(filename, _logStr);
21785 				logZip.generateAsync({
21786 				type: 'blob',
21787 				compression: 'DEFLATE',
21788 				compressionOptions: {
21789 					level: 9
21790 				}
21791 				}).then(function (content) {
21792 					options.uploadzipfile = content;
21793 					
21794 					var tmpOnAdd = (options.onAdd && typeof options.onAdd === "function")? options.onAdd : function(){};
21795                 /** @private */
21796                 options.onAdd = function(){
21797                     tmpOnAdd();
21798                     _logArray.length = 0; _logSize =0;
21799                     _sendLogShown = false;
21800                     callBack.success();
21801                     };
21802                 //adding onLoad to the callbacks, this is the subscribe success case for the first time user subscribe to the client log node
21803                 /** @private */
21804                 options.onLoad = function (clientLogObj) {
21805                 	clientLogObj.sendLogs(options.uploadzipfile,{
21806                             error: callBack.error,
21807                             success: options.onAdd,
21808                             isZip: true
21809                         });
21810                  };
21811 
21812                 userObj.getClientLog(options);
21813 				});
21814             },
21815             
21816 			/**
21817             * @ignore
21818             * Publish logs to server and clear the memory
21819             *
21820             * @param userObj
21821             * @param options
21822             * @param callBack
21823             */
21824         	publish: function(userObj, options, callBack) {
21825                 // Avoid null references.
21826                 options = options || {};
21827                 callBack = callBack || {};
21828 
21829                 if (callBack.sending === "function") {
21830                     callBack.sending();
21831                 }
21832 
21833                 //logs the basic version and machine info and escaped new line
21834                 _logStr = Utilities.getSanitizedUserAgentString() + "
";
21835 
21836 				//join the logs to correct string format
21837 				_logStr += unescape(_logArray.join(""));
21838 				
21839 
21840                 //turning log string to JSON obj
21841                 var logObj = {
21842                         ClientLog: {
21843                         logData : _logStr //_logStr
21844                     }
21845                 },
21846                 tmpOnAdd = (options.onAdd && typeof options.onAdd === "function")? options.onAdd : function(){};
21847                 /** @private */
21848                 options.onAdd = function(){
21849                     tmpOnAdd();
21850                     _logArray.length = 0; _logSize =0;
21851                     _sendLogShown = false;
21852                     };
21853                 //adding onLoad to the callbacks, this is the subscribe success case for the first time user subscribe to the client log node
21854                 /** @private */
21855                 options.onLoad = function (clientLogObj) {
21856                     clientLogObj.sendLogs(logObj,{
21857                             error: callBack.error
21858                         });
21859                     };
21860                 userObj.getClientLog(options);
21861             }
21862         };
21863     }());
21864 
21865     window.finesse = window.finesse || {};
21866     window.finesse.cslogger = window.finesse.cslogger || {};
21867     /** @private */
21868     window.finesse.cslogger.FinesseLogger = FinesseLogger;
21869 
21870     return FinesseLogger;
21871 });
21872 
21873 /**
21874  *  Contains a list of topics used for containerservices pubsub.
21875  *
21876  */
21877 
21878 /**
21879  * @class
21880  * Contains a list of topics with some utility functions.
21881  */
21882 /** @private */
21883 define('containerservices/Topics',[], function () {
21884 
21885     var Topics = (function () {
21886 
21887     /**
21888      * The namespace prepended to all Finesse topics.
21889      */
21890     this.namespace = "finesse.containerservices";
21891 
21892     /**
21893      * @private
21894      * Gets the full topic name with the ContainerServices namespace prepended.
21895      * @param {String} topic
21896      *     The topic category.
21897      * @returns {String}
21898      *     The full topic name with prepended namespace.
21899      */
21900     var _getNSTopic = function (topic) {
21901         return this.namespace + "." + topic;
21902     };
21903 
21904 
21905 
21906     /** @scope finesse.containerservices.Topics */
21907     return {
21908         /** 
21909          * @private
21910          * request channel. */
21911         REQUESTS: _getNSTopic("requests"),
21912 
21913         /** 
21914          * @private
21915          * reload gadget channel. */
21916         RELOAD_GADGET: _getNSTopic("reloadGadget"),
21917 
21918         /**
21919          * @private
21920          * Convert a Finesse REST URI to a OpenAjax compatible topic name.
21921          */
21922         getTopic: function (restUri) {
21923             //The topic should not start with '/' else it will get replaced with
21924             //'.' which is invalid.
21925             //Thus, remove '/' if it is at the beginning of the string
21926             if (restUri.indexOf('/') === 0) {
21927                 restUri = restUri.substr(1);
21928             }
21929 
21930             //Replace every instance of "/" with ".". This is done to follow the
21931             //OpenAjaxHub topic name convention.
21932             return restUri.replace(/\//g, ".");
21933         }
21934     };
21935 	}());
21936 	
21937 	window.finesse = window.finesse || {};
21938     window.finesse.containerservices = window.finesse.containerservices || {};
21939     window.finesse.containerservices.Topics = Topics;
21940     
21941     /** @namespace JavaScript class objects and methods to handle gadget container services.*/
21942     finesse.containerservices = finesse.containerservices || {};
21943 
21944     return Topics;
21945  });
21946 
21947 /** The following comment is to prevent jslint errors about 
21948  * using variables before they are defined.
21949  */
21950 /*global finesse*/
21951 
21952 /**
21953  * Per containerservices request, publish to the OpenAjax gadget pubsub infrastructure.
21954  *
21955  * @requires OpenAjax, finesse.containerservices.Topics
21956  */
21957 
21958 /** @private */
21959 define('containerservices/MasterPublisher',[
21960     "utilities/Utilities",
21961     "containerservices/Topics"
21962 ],
21963 function (Utilities, Topics) {
21964 
21965     var MasterPublisher = function () {
21966 
21967     var
21968     
21969     /**
21970      * Reference to the gadget pubsub Hub instance.
21971      * @private
21972      */
21973     _hub = gadgets.Hub,
21974 
21975     /**
21976      * Reference to the Topics class.
21977      * @private
21978      */
21979     _topics = Topics,
21980     
21981     /**
21982      * Reference to conversion utilities class.
21983      * @private
21984      */
21985     _utils = Utilities,
21986     
21987     /**
21988      * References to ClientServices logger methods
21989      * @private
21990      */
21991     _logger = {
21992         log: finesse.clientservices.ClientServices.log
21993     },
21994     
21995    /**
21996      * The types of possible request types supported when listening to the
21997      * requests channel. Each request type could result in different operations.
21998      * @private
21999      */
22000     _REQTYPES = {
22001 		ACTIVETAB: "ActiveTabReq",
22002 		SET_ACTIVETAB: "SetActiveTabReq",
22003         RELOAD_GADGET: "ReloadGadgetReq"
22004     },
22005 
22006     /**
22007      * Handles client requests made to the request topic. The type of the
22008      * request is described in the "type" property within the data payload. Each
22009      * type can result in a different operation.
22010      * @param {String} topic
22011      *     The topic which data was published to.
22012      * @param {Object} data
22013      *     The data containing requests information published by clients.
22014      * @param {String} data.type
22015      *     The type of the request. Supported: "ActiveTabReq", "SetActiveTabReq", "ReloadGadgetReq"
22016      * @param {Object} data.data
22017      *     May contain data relevant for the particular requests.
22018      * @param {String} [data.invokeID]
22019      *     The ID used to identify the request with the response. The invoke ID
22020      *     will be included in the data in the publish to the topic. It is the
22021      *     responsibility of the client to correlate the published data to the
22022      *     request made by using the invoke ID.
22023      * @private
22024      */
22025     _clientRequestHandler = function (topic, data) {
22026     
22027         //Ensure a valid data object with "type" and "data" properties.
22028         if (typeof data === "object" &&
22029                 typeof data.type === "string" &&
22030                 typeof data.data === "object") {
22031 			switch (data.type) {
22032 			case _REQTYPES.ACTIVETAB:
22033                 _hub.publish("finesse.containerservices.activeTab", finesse.container.Tabs.getActiveTab());
22034                 break;
22035             case _REQTYPES.SET_ACTIVETAB:
22036                 if (typeof data.data.id === "string") {
22037                     _logger.log("Handling request to activate tab: " + data.data.id);
22038                     if (!finesse.container.Tabs.activateTab(data.data.id)) {
22039                         _logger.log("No tab found with id: " + data.data.id);
22040                     }
22041                 }
22042                 break;
22043             case _REQTYPES.RELOAD_GADGET:
22044                 _hub.publish("finesse.containerservices.reloadGadget", data.data);
22045                 break;
22046 			default:
22047 				break;
22048 			}
22049         }
22050     };
22051 
22052     (function () {
22053 
22054         //Listen to a request channel to respond to any requests made by other
22055         //clients because the Master may have access to useful information.
22056         _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
22057     }());
22058 
22059     //BEGIN TEST CODE//
22060     /**
22061      * Test code added to expose private functions that are used by unit test
22062      * framework. This section of code is removed during the build process
22063      * before packaging production code. The [begin|end]TestSection are used
22064      * by the build to identify the section to strip.
22065      * @ignore
22066      */
22067     this.beginTestSection = 0;
22068 
22069     /**
22070      * @ignore
22071      */
22072     this.getTestObject = function () {
22073         //Load mock dependencies.
22074         var _mock = new MockControl();
22075         _hub = _mock.createMock(gadgets.Hub);
22076 
22077         return {
22078             //Expose mock dependencies
22079             mock: _mock,
22080             hub: _hub,
22081 			
22082             //Expose internal private functions
22083             reqtypes: _REQTYPES,
22084             
22085             clientRequestHandler: _clientRequestHandler
22086 
22087         };
22088     };
22089 
22090 
22091     /**
22092      * @ignore
22093      */
22094     this.endTestSection = 0;
22095     //END TEST CODE//
22096 	};
22097 	
22098 	window.finesse = window.finesse || {};
22099     window.finesse.containerservices = window.finesse.containerservices || {};
22100     window.finesse.containerservices.MasterPublisher = MasterPublisher;
22101 	
22102     return MasterPublisher;
22103 });
22104 
22105 /**
22106  * JavaScript representation of the Finesse WorkflowActionEvent object.
22107  *
22108  * @requires finesse.FinesseBase
22109  */
22110 
22111 /** The following comment is to prevent jslint errors about 
22112  * using variables before they are defined.
22113  */
22114 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
22115 /** @private */
22116 define('containerservices/WorkflowActionEvent', ["FinesseBase"], function (FinesseBase) {
22117     var WorkflowActionEvent = FinesseBase.extend(/** @lends finesse.containerservices.WorkflowActionEvent.prototype */{
22118         /**
22119          * Reference to the WorkflowActionEvent name
22120          * This will be set by setWorkflowActionEvent
22121          * @private
22122          */
22123         _name: null,
22124 
22125         /**
22126          * Reference to the WorkflowActionEvent type
22127          * This will be set by setWorkflowActionEvent
22128          * @private
22129          */
22130         _type: null,
22131 
22132         /**
22133          * Reference to the WorkflowActionEvent handledBy value
22134          * This will be set by setWorkflowActionEvent
22135          * @private
22136          */
22137         _handledBy: null,
22138 
22139         /**
22140          * Reference to the WorkflowActionEvent params array
22141          * This will be set by setWorkflowActionEvent
22142          * @private
22143          */
22144         _params: [],
22145 
22146         /**
22147          * Reference to the WorkflowActionEvent actionVariables array
22148          * This will be set by setWorkflowActionEvent
22149          * @private
22150          */            
22151         _actionVariables: [], 
22152         
22153         /**
22154          * @class
22155          * JavaScript representation of a WorkflowActionEvent object.
22156          * The WorkflowActionEvent object is delivered as the payload of
22157          * a WorkflowAction callback.  This can be subscribed to by using
22158          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
22159          * topic of {@link finesse.containerservices.ContainerServices.Topics#WORKFLOW_ACTION_EVENT}. 
22160          * Gadgets should key on events with a handleBy value of "OTHER".
22161          * 
22162          * @constructs
22163          **/
22164         init: function () {
22165             this._super();
22166         },        
22167 
22168         /**
22169 	     * Validate that the passed in object is a WorkflowActionEvent object
22170 	     * and sets the variables if it is
22171 	     * @param maybeWorkflowActionEvent A possible WorkflowActionEvent object to be evaluated and set if 
22172 	     *                                 it validates successfully.
22173 	     * @returns {Boolean} Whether it is valid or not.
22174          * @private
22175 	     */
22176 	    setWorkflowActionEvent: function(maybeWorkflowActionEvent) {
22177 	        var returnValue;
22178 	
22179 	        if (maybeWorkflowActionEvent.hasOwnProperty("name") === true &&
22180 	                maybeWorkflowActionEvent.hasOwnProperty("type") === true &&
22181                     maybeWorkflowActionEvent.hasOwnProperty("handledBy") === true &&
22182 	                maybeWorkflowActionEvent.hasOwnProperty("params") === true &&
22183 	                maybeWorkflowActionEvent.hasOwnProperty("actionVariables") === true) {
22184 	            this._name = maybeWorkflowActionEvent.name;
22185 	            this._type = maybeWorkflowActionEvent.type;
22186                 this._handledBy = maybeWorkflowActionEvent.handledBy;
22187 	            this._params = maybeWorkflowActionEvent.params;
22188 	            this._actionVariables = maybeWorkflowActionEvent.actionVariables;
22189 	            returnValue = true;
22190 	        } else {
22191 	            returnValue = false;
22192 	        }
22193 	
22194 	        return returnValue;
22195 	    },
22196 	
22197 	    /**
22198 	     * Getter for the WorkflowActionEvent name.
22199 	     * @returns {String} The name of the WorkflowAction.
22200 	     */
22201 	    getName: function () {
22202 	        // escape nulls to empty string
22203 	        return this._name || "";
22204 	    },
22205 	
22206 	    /**
22207 	     * Getter for the WorkflowActionEvent type.
22208 	     * @returns {String} The type of the WorkflowAction (BROWSER_POP, HTTP_REQUEST).
22209 	     */
22210 	    getType: function () {
22211 	        // escape nulls to empty string
22212 	        return this._type || "";
22213 	    },
22214 	
22215         /**
22216          * Getter for the WorkflowActionEvent handledBy value. Gadgets should look for
22217          * events with a handleBy of "OTHER".
22218          * @see finesse.containerservices.WorkflowActionEvent.HandledBy
22219          * @returns {String} The handledBy value of the WorkflowAction that is a value of {@link finesse.containerservices.WorkflowActionEvent.HandledBy}.
22220          */
22221         getHandledBy: function () {
22222             // escape nulls to empty string
22223             return this._handledBy || "";
22224         },
22225 
22226 
22227 	    /**
22228 	     * Getter for the WorkflowActionEvent Params map.
22229 	     * @returns {Object} key = param name, value = Object{name, value, expandedValue}
22230 	     * BROWSER_POP<ul>
22231 	     * <li>windowName : Name of window to pop into, or blank to always open new window.
22232 	     * <li>path : URL to open.</ul>
22233 	     * HTTP_REQUEST<ul>
22234 	     * <li>method : "PUT" or "POST".
22235 	     * <li>location : "FINESSE" or "OTHER".
22236 	     * <li>contentType : MIME type of request body, if applicable, e.g. "text/plain".
22237 	     * <li>path : Request URL.
22238 	     * <li>body : Request content for POST requests.</ul>
22239 	     */
22240 	    getParams: function () {
22241 	        var map = {},
22242 	            params = this._params,
22243 	            i,
22244 	            param;
22245 	
22246 	        if (params === null || params.length === 0) {
22247 	            return map;
22248 	        }
22249 	
22250 	        for (i = 0; i < params.length; i += 1) {
22251 	            param = params[i];
22252 	            // escape nulls to empty string
22253 	            param.name = param.name || "";
22254 	            param.value = param.value || "";
22255 	            param.expandedValue = param.expandedValue || "";
22256 	            map[param.name] = param;
22257 	        }
22258 	
22259 	        return map;
22260 	    },
22261 	    
22262 	    /**
22263 	     * Getter for the WorkflowActionEvent ActionVariables map
22264 	     * @returns {Object} key = action variable name, value = Object{name, type, node, testValue, actualValue}
22265 	     */
22266 	    getActionVariables: function() {
22267 	        var map = {},
22268 	            actionVariables = this._actionVariables,
22269 	            i,
22270 	            actionVariable;
22271 	
22272 	        if (actionVariables === null || actionVariables.length === 0) {
22273 	            return map;
22274 	        }
22275 	
22276 	        for (i = 0; i < actionVariables.length; i += 1) {
22277 	            actionVariable = actionVariables[i];
22278 	            // escape nulls to empty string
22279 	            actionVariable.name = actionVariable.name || "";
22280 	            actionVariable.type = actionVariable.type || "";
22281 	            actionVariable.node = actionVariable.node || "";
22282 	            actionVariable.testValue = actionVariable.testValue || "";
22283 	            actionVariable.actualValue = actionVariable.actualValue || "";
22284 	            map[actionVariable.name] = actionVariable;
22285 	        }
22286 	
22287 	        return map;
22288 	    }
22289     }); 
22290     
22291     
22292     WorkflowActionEvent.HandledBy = /** @lends finesse.containerservices.WorkflowActionEvent.HandledBy.prototype */ {
22293         /**
22294          * This specifies that Finesse will handle this WorkflowActionEvent.  A 3rd Party can do additional processing
22295          * with the action, but first and foremost Finesse will handle this WorkflowAction.
22296          */
22297         FINESSE: "FINESSE",
22298 
22299         /**
22300          * This specifies that a 3rd Party will handle this WorkflowActionEvent.  Finesse's Workflow Engine Executor will 
22301          * ignore this action and expects Gadget Developers to take action.
22302          */
22303         OTHER: "OTHER",
22304         
22305         /**
22306          * @class This is the set of possible HandledBy values used for WorkflowActionEvent from ContainerServices.  This
22307          * is provided from the {@link finesse.containerservices.WorkflowActionEvent#getHandledBy} method.
22308          * @constructs
22309          */
22310         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
22311     };    
22312     
22313     window.finesse = window.finesse || {};
22314     window.finesse.containerservices = window.finesse.containerservices || {};
22315     window.finesse.containerservices.WorkflowActionEvent = WorkflowActionEvent;
22316     
22317     return WorkflowActionEvent;
22318 });
22319 
22320 /**
22321  * JavaScript representation of the Finesse TimerTickEvent
22322  *
22323  * @requires finesse.FinesseBase
22324  */
22325 
22326 /** The following comment is to prevent jslint errors about 
22327  * using variables before they are defined.
22328  */
22329 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
22330 /** @private */
22331 define('containerservices/TimerTickEvent',[
22332     "FinesseBase"
22333 ],
22334 function (FinesseBase) {
22335     var TimerTickEvent = FinesseBase.extend(/** @lends finesse.containerservices.TimerTickEvent.prototype */{
22336         /**
22337          * date the TimerTickEvent was queued 
22338          * @private
22339          */
22340         _dateQueued: null,
22341 
22342         /**
22343          * the frequency of the timer tick (in miiliseconds)
22344          * @private
22345          */
22346         _tickFrequency: 1000,
22347 
22348         /**
22349          * @class
22350          * JavaScript representation of a TimerTickEvent object.
22351          * The TimerTickEvent object is delivered as the payload of
22352          * a TimerTickEvent callback.  This can be subscribed to by using
22353          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
22354          * topic of {@link finesse.containerservices.ContainerServices.Topics#TIMER_TICK_EVENT}. 
22355          * 
22356          * @constructs
22357          **/
22358         init: function (tickFrequency, dateQueued) {
22359             this._super();
22360             
22361             this._tickFrequency = tickFrequency;
22362             this._dateQueued = dateQueued;
22363         },
22364 
22365        /**
22366          * Get the "tickFrequency" field
22367          * @param {int} which is the "TickFrequency" field
22368          * @private
22369          */
22370         getTickFrequency: function () {
22371             return this._tickFrequency;
22372         },
22373 
22374         /**
22375          * Getter for the TimerTickEvent "DateQueued" field. 
22376          * @returns {Date} which is a Date object when the TimerTickEvent was queued
22377          */
22378         getDateQueued: function () {
22379             return this._dateQueued;
22380         }
22381 
22382     });
22383     
22384     window.finesse = window.finesse || {};
22385     window.finesse.containerservices = window.finesse.containerservices || {};
22386     window.finesse.containerservices.TimerTickEvent = TimerTickEvent;
22387     
22388     return TimerTickEvent;
22389 });
22390 
22391 /**
22392  * JavaScript representation of the Finesse GadgetViewChangedEvent object.
22393  *
22394  * @requires finesse.FinesseBase
22395  */
22396 
22397 /** The following comment is to prevent jslint errors about 
22398  * using variables before they are defined.
22399  */
22400 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
22401 /** @private */
22402 define('containerservices/GadgetViewChangedEvent',[
22403     "FinesseBase"
22404 ],
22405 function (FinesseBase) {
22406     var GadgetViewChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.GadgetViewChangedEvent.prototype */{
22407         /**
22408          * Reference to the gadget id
22409          * @private
22410          */
22411         _gadgetId: null,
22412 
22413         /**
22414          * Reference to the tab id
22415          * @private
22416          */
22417         _tabId: null,
22418 
22419         /**
22420          * Reference to the maxAvailableHeight
22421          * @private
22422          */
22423         _maxAvailableHeight: null,
22424 
22425         /**
22426          * Reference to the view
22427          * E.g. 'default' or 'canvas'
22428          * @private
22429          */
22430         _view: null,
22431         
22432         /**
22433          * @class
22434          * JavaScript representation of a GadgetViewChangedEvent object.
22435          * The GadgetViewChangedEvent object is delivered as the payload of
22436          * a GadgetViewChangedEvent callback.  This can be subscribed to by using
22437          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
22438          * topic of {@link finesse.containerservices.ContainerServices.Topics#GADGET_VIEW_CHANGED_EVENT}. 
22439          * 
22440          * @constructs
22441          **/
22442         init: function (gadgetId, tabId, maxAvailableHeight, view) {
22443             this._super();
22444 
22445             this._gadgetId = gadgetId;
22446             this._tabId = tabId;
22447             this._maxAvailableHeight = maxAvailableHeight;
22448             this._view = view;
22449         },
22450     
22451         /**
22452          * Getter for the gadget id.
22453          * @returns {String} The identifier for the gadget changing view.
22454          */
22455         getGadgetId: function () {
22456             // escape nulls to empty string
22457             return this._gadgetId || "";
22458         },
22459     
22460         /**
22461          * Getter for the maximum available height.
22462          * @returns {String} The maximum available height for the gadget's view.
22463          */
22464         getMaxAvailableHeight: function () {
22465             // escape nulls to empty string
22466             return this._maxAvailableHeight || "";
22467         },
22468 
22469         /**
22470          * Getter for the tab id.
22471          * @returns {String} The identifier for the tab where the gadget changing view resides.
22472          */
22473         getTabId: function () {
22474             // escape nulls to empty string
22475             return this._tabId || "";
22476         },
22477 
22478         /**
22479          * Getter for the view.
22480          * @returns {String} The view type the gadget is changing to.
22481          */
22482         getView: function () {
22483             // escape nulls to empty string
22484             return this._view || "";
22485         }
22486     });
22487     
22488     window.finesse = window.finesse || {};
22489     window.finesse.containerservices = window.finesse.containerservices || {};
22490     window.finesse.containerservices.GadgetViewChangedEvent = GadgetViewChangedEvent;
22491     
22492     return GadgetViewChangedEvent;
22493 });
22494 
22495 /**
22496  * JavaScript representation of the Finesse MaxAvailableHeightChangedEvent object.
22497  *
22498  * @requires finesse.FinesseBase
22499  */
22500 
22501 /** The following comment is to prevent jslint errors about 
22502  * using variables before they are defined.
22503  */
22504 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
22505 /** @private */
22506 define('containerservices/MaxAvailableHeightChangedEvent',[
22507     "FinesseBase"
22508 ],
22509 function (FinesseBase) {
22510     var MaxAvailableHeightChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.MaxAvailableHeightChangedEvent.prototype */{
22511 
22512         /**
22513          * Reference to the maxAvailableHeight
22514          * @private
22515          */
22516         _maxAvailableHeight: null,
22517         
22518         /**
22519          * @class
22520          * JavaScript representation of a MaxAvailableHeightChangedEvent object.
22521          * The MaxAvailableHeightChangedEvent object is delivered as the payload of
22522          * a MaxAvailableHeightChangedEvent callback.  This can be subscribed to by using
22523          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
22524          * topic of {@link finesse.containerservices.ContainerServices.Topics#MAX_AVAILABLE_HEIGHT_CHANGED_EVENT}. 
22525          * 
22526          * @constructs
22527          **/
22528         init: function (maxAvailableHeight) {
22529             this._super();
22530 
22531             this._maxAvailableHeight = maxAvailableHeight;
22532         },
22533     
22534         /**
22535          * Getter for the maximum available height.
22536          * @returns {String} The maximum available height for a gadget in canvas view
22537          */
22538         getMaxAvailableHeight: function () {
22539             // escape nulls to empty string
22540             return this._maxAvailableHeight || "";
22541         }
22542     });
22543     
22544     window.finesse = window.finesse || {};
22545     window.finesse.containerservices = window.finesse.containerservices || {};
22546     window.finesse.containerservices.MaxAvailableHeightChangedEvent = MaxAvailableHeightChangedEvent;
22547     
22548     return MaxAvailableHeightChangedEvent;
22549 });
22550 
22551 /**
22552  * Exposes a set of API wrappers that will hide the dirty work of
22553  *     constructing Finesse API requests and consuming Finesse events.
22554  *
22555  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
22556  */
22557 
22558 /** The following comment is to prevent jslint errors about using variables before they are defined. */
22559 /*global window:true, gadgets:true, publisher:true, define:true, finesse:true, _tabTracker:true, _workflowActionEventTracker:true, _masterReloader:true, _accessTokenRefreshed:true, frameElement:true, $:true, parent:true, MockControl:true, _getNotifierReference:true, _gadgetViewChanged:true, _maxAvailableHeightChanged:true */
22560 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
22561 /** @private */
22562 define('containerservices/ContainerServices',[
22563     "utilities/Utilities",
22564     "restservices/Notifier",
22565     "containerservices/Topics",
22566     "containerservices/MasterPublisher",
22567     "containerservices/WorkflowActionEvent",
22568     "containerservices/TimerTickEvent",
22569     "containerservices/GadgetViewChangedEvent",
22570     "containerservices/MaxAvailableHeightChangedEvent"
22571 ],
22572 function (Utilities, Notifier, Topics, MasterPublisher, WorkflowActionEvent) {
22573 
22574     var ContainerServices = ( function () { /** @lends finesse.containerservices.ContainerServices.prototype */
22575 
22576     var
22577 
22578     /**
22579      * Shortcut reference to the Utilities singleton
22580      * This will be set by init()
22581      * @private
22582      */
22583     _util,
22584 
22585     /**
22586      * Shortcut reference to the gadget pubsub Hub instance.
22587      * This will be set by init()
22588      * @private
22589      */
22590     _hub,
22591 
22592     /**
22593      * Boolean whether this instance is master or not
22594      * @private
22595      */
22596     _master = false,
22597 
22598     /**
22599      * Whether the Client Services have been initiated yet.
22600      * @private
22601      */
22602     _inited = false,
22603     
22604     /**
22605      * References to ClientServices logger methods
22606      * @private
22607      */
22608     _logger = {
22609         log: finesse.clientservices.ClientServices.log
22610     },
22611     
22612      /**
22613      * Stores the list of subscription IDs for all subscriptions so that it
22614      * could be retrieve for unsubscriptions.
22615      * @private
22616      */
22617     _subscriptionID = {},
22618     
22619     /**
22620      * Reference to the gadget's parent container
22621      * @private
22622      */
22623     _container,
22624 
22625     /**
22626      * Reference to the MasterPublisher
22627      * @private
22628      */
22629     _publisher,
22630     
22631     /**
22632      * Object that will contain the Notifiers
22633      * @private
22634      */
22635     _notifiers = {},
22636 
22637     /**
22638      * Reference to the tabId that is associated with the gadget
22639      * @private
22640      */
22641     _myTab = null,
22642     
22643     /**
22644      * Reference to the visibility of current gadget
22645      * @private
22646      */
22647     _visible = false,
22648     
22649     /**
22650      * Reference for auth modes constants.
22651      * @private
22652      */
22653     _authModes,
22654     
22655     /**
22656      * Shortcut reference to the Topics class.
22657      * This will be set by init()
22658      * @private
22659      */
22660     _topics,
22661     
22662     /**
22663      * Check whether the common desktop apis are available for finext.
22664      * In case it not available it will use the existing finesse Tab logic
22665      * @private
22666      */
22667     _commonDesktop,
22668 
22669     /**
22670      * Associates a topic name with the private handler function.
22671      * Adding a new topic requires that you add this association here 
22672      *  in to keep addHandler generic.
22673      * @param {String} topic : Specifies the callback to retrieve
22674      * @return {Function} The callback function associated with the topic param.
22675      * @private
22676      */
22677     _topicCallback = function (topic) {
22678         var callback, notifier;
22679         switch (topic)
22680         {
22681             case finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB:
22682                 callback = _tabTracker;
22683                 break;
22684             case finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT:
22685                 callback = _workflowActionEventTracker;
22686                 break;
22687             case finesse.containerservices.ContainerServices.Topics.RELOAD_GADGET_EVENT:
22688                 callback = _masterReloader;
22689                 break;
22690             case finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT:
22691                 callback = _gadgetViewChanged;
22692                 break;
22693             case finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT:
22694                 callback = _maxAvailableHeightChanged;
22695                 break;
22696             case finesse.containerservices.ContainerServices.Topics.ACCESS_TOKEN_REFRESHED_EVENT:
22697                 callback = _accessTokenRefreshed;
22698                 break;
22699             default:
22700                 callback = function (param) {
22701                      var data = null;
22702                      
22703                      notifier = _getNotifierReference(topic);
22704                      
22705                      if (arguments.length === 1) {
22706                         data = param;
22707                      } else {
22708                         data = arguments;
22709                      }
22710                      notifier.notifyListeners(data);
22711                 };
22712         }
22713         return callback;
22714     },
22715 
22716     /**
22717      * Ensure that ClientServices have been inited.
22718      * @private
22719      */
22720     _isInited = function () {
22721         if (!_inited) {
22722             throw new Error("ContainerServices needs to be inited.");
22723         }
22724         return _inited;
22725     },
22726 
22727     /**
22728      * Retrieves a Notifier reference to a particular topic, and creates one if it doesn't exist.
22729      * @param {String} topic : Specifies the notifier to retrieve
22730      * @return {Notifier} The notifier object.
22731      * @private
22732      */
22733     _getNotifierReference = function (topic) {
22734         if (!_notifiers.hasOwnProperty(topic))
22735         {
22736             _notifiers[topic] = new Notifier();
22737         }
22738 
22739         return _notifiers[topic];
22740     },
22741 
22742     /**
22743      * Utility function to make a subscription to a particular topic. Only one
22744      * callback function is registered to a particular topic at any time.
22745      * @param {String} topic
22746      *     The full topic name. The topic name should follow the OpenAjax
22747      *     convention using dot notation (ex: finesse.api.User.1000).
22748      * @param {Function} callback
22749      *     The function that should be invoked with the data when an event
22750      *     is delivered to the specific topic.
22751      * @returns {Boolean}
22752      *     True if the subscription was made successfully and the callback was
22753      *     been registered. False if the subscription already exist, the
22754      *     callback was not overwritten.
22755      * @private
22756      */
22757     _subscribe = function (topic, callback) {
22758         _isInited();
22759 
22760         //Ensure that the same subscription isn't made twice.
22761         if (!_subscriptionID[topic]) {
22762             //Store the subscription ID using the topic name as the key.
22763             _subscriptionID[topic] = _hub.subscribe(topic,
22764                 //Invoke the callback just with the data object.
22765                 function (topic, data) {
22766                     callback(data);
22767                 });
22768             return true;
22769         }
22770         return false;
22771     },
22772 
22773     /**
22774      * Unsubscribe from a particular topic.
22775      * @param {String} topic : The full topic name.
22776      * @private
22777      */
22778     _unsubscribe = function (topic) {
22779         _isInited();
22780 
22781         //Unsubscribe from the topic using the subscription ID recorded when
22782         //the subscription was made, then delete the ID from data structure.
22783         _hub.unsubscribe(_subscriptionID[topic]);
22784         delete _subscriptionID[topic];
22785     },
22786 
22787     /**
22788      * Get my tab id.
22789      * @returns {String} tabid : The tabid of this container/gadget.
22790      * @private
22791      */
22792     _getMyTab = function () {
22793     	
22794     	// Adding startsWith to the string prototype for IE browser
22795     	// See defect CSCvj93044
22796     	
22797     	if (!String.prototype.startsWith) {
22798       	  String.prototype.startsWith = function(searchString, position) {
22799       	    position = position || 0;
22800       	    return this.indexOf(searchString, position) === position;
22801       	  };
22802       	}
22803     	
22804 	if(_commonDesktop){
22805         /**
22806          *  This change is done for SPOG. SPOG container will set routNmae(i.e. current nav item)
22807          *  as user preference
22808          */
22809         var prefs,routeName;
22810         if (gadgets && gadgets.Prefs) {
22811         	prefs = gadgets.Prefs();
22812         	routeName = prefs.getString('routeName');
22813         }
22814 
22815         if (routeName) {
22816             _myTab = routeName;
22817         } else {	
22818             //This will return the nav name of the currently selected iframe.This selection is similar to the existing finesse desktop.
22819             //This is not tested with the page level gadget
22820             _myTab = _commonDesktop.route.getAllRoute()[$(frameElement).closest('div[data-group-id]').attr('data-group-id')-1];
22821             if(_myTab){
22822                 _myTab = _myTab.startsWith('#/') ? _myTab.slice(2) : _myTab;
22823             }
22824         }		
22825 	}else{
22826 		if (_myTab === null){
22827 			try {
22828 				_myTab = $(frameElement).closest("div.tab-panel").attr("id").replace("panel_", "");
22829 			}catch (err) {
22830 				_logger.log("Error accessing current tab: " + err.message);
22831 				_myTab = null;
22832 			}
22833 		}	
22834 	}  
22835 	return _myTab;
22836     },
22837     
22838     /**
22839      * Callback function that is called when an activeTab message is posted to the Hub.
22840      * Notifies listener functions if this tab is the one that was just made active.
22841      * @param {String} tabId : The tabId which was just made visible.
22842      * @private
22843      */
22844     _tabTracker = function(tabId) {
22845         if (tabId === _getMyTab()) {
22846             if(!_visible) {
22847                 _visible = true;
22848                 _notifiers[finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB].notifyListeners(this);
22849             }
22850         } else {
22851             _visible = false;
22852         }
22853     },
22854     
22855     /**
22856      * Make a request to set a particular tab active. This
22857      * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
22858      * to ensure the gadget gets properly initialized.
22859      * @param {String} tabId
22860      *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
22861      * @private
22862      */
22863     _activateTab = function ( tabId ) {
22864         _logger.log("Sending request to activate tab: " + tabId);
22865         if(_hub){
22866             var data = {
22867                 type: "SetActiveTabReq",
22868                 data: { id: tabId },
22869                 invokeID: (new Date()).getTime()          
22870             };
22871             _hub.publish(_topics.REQUESTS, data);
22872         } else {
22873             throw new Error("Hub is not defined.");
22874         }
22875         
22876     },
22877 
22878     /**
22879      * Callback function that is called when a gadget view changed message is posted to the Hub.
22880      * @private
22881      */
22882     _gadgetViewChanged = function (data) {
22883         if (data) {
22884             var gadgetViewChangedEvent = new finesse.containerservices.GadgetViewChangedEvent(
22885                 data.gadgetId,
22886                 data.tabId,
22887                 data.maxAvailableHeight,
22888                 data.view);
22889 
22890             _notifiers[finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT].notifyListeners(gadgetViewChangedEvent);
22891         }
22892     },
22893 
22894     /**
22895      * Callback function that is called when a max available height changed message is posted to the Hub.
22896      * @private
22897      */
22898     _maxAvailableHeightChanged = function (data) {
22899         if (data) {
22900             var maxAvailableHeightChangedEvent = new finesse.containerservices.MaxAvailableHeightChangedEvent(
22901                 data.maxAvailableHeight);
22902 
22903             _notifiers[finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT].notifyListeners(maxAvailableHeightChangedEvent);
22904         }
22905     },
22906 
22907     /**
22908      * Callback function that is called when a workflowActionEvent message is posted to the Hub.
22909      * Notifies listener functions if the posted object can be converted to a proper WorkflowActionEvent object.
22910      * @param {String} workflowActionEvent : The workflowActionEvent that was posted to the Hub
22911      * @private
22912      */
22913     _workflowActionEventTracker = function(workflowActionEvent) {
22914         var vWorkflowActionEvent = new finesse.containerservices.WorkflowActionEvent();
22915                 
22916         if (vWorkflowActionEvent.setWorkflowActionEvent(workflowActionEvent)) {
22917             _notifiers[finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT].notifyListeners(vWorkflowActionEvent);
22918         }
22919         // else
22920         // {
22921             //?console.log("Error in ContainerServices : _workflowActionEventTracker - could not map published HUB object to WorkflowActionEvent");
22922         // }
22923 
22924     },
22925 
22926     /**
22927      * Callback function that is called when a reloadGadget event message is posted to the Hub.
22928      *
22929      * Grabs the id of the gadget we want to reload from the data and reload it!
22930      *
22931      * @param {String} topic
22932      *      which topic the event came on (unused)
22933      * @param {Object} data
22934      *      the data published with the event
22935      * @private
22936      */
22937     _masterReloader = function (topic, data) {
22938         var gadgetId = data.gadgetId;
22939         if (gadgetId) {
22940             _container.reloadGadget(gadgetId);
22941         }
22942     },
22943     
22944     _toggleGadgetExpandCollapse = function(type) {
22945    	 	var data = {type: type, options: {'gadgetId': _findMyGadgetId()}};
22946    	 	parent.postMessage(data, "*");
22947     },
22948     
22949     /**
22950      * Pulls the gadget id from the url parameters
22951      * @return {String} id of the gadget
22952      * @private
22953      */
22954     _findMyGadgetId = function () {
22955         if (gadgets && gadgets.util && gadgets.util.getUrlParameters()) {
22956             return gadgets.util.getUrlParameters().mid;
22957         }
22958     };
22959 
22960     return {
22961         /**
22962          * @class
22963          * This class provides container-level services for gadget developers, exposing container events by
22964          * calling a set of exposed functions. Gadgets can utilize the container dialogs and 
22965          * event handling (add/remove).
22966          * @example
22967          *    containerServices = finesse.containerservices.ContainerServices.init();
22968          *    containerServices.addHandler(
22969          *      finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, 
22970          *      function() {
22971          *          clientLogs.log("Gadget is now visible");  // log to Finesse logger
22972          *          // automatically adjust the height of the gadget to show the html
22973          *          gadgets.window.adjustHeight();
22974          *      });
22975          *    containerServices.makeActiveTabReq();
22976          *    
22977          * @constructs
22978          */
22979         _fakeConstuctor: function () {
22980             /* This is here so we can document init() as a method rather than as a constructor. */
22981         },
22982         
22983         /**
22984          * Initialize ContainerServices for use in gadget.
22985          * @param {Boolean} [master=false] Do not use this parameter from your gadget.
22986          * @returns ContainerServices instance.
22987          */
22988         init: function (master) {
22989             if (!_inited) {
22990                 _inited = true;
22991                 // Set shortcuts
22992                 _util = Utilities;
22993                 _authModes = _util.getAuthModes();
22994                 try {
22995                 	_commonDesktop = window.top.cd;
22996                 } catch(err) {
22997                     _logger.log("Error accessing common desktop: " + err.message);
22998                 }
22999                 
23000                 //init the hub only when it's available
23001                 if(gadgets.Hub) {
23002                     _hub = gadgets.Hub;
23003                 }
23004 
23005                 if(Topics) {
23006                     _topics = Topics;
23007                 }
23008 
23009                 if (master) {
23010                     _master = true;
23011                     _container = finesse.container.Container;
23012                     _publisher = new MasterPublisher();
23013 
23014                     // subscribe for reloading gadget events
23015                     // we only want the master ContainerServices handling these events
23016                     _hub.subscribe(_topics.RELOAD_GADGET, _topicCallback(_topics.RELOAD_GADGET));
23017                 } else {
23018                      // For SPOG like containers where parent.finesse is undefined.
23019                      if(parent.finesse){
23020                         _container = parent.finesse.container.Container;
23021                      }
23022                 }
23023             }
23024             
23025             this.makeActiveTabReq();
23026 
23027             if(finesse.modules && finesse.modules.ToastPopover){
23028                 finesse.ToastPopoverInstance = new finesse.modules.ToastPopover();
23029             }
23030 
23031             /* initialize popOverService */
23032             if(window.finesse.containerservices.PopoverService){
23033                 window.finesse.containerservices.PopoverService.init(this);   
23034             }
23035             
23036             //Return the CS object for object chaining.
23037             return this;
23038         },
23039 
23040         /**
23041          * Shows the UI Dialog with the specified parameters.
23042          * @param {Object} options
23043          *  An object containing additional options for the dialog.
23044          * @param {String} options.title
23045          *  Title to use.
23046          * @param {Function} options.close
23047          *  A function to invoke when the dialog is closed.
23048          * @param {String} options.message
23049          *  The message to display in the dialog.
23050          * @param {Boolean} options.isBlocking
23051          *  Flag indicating whether this dialog will block other dialogs from being shown (Modal).
23052          * @see finesse.containerservices.ContainerServices#hideDialog
23053          * @example
23054          * // containerServices = finesse.containerservices.ContainerServices.init();
23055          * containerServices.showDialog(
23056          * {title:'Error Occurred',message: 'Something went wrong', close: {Function}
23057          * });
23058          * 
23059          * NoteIn case of SPOG like containers, _container.showDialog will not be present. in this case fallback dialog is used
23060          */
23061         showDialog: function(options) {
23062             if (_container && _container.showDialog && _container.showDialog !== this.showDialog) {
23063                 return _container.showDialog(options);
23064             }
23065 
23066             // showDialog _container will be undefined incase of gadget hosted in spog. Hence jQuery dialog is used as a fallback modal/popup.
23067             if ( !this.fallbackDialog ) {
23068                 this.fallbackDialog = $('<div />').dialog({
23069                     autoOpen: false,
23070                     modal: true,
23071                     dialogClass: "finesse-fallback-dialog"
23072                 });
23073             }
23074 
23075             this.hideDialog();
23076             
23077             return this.fallbackDialog.dialog({
23078                 title: options.title,
23079                 buttons: options.buttons,
23080                 close: options.close || this.hideDialog,
23081             }).html(options.message).dialog("open");
23082         },
23083         
23084         /**
23085          * Hides the UI Dialog.
23086          * @see finesse.containerservices.ContainerServices#showDialog
23087          * @example	 
23088          * // containerServices = finesse.containerservices.ContainerServices.init();
23089          * containerServices.hideDialog();
23090          */
23091         hideDialog: function() {
23092             if (_container && _container.hideDialog && _container.hideDialog !== this.hideDialog) {
23093                 return _container.hideDialog();
23094             }
23095 
23096             if ( this.fallbackDialog ) {
23097                 return this.fallbackDialog.dialog('close');
23098             }
23099         },
23100         
23101         /**
23102          * Shows the Certificate Banner with message "Gadget certificates are yet to be accepted."
23103          * @param  {Function} [callback]
23104          *  Callback is invoked when user closes the banner manually.
23105          * @returns {String} id
23106          * 	Note : Gadgets calling showCertificateBanner can ignore the return value as it is not required.
23107          * @see finesse.containerservices.ContainerServices#hideCertificateBanner
23108          * @example	
23109          * // For Gadgets 
23110          * // containerServices = finesse.containerservices.ContainerServices.init();
23111          * 	  containerServices.showCertificateBanner(callback);
23112          * 
23113          * // For non gadget Client , id is required to hide the certificate banner
23114          * // which is returned when showCertificateBanner is called
23115          * 	var id = containerServices.showCertificateBanner(callback);
23116          * 
23117          */
23118         showCertificateBanner: function(callback) {
23119             if ((_container.showCertificateBanner !== undefined) && (_container.showCertificateBanner !== this.showCertificateBanner)) {
23120             		var options = {};
23121             		/**
23122             		 * If getMyGadgetId is undefined , i.e. for components, a unique id will be returned,
23123             		 * which should be sent while calling hideCertificateBanner
23124             		 */ 
23125             		options.id = window.finesse.containerservices.ContainerServices.getMyGadgetId();
23126             		options.callback = callback;
23127                 return _container.showCertificateBanner(options);
23128             }
23129         },
23130         
23131         /**
23132          *  Request to hide the Certificate Banner.
23133          *  The banner will be hidden when all gadgets which invoked showCertificateBanner
23134          *  have made a corresponding invocation to hideCertificateBanner, or when the user closes the banner manually.
23135          *  @param {String} [id]
23136          *  Note : Gadgets do not need to send the id parameter.
23137          *  The id is used by the desktop in scenarios where the banner has to be invoked by a non-gadget client.
23138          *@example	 
23139          * // For Gadgets
23140          * // containerServices = finesse.containerservices.ContainerServices.init();
23141          * containerServices.hideCertificateBanner();
23142          * 
23143          * // For non gadget Client
23144          * containerServices.hideCertificateBanner(id);
23145          * 
23146          */
23147         hideCertificateBanner: function(id) {
23148         		if(id === undefined && window.finesse.containerservices.ContainerServices.getMyGadgetId() === undefined) {
23149         			throw new Error('ID returned when showCertificateBanner was called need to be sent as params');
23150         		}
23151             if ((_container.hideCertificateBanner !== undefined) && (_container.hideCertificateBanner !== this.hideCertificateBanner)) {
23152                 return _container.hideCertificateBanner(id || window.finesse.containerservices.ContainerServices.getMyGadgetId());
23153             }
23154         },
23155 
23156         /**
23157          *  Reloads the current gadget. 
23158          *  For use from within a gadget only.
23159          * @example	 
23160          * // containerServices = finesse.containerservices.ContainerServices.init();
23161          * containerServices.reloadMyGadget();
23162          *  
23163          */
23164         reloadMyGadget: function () {
23165             var topic, gadgetId, data;
23166 
23167             if (!_master) {
23168                 // first unsubscribe this gadget from all topics on the hub
23169                 for (topic in _notifiers) {
23170                     if (_notifiers.hasOwnProperty(topic)) {
23171                         _unsubscribe(topic);
23172                         delete _notifiers[topic];
23173                     }
23174                 }
23175 
23176                 // send an asynch request to the hub to tell the master container
23177                 // services that we want to refresh this gadget
23178                 gadgetId = _findMyGadgetId();
23179                 data = {
23180                     type: "ReloadGadgetReq",
23181                     data: {gadgetId: gadgetId},
23182                     invokeID: (new Date()).getTime()          
23183                 };
23184                 _hub.publish(_topics.REQUESTS, data);
23185             }            
23186         },
23187 
23188         /**
23189          * Updates the url for this gadget and then reload it.
23190          * 
23191          * This allows the gadget to be reloaded from a different location
23192          * than what is uploaded to the current server. For example, this
23193          * would be useful for 3rd party gadgets to implement their own failover
23194          * mechanisms.
23195          *
23196          * For use from within a gadget only.
23197          *
23198          * @param {String} url
23199          *      url from which to reload gadget
23200          *  @example	 
23201          * // containerServices = finesse.containerservices.ContainerServices.init();
23202          * containerServices.reloadMyGadgetFromUrl(url);    
23203          */
23204         reloadMyGadgetFromUrl: function (url) {
23205             if (!_master) {
23206                 var gadgetId = _findMyGadgetId();
23207 
23208                 // update the url in the container
23209                 _container.modifyGadgetUrl(gadgetId, url);
23210 
23211                 // reload it
23212                 this.reloadMyGadget();
23213             }
23214         },
23215         
23216         /**
23217          * Adds a handler for one of the supported topics provided by ContainerServices.  The callbacks provided
23218          * will be invoked when that topic is notified.  
23219          * @param {String} topic
23220          *  The Hub topic to which we are listening.
23221          * @param {Function} callback
23222          *  The callback function to invoke.
23223          * @see finesse.containerservices.ContainerServices.Topics
23224          * @see finesse.containerservices.ContainerServices#removeHandler
23225          * @example
23226          * containerServices.addHandler(
23227          *      finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, 
23228          *      function() {
23229          *          clientLogs.log("Gadget is now visible");  // log to Finesse logger
23230          *          // automatically adjust the height of the gadget to show the html
23231          *          gadgets.window.adjustHeight();
23232          *      });
23233          */
23234         addHandler: function (topic, callback) {
23235             _isInited();
23236             var notifier = null;
23237             
23238             try {    
23239                 // For backwards compatibility...
23240                 if (topic === "tabVisible") {
23241                     if (window.console && typeof window.console.log === "function") {
23242                         window.console.log("WARNING - Using tabVisible as topic.  This is deprecated.  Use finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB now!");
23243                     }
23244                     
23245                     topic = finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB;
23246                 }
23247                 
23248                 // Add the callback to the notifier.
23249                 _util.validateHandler(callback);
23250             
23251                 notifier = _getNotifierReference(topic);
23252             
23253                 notifier.addListener(callback);
23254             
23255                 // Subscribe to the topic. _subscribe ensures that a topic is only subscribed to once,
23256                 // so attempt to subscribe each time a handler is added. This ensures that a topic is subscribed
23257                 // to only when necessary.
23258                 _subscribe(topic, _topicCallback(topic));
23259             
23260             } catch (err) {
23261                 throw new Error("addHandler(): " + err);
23262             }
23263         }, 
23264         
23265         /**
23266          * Removes a previously-added handler for one of the supported topics.
23267          * @param {String} topic
23268          *  The Hub topic from which we are removing the callback.
23269          * @param {Function} callback
23270          *  The name of the callback function to remove.
23271          * @see finesse.containerservices.ContainerServices.Topics
23272          * @see finesse.containerservices.ContainerServices#addHandler
23273          * @example
23274          * containerServices.removeHandler(
23275          *      finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, 
23276          *      function() {
23277          *          clientLogs.log("Gadget is now visible");  // log to Finesse logger
23278          *          // automatically adjust the height of the gadget to show the html
23279          *          gadgets.window.adjustHeight();
23280          *      });
23281          */
23282         removeHandler: function(topic, callback) {
23283             var notifier = null;
23284             
23285             try {
23286                 _util.validateHandler(callback);
23287     
23288                 notifier = _getNotifierReference(topic);
23289     
23290                 notifier.removeListener(callback);
23291             } catch (err) {
23292                 throw new Error("removeHandler(): " + err);
23293             }
23294         },
23295         
23296         /**
23297          * Wrapper API for publishing data on the Openajax hub
23298          * @param {String} topic
23299          *  The Hub topic to which we are publishing.
23300          * @param {Object} data
23301          *  The data to be published on the hub.
23302          *  @example
23303          *  containerServices.publish('TOPIC_NAME',data);
23304          *  @see
23305          *  finesse.containerservices.ContainerServices.Topics
23306          */
23307         publish : function(topic , data){
23308             if(_hub){
23309                 _hub.publish(topic, data);
23310             } else {
23311                 throw new Error("Hub is not defined.");
23312             }
23313         },
23314 
23315         /**
23316          * Returns the visibility of current gadget.  Note that this 
23317          * will not be set until after the initialization of the gadget.
23318          * @return {Boolean} The visibility of current gadget.
23319          *  @example
23320          *  containerServices.tabVisible();
23321          */
23322         tabVisible: function(){
23323             return _visible;
23324         },
23325         
23326         /**
23327          * Make a request to check the current tab.  The 
23328          * activeTab event will be invoked if on the active tab. The 
23329          * handler for activeTab can be added using {@link finesse.containerservices.ContainerServices#addHandler}
23330          * with TOPIC 'ACTIVE_TAB'
23331          * Called by default when ContainerServices is initialized,
23332          * i.e. finesse.containerservices.ContainerServices.init();
23333          *  @example
23334          *  containerServices.makeActiveTabReq();
23335          */
23336         makeActiveTabReq : function () {
23337             if(_hub){
23338                 var data = {
23339                     type: "ActiveTabReq",
23340                     data: {},
23341                     invokeID: (new Date()).getTime()          
23342                 };
23343                 _hub.publish(_topics.REQUESTS, data);
23344             } else {
23345                 throw new Error("Hub is not defined.");
23346             }
23347             
23348         },
23349 
23350         /**
23351          * Make a request to set a particular tab active.
23352          * @param {String} tabId
23353          *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
23354          * @example
23355          *  containerServices.activateTab(tabId);
23356          */
23357         activateTab : function (tabId) {
23358             _activateTab(tabId);
23359         },
23360         
23361         /**
23362          * Make a request to set this container's tab active. 
23363          * This will sent request to activate the tab in which 
23364          * gadget is present.
23365          * @example
23366          *  containerServices.activateMyTab();
23367          */
23368         activateMyTab : function () {
23369             _activateTab( _getMyTab() );
23370         },
23371         
23372         /**
23373          * Get the tabId of my container/gadget.
23374          * @returns {String} tabid : The tabid of this container/gadget.
23375          * 
23376          * @example
23377          *  containerServices.getMyTabId();
23378          */
23379         getMyTabId : function () {
23380 		return _getMyTab();
23381 	},
23382 
23383         /**
23384          * Gets the id of the gadget.
23385          * @returns {number} the id of the gadget
23386          * @example
23387          *  containerServices.getMyGadgetId();
23388          */
23389         getMyGadgetId : function () {
23390             return _findMyGadgetId();
23391         },
23392         
23393         /**
23394          * expandMyGadget method allows the gadget to expand itself.
23395          * 
23396          * To enable collapsible feature for any gadget add below gadget ModulePrefs inside the gadget code.
23397          * 
23398          * <ModulePrefs title="Sample Gadget" description="Sample Gadget">
23399          * 		<Optional feature="collapsible" />
23400          * </ModulePrefs>
23401          */
23402         expandMyGadget: function(){
23403         	_toggleGadgetExpandCollapse('EXPAND_GADGET');
23404         },
23405         
23406         /**
23407          * collapseMyGadget method allows the gadget to collapse itself. In collapse state, only gadget header will be displayed.
23408          * 
23409          * To enable collapsible feature for any gadget add below gadget ModulePrefs inside the gadget code.
23410          * 
23411          * <ModulePrefs title="Sample Gadget" description="Sample Gadget">
23412          * 		<Optional feature="collapsible" />
23413          * </ModulePrefs>
23414          */
23415         collapseMyGadget: function(){
23416         	_toggleGadgetExpandCollapse('COLLAPSE_GADGET');
23417         },
23418 
23419         //BEGIN TEST CODE//
23420         /**
23421          * Test code added to expose private functions that are used by unit test
23422          * framework. This section of code is removed during the build process
23423          * before packaging production code. The [begin|end]TestSection are used
23424          * by the build to identify the section to strip.
23425          * @ignore
23426          */
23427         beginTestSection : 0,
23428 
23429         /**
23430          * @ignore
23431          */
23432         getTestObject: function () {
23433             //Load mock dependencies.
23434             var _mock = new MockControl();
23435             _util = _mock.createMock(Utilities);
23436             _hub = _mock.createMock(gadgets.Hub);
23437             _inited = true;
23438             return {
23439                 //Expose mock dependencies
23440                 mock: _mock,
23441                 hub: _hub,
23442                 util: _util,
23443                 addHandler: this.addHandler,
23444                 removeHandler: this.removeHandler
23445             };
23446         },
23447 
23448         /**
23449          * @ignore
23450          */
23451        endTestSection: 0
23452         //END TEST CODE//
23453     };
23454     }());
23455     
23456     ContainerServices.Topics = /** @lends finesse.containerservices.ContainerServices.Topics.prototype */ {
23457         /**
23458          * Topic for subscribing to be notified when the active tab changes.
23459          * The provided callback will be invoked when the tab containing the gadget 
23460          * subscribed to the topic becomes active. To ensure code is called
23461          * when the gadget is already on the active tab use the 
23462          * {@link finesse.containerservices.ContainerServices#makeActiveTabReq}
23463          * method.
23464          */
23465         ACTIVE_TAB: "finesse.containerservices.activeTab",
23466 
23467         /**
23468          * Topic for WorkflowAction events traffic.
23469          * The provided callback will be invoked when a WorkflowAction needs
23470          * to be handled.  The callback will be passed a {@link finesse.containerservices.WorkflowActionEvent}
23471          * that can be used to interrogate the WorkflowAction and determine to use or not.
23472          */
23473         WORKFLOW_ACTION_EVENT: "finesse.containerservices.workflowActionEvent",
23474         
23475         /**
23476          * Topic for Timer Tick event.
23477          * The provided callback will be invoked when this event is fired.
23478          * The callback will be passed a {@link finesse.containerservices.TimerTickEvent}.
23479          */
23480         TIMER_TICK_EVENT : "finesse.containerservices.timerTickEvent",
23481 
23482         /**
23483          * Topic for Non Voice Gadgets to communicate with Finesse Container.
23484          * Finesse container will handle this event
23485          */
23486         FINEXT_NON_VOICE_GADGET_EVENT : "finext.nv",
23487 
23488         /**
23489          * Topic for listening to the active call event.
23490          * The provided callback will be invoked when a agent will have a call
23491          * or when the call is ended.
23492          * The callback will be passed ActiveCallStatusEvent
23493          * Example, when the call is active ActiveCallStatusEvent {status: true,type: "info"}
23494          * and when the call is ended or inactive  ActiveCallStatusEvent {status: false, type: "info"}
23495          */
23496         ACTIVE_CALL_STATUS_EVENT : "finesse.containerservices.activeCallStatusEvent",
23497 
23498         /**
23499          * Topic for Gadgets to communicate with Finesse Popover Container.
23500          * The provided callback will be invoked when popover event is fired.
23501          */
23502         FINEXT_POPOVER_EVENT : "finext.popover",
23503 
23504         /**
23505          * Topic for Reload Gadget events traffic.
23506          * Only the master ContainerServices instance will handle this event.
23507          */
23508         RELOAD_GADGET_EVENT: "finesse.containerservices.reloadGadget",
23509         
23510         /**
23511          * Topic for listening to gadget view changed events.
23512          * The provided callback will be invoked when a gadget changes view.
23513          * The callback will be passed a {@link finesse.containerservices.GadgetViewChangedEvent}.
23514          */
23515         GADGET_VIEW_CHANGED_EVENT: "finesse.containerservices.gadgetViewChangedEvent",
23516 
23517         /**
23518          * Topic for listening to max available height changed events.
23519          * The provided callback will be invoked when the maximum height available to a maximized gadget changes.
23520          * This event is only meant for maximized gadgets and will not be published unless a maximized gadget exists.
23521          * The callback will be passed a {@link finesse.containerservices.MaxAvailableHeightChangedEvent}.
23522          */
23523         MAX_AVAILABLE_HEIGHT_CHANGED_EVENT: "finesse.containerservices.maxAvailableHeightChangedEvent",
23524         
23525         /**
23526          * @class This is the set of Topics used for subscribing for events from ContainerServices.
23527          * Use {@link finesse.containerservices.ContainerServices#addHandler} to subscribe to the topic.
23528          * 
23529          * @constructs
23530          */
23531         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
23532     };
23533     
23534     window.finesse = window.finesse || {};
23535     window.finesse.containerservices = window.finesse.containerservices || {};
23536     window.finesse.containerservices.ContainerServices = ContainerServices;
23537     
23538     return ContainerServices;
23539  });
23540 
23541 /**
23542  * FinesseToaster is a utility class to show toaster notification in Finesse.
23543  * FinesseToaster leverages HTML5 Notification API to display Toaster
23544  * Notification.
23545  * 
23546  */
23547 
23548 define('containerservices/FinesseToaster',[],function() {
23549 
23550 	var FinesseToaster = (function() {
23551 		/** @lends finesse.containerservices.FinesseToaster.prototype */
23552 
23553 		var
23554 
23555 		/** How long the toaster will be displayed by default. Default timeout is 8 seconds */
23556 		AUTO_CLOSE_TIME = 8000,
23557 
23558 		/** PERMISSION_GRANTED constant for granted string */
23559 		PERMISSION_GRANTED = 'granted',
23560 
23561 		/** PERMISSION_DEFAULT constant for default string */
23562 		PERMISSION_DEFAULT = 'default',
23563 
23564 		/** PERMISSION_DENIED constant for denied string */
23565 		PERMISSION_DENIED = 'denied',
23566 
23567 		/** ICON_PATH constant for holding path icon images */
23568 		ICON_PATH = '/desktop/theme/finesse/images/modules/',
23569 
23570 		/**
23571 		 * Shortcut reference to finesse.cslogger.ClientLogger singleton This
23572 		 * will be set by init(), it should already be initialized by
23573 		 * PageServices
23574 		 *
23575 		 * @private
23576 		 */
23577 		_logger,
23578 		
23579 		/**
23580 		 * Boolean variable to determine if finesse toaster is enabled 
23581 		 * @private
23582 		 */
23583 		_isToasterDisabled = false,
23584 		
23585 		/**
23586 		 * Boolean variable to determine if finesse toaster is initialized 
23587 		 * @private
23588 		 */
23589 		_isToasterInitialized,
23590 		
23591 		/**
23592 		 * this function check of provided parameter is a javascript function.
23593 		 * 
23594 		 * @private
23595 		 */
23596 		isFunction = function(param) {
23597 			if (typeof param === "function") {
23598 				return true;
23599 			}
23600 			return false;
23601 		},
23602 
23603 		/**
23604 		 * _createNotification creates Notification instance
23605 		 *
23606 		 * @param {String}
23607 		 *            title title string should be displayed in the Toaster
23608 		 * @param {Object}
23609 		 *            options JSON object for notification options.
23610 		 */
23611 		_createNotification = function(title, options) {
23612 			var notification = new window.Notification(title, options);
23613 			return notification;
23614 		},
23615 
23616 		/**
23617 		 * _setAutoClose set the auto close time for toaster, it checks if
23618 		 * client code passed custom time out for the toaster, otherwise it set
23619 		 * the AUTO_CLOSE_TIME
23620 		 *
23621 		 * @param {Object}
23622 		 *            notification window.Notification that creates Html5
23623 		 *            Notification.
23624 		 * @param {String}
23625 		 *            autoClose autoClose time of the Toaster
23626 		 * @return toasterTimeout Toaster Timeout
23627 		 */
23628 		_setAutoClose = function(notification, autoClose) {
23629 
23630 			// check if custom close time passed other wise set
23631 			// DEFAULT_AUTO_CLOSE
23632 			var autoCloseTime = (autoClose && !isNaN(autoClose)) ? autoClose
23633 					: AUTO_CLOSE_TIME,
23634 			// set the time out for notification toaster
23635 			toasterTimer = setTimeout(function() {
23636 				notification.close();
23637 			}, autoCloseTime);
23638 
23639 			return toasterTimer;
23640 
23641 		},
23642 
23643 		/** This method will request permission to display Toaster. */
23644 		_requestPermission = function() {
23645 			// If they are not denied (i.e. default)
23646 			if (window.Notification
23647 					&& window.Notification.permission !== PERMISSION_DENIED) {
23648 				// Request permission
23649 				window.Notification
23650 						.requestPermission(function(status) {
23651 
23652 							// Change based on user's decision
23653 							if (window.Notification.permission !== status) {
23654 								window.Notification.permission = status;
23655 							}
23656 							_logger
23657 									.log("FinesseToaster.requestPermission(): request permission status "
23658 											+ status);
23659 
23660 						});
23661 
23662 			} else {
23663 				_logger
23664 						.log("FinesseToaster.requestPermission(): Notification not supported or permission denied.");
23665 			}
23666 
23667 		},
23668 
23669 		/**
23670 		 * This method will add onclick and onerror listener to Notification.
23671 		 * on click of toaster the gadget which originally had focus may loose
23672 		 * the focus. To get the back the focus on any element inside the gadget
23673 		 * use the on click callback handler.
23674 		 * 
23675 		 * @param {Object}
23676 		 *            notification window.Notification that creates Html5
23677 		 *            Notification.
23678 		 * @param {Object}
23679 		 *            options JSON object for notification options.
23680 		 */
23681 		_addToasterListeners = function(notification, options, toasterTimer) {
23682 			// this is onlcik handler of toaster. this handler will be invoked
23683 			// on click of toaster
23684 			notification.onclick = function() {
23685 				// in case of manually closed toaster, stop the notification
23686 				// auto close method to be invoked
23687 				clearTimeout(toasterTimer);
23688 				// This will maximize/activate chrome browser on click of
23689 				// toaster. this handling required only in case of Chrome
23690 				if (window.chrome) {
23691 					parent.focus();
23692 				}
23693 
23694 				if (options && options.onclick) {
23695 					if (isFunction(options.onclick)) {
23696 						options.onclick();
23697 					} else {
23698 						throw new Error("onclick callback must be a function");
23699 					}
23700 				}
23701 
23702 				//close toaster upon click
23703 				this.close();
23704 
23705 			};
23706 
23707 			// this is onerror handler of toaster, if there is any error while
23708 			// loading toaster this hadnler will be invoked
23709 			notification.onerror = function() {
23710 				if (options && options.onerror) {
23711 					if (isFunction(options.onerror)) {
23712 						options.onerror();
23713 					} else {
23714 						throw new Error("onerror callback must be a function");
23715 					}
23716 				}
23717 			};
23718 		};
23719 		
23720 		return {
23721 
23722 			/**
23723 			 * @class
23724 			 *  FinesseToaster is a utility class to show toaster
23725 			 *        notification in Finesse. FinesseToaster leverages <a
23726 			 *        href="https://www.w3.org/TR/notifications/">HTML5
23727 			 *        Notification</a> API to display Toaster Notification. 
23728 			 *       <p> <a
23729 			 *        href="https://developer.mozilla.org/en/docs/Web/API/notification#Browser_compatibility">For
23730 			 *        HTML5 Notification API and browser compatibility, please click
23731 			 *        here.</a></p>
23732 			 * 
23733 			 * @constructs
23734 			 */
23735 			_fakeConstuctor : function() {
23736 
23737 			},
23738 			/**
23739 			 * TOASTER_DEFAULT_ICONS constants has list of predefined icons (e.g INCOMING_CALL_ICON).
23740 			 * <p><b>Constant list</b></p>
23741 			 * <ul>
23742 			 * <li>TOASTER_DEFAULT_ICONS.INCOMING_CALL_ICON</li>
23743 			 * <li>TOASTER_DEFAULT_ICONS.INCOMING_CHAT_ICON</li>
23744 			 * <li>TOASTER_DEFAULT_ICONS.INCOMING_TEAM_MESSAGE</li>
23745 			 * </ul>
23746 			 */
23747 			TOASTER_DEFAULT_ICONS : {
23748 				INCOMING_CALL_ICON : ICON_PATH + "incoming_call.png",
23749 				INCOMING_CHAT_ICON : ICON_PATH + "incoming_chat.png",
23750 				INCOMING_TEAM_MESSAGE : ICON_PATH + "incoming_team_message.png"
23751 			},
23752 
23753 			/**
23754 			 * <b>showToaster </b>: shows Toaster Notification.
23755 			 *
23756 			 * @param {String}
23757 			 *            <b>title</b> : title string should be displayed in the Toaster
23758 			 * @param {Object}
23759 			 *            options is JSON object for notification options. 
23760 			 *            <ul>
23761 			 *            <li><b>options</b> = { </li>
23762 			 *            <li><b>body</b> : The body string of the notification as
23763 			 *            specified in the options parameter of the constructor.</li>
23764 			 *            <li><b>icon</b>: The URL of the image used as an icon of the
23765 			 *            notification as specified in the options parameter of
23766 			 *            the constructor.</li>
23767 			 *            <li><b>autoClose</b> : custom auto close time of the toaster</li>
23768 			 *            <li><b>showWhenVisible</b> : 'true' toaster shows up even when page is 
23769 			 *            visible,'false' toaster  shows up only when page is invisible </li>
23770 			 *           <li> }</li>
23771 			 *            </ul>
23772 			 *            
23773 			 * @example
23774 			 * finesse.containerservices.FinesseToaster.showToaster(
23775 			 * 'Incoming Alert',{body:'There is new message'}
23776 			 * );
23777 			 *
23778 			 */
23779 			showToaster : function(title, options) {
23780 				
23781 				if(!_isToasterInitialized){
23782 					throw new Error("FinesseToaster.showToaster() : Finesse toaster is not initialized");
23783 				}
23784 				
23785 				if(_isToasterDisabled){
23786 					_logger.log("FinesseToaster.showToaster() : FinesseToaster is disabled");
23787 					return;
23788 				}
23789 
23790 				var notification, toasterTimer;
23791 
23792 				// If notifications are granted show the notification
23793 				if (window.Notification
23794 						&& window.Notification.permission === PERMISSION_GRANTED) {
23795 
23796 					// document.hasFocus() used over document.hidden to keep the consistent behavior across mozilla/chrome
23797 					if (document.hasFocus() === false
23798 							|| options.showWhenVisible) {
23799 						if (_logger && AUTO_CLOSE_TIME > -1) {
23800 							notification = _createNotification(title, options);
23801 
23802 							// set the auto close time out of the toaster
23803 							toasterTimer = _setAutoClose(notification,
23804 									options.autoClose);
23805 
23806 							// and Toaster Event listeners. eg. onclick , onerror.
23807 							_addToasterListeners(notification, options,
23808 									toasterTimer);
23809 						} 
23810 					} else {
23811 						_logger
23812 								.log("FinesseToaster supressed : Page is visible and  FineeseToaster.options.showWhenVisible is false");
23813 					}
23814 
23815 				}
23816 
23817 				return notification;
23818 			},
23819 
23820 			/**
23821 			 * initialize FininseToaster and inject dependencies. this method
23822 			 * will also request permission in browser from user to display
23823 			 * Toaster Notification.
23824 			 *
23825 			 * @param {Object}
23826 			 *			finesse.container.Config or finesse.gadget.Config object based on where it is getting initialized from.
23827 			 *            
23828 			 * @param {Object}
23829 			 *            finesse.cslogger.ClientLogger
23830 			 * @return finesse.containerservices.FinesseToaster
23831 			 */
23832 			init : function(config, logger) {
23833 				
23834 				_isToasterInitialized = true;
23835 				
23836 				// This is for injecting mocked logger.
23837 				if (logger) {
23838 					_logger = logger;
23839 				} else {
23840 					_logger = finesse.cslogger.ClientLogger;
23841 				}
23842 				
23843 				//set default toaster notification timeout
23844 				if (config && config.toasterNotificationTimeout !== undefined) {
23845 					AUTO_CLOSE_TIME = Number(config.toasterNotificationTimeout) * 1000;
23846 					
23847 					if(AUTO_CLOSE_TIME === 0){
23848 						//Finesse toaster has been disabled
23849 						_isToasterDisabled = true;
23850 					}
23851 				} 
23852 
23853 				// Request permission
23854 				_requestPermission();
23855 				return finesse.containerservices.FinesseToaster;
23856 			}
23857 		};
23858 
23859 	}());
23860 
23861 	window.finesse = window.finesse || {};
23862 	window.finesse.containerservices = window.finesse.containerservices || {};
23863 	window.finesse.containerservices.FinesseToaster = FinesseToaster;
23864 
23865 	return FinesseToaster;
23866 });
23867 
23868 /**
23869  * This "interface" is just a way to easily jsdoc the Object callback handlers.
23870  *
23871  * @requires finesse.clientservices.ClientServices
23872  * @requires Class
23873  */
23874 /** @private */
23875 define('interfaces/RestObjectHandlers',[
23876     "FinesseBase",
23877      "utilities/Utilities",
23878      "restservices/Notifier",
23879      "clientservices/ClientServices",
23880      "clientservices/Topics"
23881 ],
23882 function () {
23883 
23884     var RestObjectHandlers = ( function () { /** @lends finesse.interfaces.RestObjectHandlers.prototype */
23885         
23886         return {
23887 
23888             /**
23889              * @class
23890              * This "interface" defines REST Object callback handlers, passed as an argument to
23891              * Object getter methods in cases where the Object is going to be created.
23892              * 
23893              * @param {Object} [handlers]
23894              *     An object containing callback handlers for instantiation and runtime
23895              *     Callback to invoke upon successful instantiation, passes in REST object.
23896              * @param {Function} [handlers.onLoad(this)]
23897              *     Callback to invoke upon loading the data for the first time.
23898              * @param {Function} [handlers.onChange(this)]
23899              *     Callback to invoke upon successful update object (PUT)
23900              * @param {Function} [handlers.onAdd(this)]
23901              *     Callback to invoke upon successful update to add object (POST)
23902              * @param {Function} [handlers.onDelete(this)]
23903              *     Callback to invoke upon successful update to delete object (DELETE)
23904              * @param {Function} [handlers.onError(rsp)]
23905              *     Callback to invoke on update error (refresh or event)
23906              *     as passed by finesse.restservices.RestBase.restRequest()<br>
23907              *     {<br>
23908              *         status: {Number} The HTTP status code returned<br>
23909              *         content: {String} Raw string of response<br>
23910              *         object: {Object} Parsed object of response<br>
23911              *         error: {Object} Wrapped exception that was caught<br>
23912              *         error.errorType: {String} Type of error that was caught<br>
23913              *         error.errorMessage: {String} Message associated with error<br>
23914              *     }<br>
23915              *     <br>
23916              * Note that RestCollections have two additional callback handlers:<br>
23917              * <br>
23918              * @param {Function} [handlers.onCollectionAdd(this)]: when an object is added to this collection
23919              * @param {Function} [handlers.onCollectionDelete(this)]: when an object is removed from this collection
23920 
23921              * @constructs
23922              */
23923             _fakeConstuctor: function () {
23924                 /* This is here to enable jsdoc to document this as a class. */
23925             }
23926         };
23927     }());
23928 
23929 window.finesse = window.finesse || {};
23930 window.finesse.interfaces = window.finesse.interfaces || {};
23931 window.finesse.interfaces.RestObjectHandlers = RestObjectHandlers;
23932 
23933 return RestObjectHandlers;
23934 
23935 });
23936 
23937 
23938 /**
23939  * This "interface" is just a way to easily jsdoc the REST request handlers.
23940  *
23941  * @requires finesse.clientservices.ClientServices
23942  * @requires Class
23943  */
23944 /** @private */
23945 define('interfaces/RequestHandlers',[
23946     "FinesseBase",
23947      "utilities/Utilities",
23948      "restservices/Notifier",
23949      "clientservices/ClientServices",
23950      "clientservices/Topics"
23951 ],
23952 function () {
23953 
23954     var RequestHandlers = ( function () { /** @lends finesse.interfaces.RequestHandlers.prototype */
23955         
23956         return {
23957 
23958             /**
23959              * @class
23960              * This "interface" defines REST Object callback handlers, passed as an argument to
23961              * Object getter methods in cases where the Object is going to be created.
23962              * 
23963              * @param {Object} handlers
23964              *     An object containing the following (optional) handlers for the request:<ul>
23965              *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
23966              *         response object as its only parameter:<ul>
23967              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
23968              *             <li><b>content:</b> {String} Raw string of response</li>
23969              *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
23970              *         <li><b>error(rsp):</b> An error callback function for an unsuccessful request to be invoked with the
23971              *         error response object as its only parameter:<ul>
23972              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
23973              *             <li><b>content:</b> {String} Raw string of response</li>
23974              *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
23975              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
23976              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
23977              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
23978              *             </ul></li>
23979              *         </ul>
23980 
23981              * @constructs 
23982              */
23983             _fakeConstuctor: function () {
23984                 /* This is here to enable jsdoc to document this as a class. */
23985             }
23986         };
23987     }());
23988 
23989 window.finesse = window.finesse || {};
23990 window.finesse.interfaces = window.finesse.interfaces || {};
23991 window.finesse.interfaces.RequestHandlers = RequestHandlers;
23992 
23993 finesse = finesse || {};
23994 /** @namespace These interfaces are just a convenience for documenting common parameter structures. */
23995 finesse.interfaces = finesse.interfaces || {};
23996 
23997 return RequestHandlers;
23998 
23999 });
24000 
24001 
24002 
24003 define('gadget/Config',[
24004 	"utilities/Utilities"
24005 ], function (Utilities) {  
24006 	var Config = (function () { /** @lends finesse.gadget.Config.prototype */
24007 		
24008 		if (gadgets && gadgets.Prefs) {
24009 		
24010 			var _prefs = new gadgets.Prefs();
24011 		
24012 			return {
24013 				/**
24014 				 * The base64 encoded "id:password" string used for authentication.
24015 				 * In case of SPOG container, the credentials will not be there in browser session storage so it will be taken from the gadget prefs.
24016 				 *
24017 				 * Since 12.5 (CSCvs38506): SPOG will be using 'credentials' as user pref. Keeping 'authorization' for backward compatibility
24018 				 */
24019 				authorization: Utilities.getUserAuthString() || _prefs.getString("credentials") || _prefs.getString("authorization"),
24020 				
24021 				/**
24022 				 * The  auth token string used for authentication in SSO deployments.
24023 				 */
24024 				authToken: Utilities.getToken(),
24025 				
24026 				/**
24027 				 * The country code of the client (derived from locale).
24028 				 */
24029 				country: _prefs.getString("country"),
24030 				
24031 				/**
24032 				 * The language code of the client (derived from locale).
24033 				 */
24034 				language: _prefs.getString("language"),
24035 				
24036 				/**
24037 				 * The locale of the client.
24038 				 */
24039 				locale: _prefs.getString("locale"),
24040 				
24041 				/**
24042 				 * The Finesse server IP/host as reachable from the browser.
24043 				 */
24044 				host: _prefs.getString("host"),
24045 				
24046 				/**
24047 				 * The Finesse server host's port reachable from the browser.
24048 				 */
24049 				hostPort: _prefs.getString("hostPort"),
24050 				
24051 				/**
24052 				 * The extension of the user.
24053 				 */
24054 				extension: _prefs.getString("extension"),
24055 				
24056 				/**
24057 				 * One of the work modes found in {@link finesse.restservices.User.WorkMode}, or something false (undefined) for a normal login.
24058 				 */
24059 				mobileAgentMode: _prefs.getString("mobileAgentMode"),
24060 				
24061 				/**
24062 				 * The dial number to use for mobile agent, or something false (undefined) for a normal login.
24063 				 */
24064 				mobileAgentDialNumber: _prefs.getString("mobileAgentDialNumber"),
24065 				
24066 				/**
24067 				 * The domain of the XMPP server.
24068 				 */
24069 				xmppDomain: _prefs.getString("xmppDomain"),
24070 				
24071 				/**
24072 				 * The pub sub domain where the pub sub service is running.
24073 				 */
24074 				pubsubDomain: _prefs.getString("pubsubDomain"),
24075 				
24076 				/**
24077 				 * The Finesse API IP/host as reachable from the gadget container.
24078 				 */
24079 				restHost: _prefs.getString("restHost"),
24080 				
24081 				/**
24082 				 * The type of HTTP protocol (http or https).
24083 				 */
24084 				scheme: _prefs.getString("scheme"),
24085 				
24086 				/**
24087 				 * The localhost fully qualified domain name.
24088 				 */
24089 				localhostFQDN: _prefs.getString("localhostFQDN"),
24090 				
24091 				/**
24092 				 * The localhost port.
24093 				 */
24094 				localhostPort: _prefs.getString("localhostPort"),
24095 				
24096 				/**
24097 				 * The id of the team the user belongs to.
24098 				 */
24099 				teamId: _prefs.getString("teamId"),
24100 				
24101 				/**
24102 				 * The name of the team the user belongs to.
24103 				 */
24104 				teamName: _prefs.getString("teamName"),
24105 				
24106 				/**
24107 				 * The drift time between the client and the server in milliseconds.
24108 				 */
24109 				clientDriftInMillis: _prefs.getInt("clientDriftInMillis"),
24110 				
24111 				/**
24112 				 * The client compatibility mode configuration (true if it is or false otherwise).
24113 				 */
24114 				compatibilityMode: _prefs.getString("compatibilityMode"),
24115 				
24116 				/**
24117 				 * The peripheral Id that Finesse is connected to.
24118 				 */
24119 				peripheralId: _prefs.getString("peripheralId"),
24120 				
24121 				/**
24122 				 * The auth mode of the finesse deployment.
24123 				 */
24124 				systemAuthMode: _prefs.getString("systemAuthMode"),
24125 				
24126 				
24127 				/**
24128 				 * The time for which fineese toaster stay on the browser.
24129 				 */
24130 				toasterNotificationTimeout: _prefs.getString("toasterNotificationTimeout"),
24131 				
24132 				navItemRoute: _prefs.getString("navItemRoute"),
24133 				
24134 				speechRecognitionHighlights: _prefs.getString("speechRecognitionHighlights"),
24135 				/**
24136 				* @class
24137 				* The Config object for gadgets within the Finesse desktop container which
24138 				* contains configuration data provided by the container page.
24139 				* @constructs
24140 				*/
24141 				_fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
24142 				
24143 			};
24144 		} else {
24145 			return {};
24146 		}
24147 	}());
24148 	
24149 	/** Assign to container and gadget namespace to have config available in both  */
24150 	window.finesse = window.finesse || {};
24151 	window.finesse.container = window.finesse.container || {};
24152 	window.finesse.container.Config = window.finesse.container.Config || Config;
24153 
24154 	window.finesse.gadget = window.finesse.gadget || {};
24155 	window.finesse.gadget.Config = Config;
24156 
24157 	return Config;
24158 });
24159 /**
24160  * Digital Channel uses a JSON payload for communication with DigitalChannelManager.
24161  * That payload has to conform to this schema.
24162  * This schema has been defined as per http://json-schema.org/
24163  *
24164  * @see utilities.JsonValidator
24165  */
24166 /** The following comment is to prevent jslint errors about using variables before they are defined. */
24167 /*global window:true, define:true*/
24168 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
24169 define('digital/ChannelSchema',['require','exports','module'],function (require, exports, module) {
24170 
24171     var ChannelSchema = (function () {  /** @lends finesse.digital.ChannelSchema.prototype */
24172 
24173         var _menuConfigSchema = {
24174             "$schema": "http://json-schema.org/draft-04/schema#",
24175             "type": "object",
24176             "properties": {
24177                 "label": {
24178                     "type": "string"
24179                 },
24180                 "menuItems": {
24181                     "type": "array",
24182                     "uniqueItems": true,
24183                     "items": [{
24184                         "type": "object",
24185                         "properties": {
24186                             "id": {
24187                                 "type": "string"
24188                             },
24189                             "label": {
24190                                 "type": "string"
24191                             },
24192                             "iconColor": {
24193                                 "type": "string",
24194                                 "enum": ["available", "unavailable", "busy"]
24195                             }
24196                         },
24197                         "required": ["id", "label", "iconColor"],
24198                         "additionalProperties": false
24199                     }]
24200                 }
24201             },
24202             "required": ["label", "menuItems"],
24203             "additionalProperties": false
24204         },
24205 
24206             _channelConfigSchema = {
24207                 "$schema": "http://json-schema.org/draft-04/schema#",
24208                 "type": "object",
24209                 "properties": {
24210                     "actionTimeoutInSec": {
24211                         "type": "integer",
24212                         "minimum": 1,
24213                         "maximum": 30
24214                     },
24215                     "icons": {
24216                         "type": "array",
24217                         "minItems": 1,
24218                         "items": [
24219                             {
24220                                 "type": "object",
24221                                 "properties": {
24222                                     "type": {
24223                                         "type": "string",
24224                                         "enum": ["collab-icon", "url"]
24225                                     },
24226                                     "value": {
24227                                         "type": "string"
24228                                     }
24229                                 },
24230                                 "required": [
24231                                     "type",
24232                                     "value"
24233                                 ],
24234                                 "additionalProperties": false
24235                             }
24236                         ]
24237                     }
24238                 },
24239                 "required": [
24240                     "actionTimeoutInSec",
24241                     "icons"
24242                 ],
24243                 "additionalProperties": false
24244             },
24245 
24246             _channelStateSchema = {
24247                 "$schema": "http://json-schema.org/draft-04/schema#",
24248                 "type": "object",
24249                 "properties": {
24250                     "label": {
24251                         "type": "string"
24252                     },
24253                     "currentState": {
24254                         "type": "string"
24255                     },
24256                     "iconColor": {
24257                         "type": "string"
24258                     },
24259                     "enable": {
24260                         "type": "boolean"
24261                     },
24262                     "logoutDisabled": {
24263                         "type": "boolean"
24264                     },
24265                     "logoutDisabledText": {
24266                         "type": "string"
24267                     },
24268                     "iconBadge": {
24269                         "type": "string",
24270                         "enum": [
24271                             "error",
24272                             "info",
24273                             "warning",
24274                             "none"
24275                         ]
24276                     },
24277                     "hoverText": {
24278                         "type": "string"
24279                     }
24280                 },
24281                 "required": [
24282                     "label",
24283                     "currentState",
24284                     "iconColor",
24285                     "enable",
24286                     "logoutDisabled",
24287                     "iconBadge"
24288                 ],
24289                 "additionalProperties": false
24290             };
24291 
24292         return {
24293 
24294             /**
24295              * @class
24296 			 * <b>F</b>i<b>n</b>esse digital <b>c</b>hannel state control (referred to as FNC elsewhere in this document) 
24297              * is a programmable desktop component that was introduced in Finesse 12.0.
24298              * This API provides the schema that is used in {@link finesse.digital.ChannelService} for various channel operations.
24299              * 
24300              * This schema has been defined as per http://json-schema.org/
24301              * <style>
24302              *   .schemaTable tr:nth-child(even) { background-color:#EEEEEE; }
24303              *   .schemaTable th { background-color: #999999; }
24304              *   .schemaTable th, td { border: none; }
24305              *   .pad30 {padding-left: 30px;}
24306              *   .inset {
24307              *          padding: 5px;
24308              *          border-style: inset; 
24309              *          background-color: #DDDDDD; 
24310              *          width: auto; 
24311              *          text-shadow: 2px 2px 3px rgba(255,255,255,0.5); 
24312              *          font: 12px arial, sans-serif; 
24313              *          color:rebeccapurple;
24314              *    }
24315              * 
24316              * </style>
24317              * 
24318              * @example
24319              * <h3 id='cdIcons'>Cisco Common Desktop Stock Icon names with image</h3>
24320              *
24321              * The channel configuration schema has options to take 
24322              * Cisco Common Desktop icon (CD-icon) name as value.
24323              * 
24324              * To get to know the list of CD-UI icon names and its visual design,
24325              * paste the below JavaScript code in javascript editor part of your 
24326              * browser developer console after Finesse login. This script will clear off the 
24327              * Finesse web-page and will display icon-name and its rendering in a HTML table.
24328              * To get back to the page, just refresh the browser.
24329              * Note: You can also set this up in a gadget for reference.
24330              *  <pre class='inset'>
24331 var showIcons = function () {
24332     $('body').html('');
24333 
24334     $('body').append("<table border='1' background-color:#a0c0a0;'>"
24335         + "<thead style='display: none;'><th>Icon Name</th>"
24336         + "<th>Icon</th></thead><tbody  "
24337         + "style='display: block;  overflow-y: auto; height: 600px'>"
24338         + "</tbody></table>");
24339 
24340     var icons = window.top.cd.core.cdIcon;
24341 
24342     var addIcon = function (name, iconJson) {
24343 
24344         var width = (iconJson.width) ? iconJson.width : 1000;
24345         var height = (iconJson.height) ? iconJson.height : 1000;
24346 
24347         var iconBuilt = "<tr><td>" + name 
24348             + "</td><td><svg width='" + width 
24349             + "' height='" + height 
24350             + "' style='height: 30px; width: 30px;'  viewBox='" 
24351             + iconJson.viewBox + "'>" 
24352             + iconJson.value + "</svg></td></tr>";
24353             
24354 
24355         try {
24356             $('tbody').append(iconBuilt);
24357         } catch (e) {
24358             console.error("Error when adding " + name, e);
24359         }
24360     }
24361 
24362     for (var icon in icons) {
24363         if (icons[icon].viewBox) addIcon(icon, icons[icon])
24364     }
24365 }
24366 
24367 showIcons();
24368              * </pre>
24369              * 
24370              * @constructs
24371              */
24372             _fakeConstuctor: function () {
24373                 /* This is here so we can document init() as a method rather than as a constructor. */
24374             },
24375 
24376             /**
24377              * @example
24378              * <BR><BR><b>Example JSON for <i>MenuConfig</i> data:</b>
24379              * <pre class='inset'>
24380 {
24381   "label" : "Chat", 
24382   "menuItems" :     
24383           [     
24384             {
24385                 "id": "ready-menu-item", 
24386                 "label": "Ready",         
24387                 "iconColor": "available" 
24388             },
24389             {
24390                 "id": "not-ready-menu-item",
24391                 "label": "Not Ready",
24392                 "iconColor": "unavailable"
24393             }
24394          ]
24395 }
24396              * </pre>
24397              * @returns
24398              * Schema for validation of the below JSON definition:
24399              * <table class="schemaTable">
24400              * <thead>
24401              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
24402              * </thead>
24403              * <tbody>
24404              * <tr><td>label</td><td>String</td><td>Chat</td><td>This will be the top level menu name for the channel menu</td></tr>
24405              * <tr><td>menuItems</td><td colspan='2'>Array</td><td>These menus will be listed under the top level menu. A single item is defined below</td></tr>
24406              * <tr><td class='pad30'>id</td><td>String</td><td>ready-menu-item</td><td>This id needs to be unique for a channel. 
24407              *      When there is a user action on the channel menu, this id will be returned back via parameter {@link finesse.digital.ChannelService#selectedMenuItemId}</td></tr>
24408              * <tr><td class='pad30'>label</td><td>String</td><td>Ready</td><td>The text of menu item</td></tr>
24409              * <tr><td class='pad30'>iconColor</td><td>Enum</td><td>available</td><td>available - shows up as green; unavailable - shows up as red;busy - shows up as orange</td></tr>
24410              * </tbody>
24411              * </table>
24412              * 
24413              * 
24414              */
24415             getMenuConfigSchema: function () {
24416                 return _menuConfigSchema;
24417             },
24418 
24419             /**
24420              * @example
24421              * <BR><BR><b>Example JSON for <i>ChannelConfig</i> data:</b>
24422              * <pre class='inset'>
24423 {
24424     "actionTimeoutInSec": 5, 
24425     "icons"             :  [
24426                                 {
24427                                     "type": "collab-icon",
24428                                     "value": "Chat"         
24429                                 },
24430                                 {
24431                                     "type": "url",
24432                                     "value": "../../thirdparty/gadget3/channel-icon.png"
24433                                 }
24434                             ]
24435 }            
24436              * </pre>
24437              * @returns
24438              * Schema for validation of the below JSON definition:
24439              * <table class="schemaTable">
24440              * <thead>
24441              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
24442              * </thead>
24443              * <tbody>
24444              * <tr><td>actionTimeoutInSec</td><td>Integer</td><td>5</td><td>Value in seconds. Represents the max time the FNC will wait after sending the menu 
24445              *              selection request to the gadget.
24446              *              Upper limit is 30 seconds. It is recommended that this limit be kept as 
24447              *              low as possible, since no other operation can be performed 
24448              *              on FNC during this period</td></tr>
24449              * <tr><td>icons</td><td colspan='2'>Array</td><td>JSON array of icons to be composed and displayed in the Header 
24450              *              to visually represent a channel. This should ideally never change. 
24451              *              A single item is defined below</td></tr>
24452              * <tr><td class='pad30'>type</td><td>Enum</td><td>collab-icon</td>
24453              * <td>Takes either <i>collab-icon</i> or <i>url</i> as value. <BR><i>collab-icon</i> would apply a stock icon. Refer to stock icon names over <a href="#cdIcons">here</a>
24454              * <BR><i>url</i> applies a custom icon supplied by gadget. The icon could be located as part of gadget files in Finesse.</td></tr>
24455              * <tr><td class='pad30'>value</td><td>String</td><td>Chat</td><td>The <a href="#cdIcons">stock icon name</a>
24456              * or the url of the custom icon</td></tr>
24457              * </tbody>
24458              * </table>
24459              */
24460             getChannelConfigSchema: function () {
24461                 return _channelConfigSchema;
24462             },
24463 
24464 
24465             /**
24466              * @example
24467              * <BR><BR><b>Example JSON for <i>ChannelState</i> data:</b>
24468              * <pre class='inset'>
24469 {
24470     "label"          : "Chat & Email", 
24471     "currentState"   : "ready", 
24472     "iconColor"      : "available",
24473     "enable"         : true,  
24474     "logoutDisabled" : true,  
24475     "logoutDisabledText" : "Please go unavailable on chat before logout", 
24476     "iconBadge"      :  "none"  
24477     "hoverText"      : "Tooltip text"
24478 }
24479              * </pre>
24480              * 
24481              * @returns
24482              * Schema for validation of the below JSON definition:
24483              * 
24484              * <table class="schemaTable">
24485              * <thead>
24486              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
24487              * </thead>
24488              * <tbody>
24489              * <tr><td>label</td><td>String</td><td>Chat & Email</td><td>The name used for the channel on hover</td></tr>
24490              * <tr><td>currentState</td><td>String</td><td>ready</td><td>Text for the current state of the channel. This text will be appended to the label & will be displayed on the channel icon as a hover message</td></tr>
24491              * <tr><td>iconColor</td><td>Enum</td><td>available</td>
24492              *      <td>Takes one of these values; available - shows up as green; 
24493              *          unavailable - shows up as red; busy - shows up as orange. 
24494              *          The icon colors will be used by the FNC in composing the final icon for the channel, 
24495              *          that indicates the current state of the channel.</td></tr>
24496              * <tr><td>enable</td><td>boolean</td><td>true</td><td>true indicates the channel is active. false indicates the channel is not active and none of the menu should be shown.</td></tr>
24497              * <tr><td>logoutDisabled</td><td>boolean</td><td>true</td><td>Define as true if the logout menu in the user identity component has to be disabled.
24498              * For e.g. during Agent READY or BUSY state, it makes sense to disable the logout menu and enable it again when the state changes to NOT READY.
24499              * <BR>Note: The true setting in any of the Channel across all Channels will cause the Logout menu to be disabled.</td></tr>
24500              * <tr><td>logoutDisabledText</td><td>String</td><td>Please go unavailable on chat before logout</td>
24501              *      <td>The text which will be shown to the user if the logout is disabled</td></tr>
24502              * <tr><td>iconBadge</td><td>Enum</td><td>none</td><td>info - Info badge will be displayed; error - Error badge will be displayed; warning - Warning badge will be displayed; none - No badge will be displayed </td> </tr>
24503              * <tr><td>hoverText</td><td>String</td><td>Tooltip text</td><td>If this is specified it will override the tooltip that shows up by default. Use this to indicate errors or disabled message etc. If the underlying issue is resolved then clear the string </td> /tr>
24504              * </tbody>
24505              * </table>
24506              */
24507             getChannelStateSchema: function () {
24508                 return _channelStateSchema;
24509             }
24510         };
24511     }());
24512 
24513     window.finesse = window.finesse || {};
24514     window.finesse.digital = window.finesse.digital || {};
24515     window.finesse.digital.ChannelSchema = ChannelSchema;
24516 
24517     // CommonJS
24518     if (typeof module === "object" && module.exports) {
24519         module.exports = ChannelSchema;
24520     }
24521 
24522     return ChannelSchema;
24523 });
24524 
24525 /**
24526  * API for managing Finesse Digital Channels.
24527  */
24528 /** @private */
24529 define('digital/ChannelService',['require','exports','module','./ChannelSchema','../utilities/JsonValidator','../utilities/Utilities','../clientservices/ClientServices'],function (require, exports, module) {
24530 	
24531     var ChannelService = (function () { /** @lends finesse.digital.ChannelService.prototype */
24532     	
24533         var SchemaMgr = require('./ChannelSchema'),
24534             SchemaValidator = require('../utilities/JsonValidator'),
24535             Utilities = require('../utilities/Utilities'),
24536             ClientService = require('../clientservices/ClientServices'),
24537             /**
24538              * References to ContainerService
24539              * @private
24540              */
24541             _containerService,
24542 
24543             /**
24544              * Whether the ChannelService have been initialized or not.
24545              * @private
24546              */
24547             _inited = false,
24548 
24549             /**
24550              * References to ClientServices logger
24551              * @private
24552              */
24553             _logger = {
24554                 log: ClientService.log
24555             },
24556 
24557             /**
24558              * Open Ajax topic for the API to publish requests for register, de-register and update on the
24559              * NonVoice Channels. The Finesse NonVoice Component (FNC) will listen to this topic and process
24560              * the request.
24561              * @private
24562              */
24563             _fncTopic,
24564 
24565             /**
24566              * Lists the various channel actions taken by gadget.
24567              * @private
24568              */
24569             ACTIONS = Object.freeze({
24570                 ADD: 'ADD',
24571                 REMOVE: 'REMOVE',
24572                 UPDATE: 'UPDATE',
24573                 UPDATE_MENU: 'UPDATE_MENU',
24574                 UPDATE_CHANNEL_STATE: 'UPDATE_CHANNEL_STATE'
24575             }),
24576 
24577             /**
24578              * Operation status enum
24579              * @private
24580              */
24581             STATUS = Object.freeze({
24582                 SUCCESS: 'success',
24583                 FAILURE: 'failure'
24584             }),
24585 
24586             /**
24587              * State Status enum
24588              * @private
24589              */
24590             STATE_STATUS = Object.freeze({
24591                 AVAILABLE: 'available',
24592                 UNAVAILABLE: 'unavailable',
24593                 BUSY: 'busy'
24594             }),
24595 
24596             /**
24597              * Icon Color mapping for state status
24598              */
24599             ICON_COLOR = Object.freeze({
24600                 available: '#2CB14C',
24601                 unavailable: '#CF4237',
24602                 busy: '#D79208'
24603             }),
24604 
24605             /**
24606              * Channel Icon Type
24607              * @private
24608              */
24609             ICON_TYPE = Object.freeze({
24610                 COLLAB_ICON: "collab-icon",
24611                 URL: "url"
24612             }),
24613 
24614             /**
24615              * Icon Badge Type
24616              * @private
24617              */
24618             BADGE_TYPE = Object.freeze({
24619                 NONE: "none",
24620                 INFO: "info",
24621                 WARNING: "warning",
24622                 ERROR: "error"
24623             }),
24624 
24625             /*
24626              * Dynamic registry object, which keeps a map of success and error callbacks for requests whose
24627              * responses are expected asynchronously. Error callback will be invoked on operation timeout.
24628              * @private
24629              */
24630             _requestCallBackRegistry = (function () {
24631 
24632                 var OPERATION_TIMEOUT = 60000,
24633                     _map = {},
24634 
24635                     _clear = function (_uid) {
24636                         if (_map[_uid]) {
24637                             if (_map[_uid].pendingTimer) {
24638                                 clearTimeout(_map[_uid].pendingTimer);
24639                             }
24640                             delete _map[_uid];
24641                         }
24642                     },
24643 
24644                     _requestTimedOutHandler = function (_uid) {
24645                         var err = {
24646                             status: 'failure',
24647                             error: {
24648                                 errorCode: '408',
24649                                 errorDesc: 'Request Timed Out'
24650                             }
24651                         };
24652                         if (_map[_uid] && typeof _map[_uid].error === 'function') {
24653                             _logger.log("ChannelService: Request Timeout. Request Id : " + _uid);
24654                             _map[_uid].error(err);
24655                         }
24656                         _clear(_uid);
24657                     },
24658 
24659                     _initTimerForTimeout = function (_uid) {
24660                         _map[_uid].pendingTimer = setTimeout(function () {
24661                             _requestTimedOutHandler(_uid);
24662                         }, OPERATION_TIMEOUT);
24663                     },
24664 
24665                     _add = function (_uid, _chId, _success, _error) {
24666                         _map[_uid] = {
24667                             channelId: _chId,
24668                             success: _success,
24669                             error: _error
24670                         };
24671                         _initTimerForTimeout(_uid);
24672                     };
24673 
24674                 return {
24675                     register: _add,
24676                     clear: _clear,
24677                     success: function (uid, data) {
24678                         if (_map[uid] && typeof _map[uid].success === 'function') {
24679                             data.channelId = _map[uid].channelId;
24680                             var response = _map[uid].success(data);
24681                             _clear(uid);
24682                             return response;
24683                         }
24684                     },
24685                     error: function (uid, data) {
24686                         if (_map[uid] && typeof _map[uid].error === 'function') {
24687                             data.channelId = _map[uid].channelId;
24688                             var response = _map[uid].error(data);
24689                             _clear(uid);
24690                             return response;
24691                         }
24692                     }
24693                 };
24694             }()),
24695 
24696             /*
24697              * Dynamic registry object, which keeps a map of channel id and its menu handler function reference.
24698              * This handler will be invoked on user interaction with its channel menu.
24699              * @private
24700              */
24701             _menuHandlerRegistry = (function () {
24702                 var _map = {},
24703 
24704                     _add = function (_id, _chId, _menuHandler) {
24705                         _map[_id] = {
24706                             channelId: _chId,
24707                             menuHandler: _menuHandler
24708                         };
24709                     },
24710 
24711                     _clear = function (_id) {
24712                         if (_map[_id]) {
24713                             delete _map[_id];
24714                         }
24715                     };
24716 
24717                 return {
24718                     register: _add,
24719                     clear: _clear,
24720                     menuSelection: function (_id, _menuItemId, _success, _error) {
24721                         if (_map[_id] && _map[_id].menuHandler) {
24722                             return _map[_id].menuHandler(_map[_id].channelId, _menuItemId,
24723                                 _success, _error);
24724                         }
24725                     }
24726                 };
24727             }()),
24728 
24729             /**
24730              * Ensure that ChannelService have been inited.
24731              * @private
24732              */
24733             _isInited = function () {
24734                 if (!_inited) {
24735                     throw new Error("ChannelService needs to be inited.");
24736                 }
24737             },
24738 
24739             /**
24740              * Gets the id of the gadget.
24741              * @returns {number} the id of the gadget
24742              * @private
24743              */
24744             _getMyGadgetId = function () {
24745                 var gadgetId = _containerService.getMyGadgetId();
24746                 return gadgetId || '';
24747             },
24748 
24749             /**
24750              * Validate the menuConfig structure in channelData payload.
24751              *
24752              * @param {Object} data
24753              *     menuConfig structure.
24754              * @throws {Error} if the data is not in menuConfig defined format.
24755              * @private
24756              */
24757             _validateMenuConfig = function (data) {
24758                 var result = SchemaValidator.validateJson(data, SchemaMgr.getMenuConfigSchema());
24759                 /* if result.valid is false, then additional details about failure are contained in
24760                    result.error which contains json like below:
24761 
24762                     {
24763                         "code": 0,
24764                         "message": "Invalid type: string",
24765                         "dataPath": "/intKey",
24766                         "schemaPath": "/properties/intKey/type"
24767                     }
24768                 */
24769                 if (!result.valid) {
24770                     _logger.log("ChannelService: Finesse Nonvoice API Validation result : " + JSON.stringify(result));
24771                     throw new Error("menuConfig structure is not in expected format. Refer finesse client logs for more details.");
24772                 }
24773             },
24774 
24775             /**
24776              * Validate the channelConfig structure in channelData payload.
24777              *
24778              * @param {Object} data
24779              *     channelConfig structure.
24780              * @throws {Error} if the data is not in channelConfig defined format.
24781              * @private
24782              */
24783             _validateChannelConfig = function (data) {
24784                 var result = SchemaValidator.validateJson(data, SchemaMgr.getChannelConfigSchema());
24785                 if (!result.valid) {
24786                     _logger.log("ChannelService: Finesse Nonvoice API Validation result : " + JSON.stringify(result));
24787                     throw new Error("channelConfig structure is not in expected format. Refer finesse client logs for more details.");
24788                 }
24789             },
24790 
24791             /**
24792              * Validate the channelState structure in channelData payload.
24793              *
24794              * @param {Object} data
24795              *     channelState structure.
24796              * @throws {Error} if the data is not in channelState defined format.
24797              * @private
24798              */
24799             _validateChannelState = function (data) {
24800                 var result = SchemaValidator.validateJson(data, SchemaMgr.getChannelStateSchema());
24801                 if (!result.valid) {
24802                     _logger.log("ChannelService: Finesse Nonvoice API Validation result : " + JSON.stringify(result));
24803                     throw new Error("channelState structure is not in expected format. Refer finesse client logs for more details.");
24804                 }
24805             },
24806 
24807             /**
24808              * Validate the entire channelData structure in payload.
24809              *
24810              * @param {Object} data
24811              *     channelData structure.
24812              * @throws {Error} if the data is not in channelData defined format.
24813              * @private
24814              */
24815             _validateAddChannelPayload = function (data) {
24816                 var err = "";
24817                 if (!data.hasOwnProperty("menuConfig")) {
24818                     err = "menuConfig property missing in Channel Data";
24819                 } else if (!data.hasOwnProperty("channelConfig")) {
24820                     err = "channelConfig property missing in Channel Data";
24821                 } else if (!data.hasOwnProperty("channelState")) {
24822                     err = "channelState property missing in Channel Data";
24823                 }
24824 
24825                 if (err) {
24826                     throw new Error(err);
24827                 }
24828                 _validateMenuConfig(data.menuConfig);
24829                 _validateChannelConfig(data.channelConfig);
24830                 _validateChannelState(data.channelState);
24831             },
24832 
24833             /**
24834              * Validate the available structure in payload.
24835              *
24836              * @param {Object} data
24837              *     channelData structure.
24838              * @throws {Error} if the data is not in channelData defined format.
24839              * @private
24840              */
24841             _validateUpdateChannelPayload = function (data) {
24842                 if (data.hasOwnProperty("menuConfig")) {
24843                     _validateMenuConfig(data.menuConfig);
24844                 }
24845                 if (data.hasOwnProperty("channelConfig")) {
24846                     _validateChannelConfig(data.channelConfig);
24847                 }
24848                 if (data.hasOwnProperty("channelState")) {
24849                     _validateChannelState(data.channelState);
24850                 }
24851             },
24852 
24853             /**
24854              * Validate the gadget passed JSON structure against the schema definition.
24855              *
24856              * @param {Object} data
24857              *     channelData structure.
24858              * @throws {Error} if the data is not in channelData defined format.
24859              * @private
24860              */
24861             _validateData = function (data, action) {
24862                 switch (action) {
24863                     case ACTIONS.ADD:
24864                         _validateAddChannelPayload(data);
24865                         break;
24866                     case ACTIONS.UPDATE:
24867                         _validateUpdateChannelPayload(data);
24868                         break;
24869                     case ACTIONS.UPDATE_CHANNEL_STATE:
24870                         _validateChannelState(data);
24871                         break;
24872                     case ACTIONS.UPDATE_MENU:
24873                         _validateMenuConfig(data);
24874                         break;
24875                 }
24876             },
24877 
24878             /**
24879              * Prepares the FNC required payload based on the action and the supplied JSON data.
24880              *
24881              * @param {Object} data
24882              *     json structure used for channel request.
24883              * @returns {Object} data in FNC required payload format.
24884              * @private
24885              */
24886             _prepareFNCChannelData = function (data, action) {
24887                 switch (action) {
24888                     case ACTIONS.ADD:
24889                     	if (data.hasOwnProperty("channelState")) {
24890                     		data.channelState.iconColor = ICON_COLOR[data.channelState.iconColor];
24891                     	}
24892                         data.menuConfig.menuItems.forEach(function (item) {
24893                             item.iconColor = ICON_COLOR[item.iconColor];
24894                         });
24895                         break;
24896                     case ACTIONS.UPDATE:
24897                         if (data.hasOwnProperty("channelState")) {
24898                             data.channelState.iconColor = ICON_COLOR[data.channelState.iconColor];
24899                         }
24900                         if (data.hasOwnProperty("menuConfig")) {
24901                             data.menuConfig.menuItems.forEach(function (item) {
24902                                 item.iconColor = ICON_COLOR[item.iconColor];
24903                             });
24904                         }
24905                         break;
24906                     case ACTIONS.UPDATE_CHANNEL_STATE:
24907                         data.iconColor = ICON_COLOR[data.iconColor];
24908                         data = {
24909                             channelState: data
24910                         };
24911                         break;
24912                     case ACTIONS.UPDATE_MENU:
24913                         data.menuItems.forEach(function (item) {
24914                             item.iconColor = ICON_COLOR[item.iconColor];
24915                         });
24916                         data = {
24917                             menuConfig: data
24918                         };
24919                         break;
24920                 }
24921                 if(data.channelState){
24922                 	data.channelState.isAgentReady = data.channelState.iconColor === ICON_COLOR.available;
24923                 }
24924                 
24925                 return data;
24926             },
24927 
24928             /**
24929              * Utility function to make a subscription to a particular topic. Only one
24930              * callback function is registered to a particular topic at any time.
24931              *
24932              * @param {String} topic
24933              *     The full topic name. The topic name should follow the OpenAjax
24934              *     convention using dot notation (ex: finesse.api.User.1000).
24935              * @param {Function} handler
24936              *     The function that should be invoked with the data when an event
24937              *     is delivered to the specific topic.
24938              * @returns {Boolean}
24939              *     True if the subscription was made successfully and the callback was
24940              *     been registered. False if the subscription already exist, the
24941              *     callback was not overwritten.
24942              * @private
24943              */
24944             _subscribe = function (topic, handler) {
24945                 try {
24946                     return _containerService.addHandler(topic, handler);
24947                 } catch (error) {
24948                     _logger.log('ChannelService: Error while subscribing to open ajax topic : ' + topic + ', Exception:' + error.toString());
24949                     throw new Error('ChannelService: Unable to subscribe the channel topic with Open Ajax Hub : ' + error);
24950                 }
24951             },
24952 
24953             /**
24954              * Function which processes the menu selection request from FNC.
24955              *
24956              * @param {Object} payload
24957              *     The payload containing the menu selection request details.
24958              * @private
24959              */
24960             _processMenuChangeRequest = function (payload) {
24961                 _menuHandlerRegistry.menuSelection(payload.id, payload.selection, function (successPayload) {
24962                     var response = {
24963                         id: payload.id,
24964                         requestId: payload.requestId,
24965                         status: successPayload.hasOwnProperty('status') ? successPayload.status : STATUS.SUCCESS
24966                     };
24967                     _containerService.publish(_fncTopic, response);
24968                 }, function (failurePayload) {
24969                     var response = {
24970                         id: payload.id,
24971                         requestId: payload.requestId,
24972                         status: failurePayload.hasOwnProperty('status') ? failurePayload.status : STATUS.FAILURE
24973                     };
24974                     if (failurePayload.hasOwnProperty('error')) {
24975                         response.error = failurePayload.error;
24976                     }
24977                     _containerService.publish(_fncTopic, response);
24978                 });
24979             },
24980 
24981             _processChannelOperationResult = function (payload) {
24982                 var response = {
24983                     status: payload.status
24984                 };
24985                 if (payload.status === STATUS.FAILURE) {
24986                     // error response
24987                     if (payload.hasOwnProperty('error')) {
24988                         response.error = payload.error;
24989                     }
24990                     _logger.log('ChannelService: Failure response for requestId: ' + payload.requestId);
24991                     _requestCallBackRegistry.error(payload.requestId, response);
24992                 } else {
24993                     // success response
24994                     _requestCallBackRegistry.success(payload.requestId, response);
24995                 }
24996             },
24997 
24998             /**
24999              * Function which processes the messages from Finesse NonVoice Component.
25000              * The messages received mainly for below cases,
25001              * <ul>
25002              *  <li> Operation responses for ADD, REMOVE and UPDATE.
25003              *  <li> User menu selection request.
25004              * <ul>
25005              * The registered callbacks are invoked based on the data.
25006              *
25007              * @param {String} payload
25008              *     The actual message sent by FNC via open ajax hub.
25009              * @private
25010              */
25011             _processFNCMessage = function (payload) {
25012                 _logger.log('ChannelService: Received Message from FNC: ' + JSON.stringify(payload));
25013                 try {
25014                     // if the received data from topic is in text format, then parse it to JSON format
25015                     payload = typeof payload === 'string' ? JSON.parse(payload) : payload;
25016                 } catch (error) {
25017                     _logger.log('ChannelService: Error while parsing the FNC message' + payload);
25018                     return;
25019                 }
25020 
25021                 if (payload.hasOwnProperty('selection')) {
25022                     // menu selection request from FNC
25023                     _processMenuChangeRequest(payload);
25024                 } else {
25025                     // ADD or REMOVE or UPDATE operation status from FNC
25026                     _processChannelOperationResult(payload);
25027                 }
25028             },
25029 
25030             /*
25031              * Validate the data and process the channel API request based on the action.
25032              *
25033              * @param {String} channelId
25034              *     Digtial channel id, should be unique within the gadget.
25035              * @param {Actions Enum} action
25036              *     Any one of the channel action defined in ACTIONS enum.
25037              * @param {Function} success
25038              *     Callback function invoked upon request successful.
25039              * @param {Function} error
25040              *     Callback function invoked upon request errored.
25041              * @param {JSON} data
25042              *     ADD or UPDATE operation data object as per the defined format.
25043              * @param {Function} menuHandler
25044              *     Callback function invoked when the user interacts with the registered channel menu.
25045              * @throws Error
25046              *     Throws error when the passed in data is not in defined format.
25047              * @private
25048              */
25049             _processChannelRequest = function (channelId, action, success, error, data, menuHandler) {
25050                 _logger.log('ChannelService: Received digital channel request. ChannelId ' + channelId + ', Action ' + action + ', Data ' + JSON.stringify(data));
25051 
25052                 _isInited();
25053 
25054                 var id = _getMyGadgetId() + '/' + channelId,
25055                     topic = _fncTopic + '.' + id,
25056                     requestUID, payload;
25057 
25058                 if (action === ACTIONS.REMOVE) {
25059                     // clear the menuHandler for this channel from menu registry
25060                     _menuHandlerRegistry.clear(id);
25061 
25062                     // remove the hub listener for the channel topic
25063                     _containerService.removeHandler(topic, _processFNCMessage);
25064                 } else {
25065                     if (!data) {
25066                         throw new Error("ChannelService: Channel Data cannot be empty.");
25067                     }
25068                     try {
25069                         data = typeof data === 'string' ? JSON.parse(data) : data;
25070                     } catch (err) {
25071                         throw new Error("ChannelService: Data Parsing Failed. ADD or UPDTAE payload not in expected format. Error: " + err.toString);
25072                     }
25073 
25074                     // validate the data
25075                     _validateData(data, action);
25076 
25077                     // prepare FNC payload
25078                     data = _prepareFNCChannelData(data, action);
25079 
25080                     if (action === ACTIONS.ADD) {
25081                         // onMenuClick must be supplied and of type function
25082                         if (!menuHandler || typeof menuHandler !== 'function') {
25083                             throw new Error("ChannelService: onMenuClick parameter in addChannel() must be a function.");
25084                         }
25085 
25086                         // register the menuHandler for this channel with menu registry for later use
25087                         _menuHandlerRegistry.register(id, channelId, menuHandler);
25088 
25089                         // subscribe this channel topic for messages from Finesse NV Header Component
25090                         _subscribe(topic, _processFNCMessage);
25091                     }
25092 
25093                     // derive the FNC action
25094                     action = action !== ACTIONS.ADD ? ACTIONS.UPDATE : action;
25095                 }
25096 
25097 
25098                 requestUID = Utilities.generateUUID();
25099                 payload = {
25100                     id: id,
25101                     topic: topic,
25102                     requestId: requestUID,
25103                     action: action,
25104                     data: data
25105                 };
25106 
25107                 /*
25108                  * register the request with request registry for asynchronously
25109                  * invoking success and failure callbacks about the operation
25110                  * status.
25111                  */
25112                 _requestCallBackRegistry.register(requestUID, channelId, success, error);
25113 
25114                 _logger.log('ChannelService: Sending channel request to FNC: ' + JSON.stringify(payload));
25115 
25116                 _containerService.publish(_fncTopic, payload);
25117             };
25118 
25119         return {
25120             
25121         	
25122 
25123         	
25124         	/**
25125              * @class
25126 			 * <b>F</b>i<b>n</b>esse digital <b>c</b>hannel state control (referred to as FNC elsewhere in this document) 
25127              * is a programmable desktop component that was introduced in Finesse 12.0.
25128              * The ChannelService API provides hooks to FNC that can be leveraged by gadget hosting channels.
25129              * 
25130              * <h3> FNC API </h3>
25131              * 
25132              * The FNC API provides methods that can be leveraged by gadgets serving channels to 
25133              * register, update or modify channel specific display information and corresponding menu action behavior
25134              *  in Agent State Control Menu (referred to as FNC Menu component).
25135              * 
25136              * <BR>
25137              * These APIs are available natively to the gadget through the finesse.js import. Examples of how to write a sample gadget
25138              * can be found <a href="https://github.com/CiscoDevNet/finesse-sample-code/tree/master/LearningSampleGadget">here</a>.
25139              * <BR>
25140              * The FNC API is encapsulated in a module named ChannelService within finesse.js and it can be accessed 
25141              * via the namespace <i><b>finesse.digital.ChannelService</b></i>.
25142 			 *
25143              * <style>
25144              *   .channelTable tr:nth-child(even) { background-color:#EEEEEE; }
25145              *   .channelTable th { background-color: #999999; }
25146              *   .channelTable th, td { border: none; }
25147              *   .pad30 {padding-left: 30px;}
25148              *   .inset {
25149              *          padding: 5px;
25150              *          border-style: inset; 
25151              *          background-color: #DDDDDD; 
25152              *          width: auto; 
25153              *          text-shadow: 2px 2px 3px rgba(255,255,255,0.5); 
25154              *          font: 12px arial, sans-serif; 
25155              *          color:rebeccapurple;
25156              *    } 
25157              * 
25158              * </style>
25159              * 
25160 			 * @example
25161 			 * 
25162 			 * <h3> Example demonstrating initialization </h3>
25163              * 
25164              * <pre class='inset'>
25165 containerServices = finesse.containerservices.ContainerServices.init();
25166 channelService    = finesse.digital.ChannelService.init(containerServices);
25167 channelService.addChannel(channelId, channelData, onMenuClick, onSuccess, onError);
25168              * </pre>
25169 
25170              * @constructs
25171              */
25172             _fakeConstuctor: function () {
25173                 /* This is here so we can document init() as a method rather than as a constructor. */
25174             },
25175 
25176             /**
25177              * Initialize ChannelService for use in gadget.
25178              *
25179              * @param {finesse.containerservices.ContainerServices} ContainerServices instance.
25180              * @returns {finesse.digital.ChannelService} instance.
25181              */
25182             init: function (containerServices) {
25183                 if (!_inited) {
25184                     if (!containerServices) {
25185                         throw new Error("ChannelService: Invalid ContainerService reference.");
25186                     }
25187                     _inited = true;
25188                     _containerService = containerServices;
25189                     window.finesse.clientservices.ClientServices.setLogger(window.finesse.cslogger.ClientLogger);
25190                     _fncTopic = containerServices.Topics.FINEXT_NON_VOICE_GADGET_EVENT;
25191                 }
25192                 return this;
25193             },
25194 
25195             /**
25196              * 
25197              * This API starts the gadget interaction with FNC by adding the channel to the FNC Menu component. 
25198              * The complete channel state is required in the form of JSON payload as part of this API.
25199              * It is advised developers pre-validate the JSON that is passed as parameter against its corresponding schema 
25200              * by testing it through {@link finesse.utilities.JsonValidator.validateJson}.
25201              * The result of the add operation will be returned via the given success or error callback.
25202              *
25203              * @param {String} channelId
25204              *     Digital channel id, should be unique within the gadget.
25205              *     The API developer can supply any string that uniquely identifies the Digital Channel for registration with FNC.
25206              *     This id would be returned in callbacks by FNC when there is a user action on menus that belongs to a channel.
25207              * @param {Object} channelData
25208              *     It is a composition of the following KEY-VALUE pair as JSON. The value part would be the object that conforms to 'key' specific schema.
25209              *     <table class="channelTable">
25210              *     <thead><tr><th>key</th><th>Value</th></tr><thead>
25211              *     <tbody>
25212              *     <tr><td>menuconfig</td><td>Refer {@link finesse.digital.ChannelSchema#getMenuConfigSchema} for description about this JSON payload</td></tr>
25213              *     <tr><td>channelConfig</td><td>Refer {@link finesse.digital.ChannelSchema#getChannelConfigSchema} for description about this JSON payload</td></tr>
25214              *     <tr><td>channelState</td><td>Refer {@link finesse.digital.ChannelSchema#getChannelStateSchema} for description about this JSON payload</td></tr>
25215              *     </tbody>
25216              *     </table>
25217              * 
25218              * @param onMenuClick
25219              *     Handler that is provided by the Gadget to the API during channel addition. 
25220              *     It is invoked whenever the user clicks a menu item on the FNC control.
25221              *     <table class="channelTable">
25222              *     <thead><tr><th>Parameter</th><th>Description</th></tr></thead>
25223              *     <tbody>
25224              *     <tr><td><h4 id="selectedMenuItemId">selectedMenuItemId</h4></td><td>The selectedMenuItemId will contain the menuId as defined in 
25225              *      menuConfig {@link finesse.digital.ChannelSchema#getMenuConfigSchema} payload. 
25226              *      The gadget has to invoke onSuccess callback, if the state change is success or 
25227              *      onError callback if there is a failure performing state change operation 
25228              *      (refer actionTimeoutInSec in JSON payload for timeout) on the underlying channel service.</td></tr>
25229              *     <tr><td><h5 id="success">onSuccess<h5></td><td>The success payload would be of the following format:
25230              *      <pre>
25231              *      {
25232              *          "channelId" : "[ID of the Digital channel]",
25233              *          "status"    : "success"
25234              *      }
25235              *      </pre></td></tr>
25236              *     <tr><td><h5 id="failure">onError<h5></td><td>The error payload would be of the following format:
25237              *      <pre>
25238              *       { 
25239              *       "channelId" : "[ID of the Digital channel]",
25240              *       "status"    : "failure",  
25241              *       "error"     : { 
25242              *                       "errorCode": "[Channel supplied error code that will be logged in Finesse client logs]",
25243              *                       "errorDesc": "An error occurred while processing request"  
25244              *                      }
25245              *       }
25246              *      </pre></td></tr>
25247              *      </tbody></table>
25248              * @param onSuccess
25249              *      Callback function invoked upon add operation successful. Refer above for the returned JSON payload.
25250              * @param onError
25251              *      Callback function invoked upon add operation is unsuccessful. Refer above for the returned JSON payload.
25252              * @throws
25253              *     Throws error when the passed in channelData is not as per schema.
25254              */
25255             addChannel: function (channelId, channelData, onMenuClick, onSuccess, onError) {
25256                 _processChannelRequest(channelId, ACTIONS.ADD, onSuccess, onError, channelData, onMenuClick);
25257             },
25258 
25259             /**
25260              * Removes a channel representation from the FNC Menu component. 
25261              * The result of the remove operation will be intimated via the given success and error callbacks.
25262              *
25263              * @param {String} channelId
25264              *     Digtial channel id, should be unique within the gadget.
25265              *     This id would be returned in the callbacks.
25266              * @param {Function} onSuccess
25267              *     <a href="#success">onSuccess</a> function that is invoked for successful operation.
25268              * @param {Function} onError
25269              *     <a href="#failure">onError</a> function that is invoked for failed operation.
25270              */
25271             removeChannel: function (channelId, onSuccess, onError) {
25272                 _processChannelRequest(channelId, ACTIONS.REMOVE, onSuccess, onError);
25273             },
25274 
25275             /**
25276              * Updates the channels representation in FNC Menu component. 
25277              * None of the data passed within the data payload <i>channelData</i> is mandatory. 
25278              * This API provides an easy way to update the complete channel configuration together in one go or partially if required. 
25279              * The result of the update operation will be intimated via the given success and error callbacks.
25280              *
25281              * @param {String} channelId
25282              *     Digtial channel id, should be unique within the gadget
25283              *     This id would be returned in the callbacks.
25284              * @param {Object} channelData
25285              *     Channel data JSON object as per the spec. Refer {@link #addChannel} for this object description. Partial data sections allowed.
25286              * @param {Function} onSuccess
25287              *     <a href="#success">onSuccess</a> function that is invoked for successful operation.
25288              * @param {Function} onError
25289              *     <a href="#failure">onError</a> function that is invoked for failed operation.
25290              */
25291             updateChannel: function (channelId, channelData, onSuccess, onError) {
25292                 _processChannelRequest(channelId, ACTIONS.UPDATE, onSuccess, onError, channelData);
25293             },
25294 
25295             /**
25296              * Updates the menu displayed against a channel.
25297              * The result of the update operation will be intimated via the given success and error callbacks.
25298              *
25299              * @param {String} channelId
25300              *     Digtial channel id, should be unique within the gadget.
25301              *     This id would be returned in the callbacks.
25302              * @param {menuItem[]} menuItems
25303              *     Refer {@link finesse.digital.ChannelSchema#getMenuConfigSchema} for menuItem definition.
25304              * @param {Function} onSuccess
25305              *     <a href="#success">onSuccess</a> function that is invoked for successful operation.
25306              * @param {Function} onError
25307              *     <a href="#failure">onError</a> function that is invoked for failed operation.
25308              */
25309             updateChannelMenu: function (channelId, menuConfig, onSuccess, onError) {
25310                 _processChannelRequest(channelId, ACTIONS.UPDATE_MENU, onSuccess, onError, menuConfig);
25311             },
25312 
25313             /**
25314              * Updates the channels current state.  The result of
25315              * the update operation will be intimated via the given success and error callbacks.
25316              *
25317              * @param {String} channelId
25318              *     Digtial channel id, should be unique within the gadget.
25319              *     This id would be returned in the callbacks.
25320              * @param {Object} channelState
25321              *     Refer {@link finesse.digital.ChannelSchema#getChannelStateSchema} for channel state definition.
25322              * @param {Function} onSuccess
25323              *     <a href="#success">onSuccess</a> function that is invoked for successful operation.
25324              * @param {Function} onError
25325              *     <a href="#failure">onError</a> function that is invoked for failed operation.
25326              */
25327             updateChannelState: function (channelId, channelState, onSuccess, onError) {
25328                 _processChannelRequest(channelId, ACTIONS.UPDATE_CHANNEL_STATE, onSuccess, onError, channelState);
25329             },
25330 
25331             /**
25332              * ENUM: Operation status.
25333              * [SUCCESS, FAILURE]
25334              */
25335             STATUS: STATUS,
25336 
25337             /**
25338              * ENUM: State Status indicates the icon color in channel.
25339              * [AVAILABLE, UNAVAILABLE, BUSY]*/
25340             STATE_STATUS: STATE_STATUS,
25341 
25342             /**
25343              * ENUM: Channel Icon location type.
25344              * [COLLAB_ICON, URL]
25345              */
25346             ICON_TYPE: ICON_TYPE,
25347 
25348             /**
25349              * ENUM: Icon Badge Type.
25350              * [NONE, INFO, WARNING, ERROR]
25351              */
25352             ICON_BADGE_TYPE: BADGE_TYPE
25353         };
25354     }());
25355     
25356     
25357     /** @namespace Namespace for JavaScript class objects and methods related to Digital Channel management.*/
25358     finesse.digital = finesse.digital || {};
25359 
25360     window.finesse = window.finesse || {};
25361     window.finesse.digital = window.finesse.digital || {};
25362     window.finesse.digital.ChannelService = ChannelService;
25363 
25364     // CommonJS
25365     if (typeof module === "object" && module.exports) {
25366         module.exports = ChannelService;
25367     }
25368 
25369 
25370     return ChannelService;
25371 });
25372 
25373 /**
25374  * Popover service uses a JSON payload for communication from gadget.
25375  * The aforesaid payload has to conform to this schema.
25376  * This schema has been defined as per <a href='http://json-schema.org/'> JSON schema </a> standard.
25377  *
25378  * @see utilities.JsonValidator
25379  */
25380 /** The following comment is to prevent jslint errors about using variables before they are defined. */
25381 /*global window:true, define:true*/
25382 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
25383 define('containerservices/PopoverSchema',['require','exports','module'],function (require, exports, module) {
25384 
25385     var PopoverSchema = (function () { /** @lends finesse.containerservices.PopoverSchema.prototype */
25386 
25387         var _bannerDataSchema = {
25388                 "$schema": "http://json-schema.org/draft-04/schema#",
25389                 "type": "object",
25390                 "properties": {
25391                     "icon": {
25392                         "type": "object",
25393                         "properties": {
25394                             "type": {
25395                                 "type": "string",
25396                                 "enum": ["collab-icon", "url"]
25397                             },
25398                             "value": {
25399                                 "type": "string"
25400                             }
25401                         },
25402                         "required": [
25403                             "type",
25404                             "value"
25405                         ]
25406                     },
25407                     "content": {
25408                         "type": "array",
25409                         "items": [{
25410                             "type": "object",
25411                             "properties": {
25412                                 "name": {
25413                                     "type": "string"
25414                                 },
25415                                 "value": {
25416                                     "type": "string"
25417                                 }
25418                             },
25419                             "required": [
25420                                 "name",
25421                                 "value"
25422                             ]
25423                         }]
25424                     },
25425                     "headerContent": {
25426                         "type": "object",
25427                         "properties": {
25428                                 "maxHeaderTitle": {
25429                                     "type": "string"
25430                                 },
25431                                 "minHeaderTitle": {
25432                                     "type": "string"
25433                                 }
25434                             }
25435                     }
25436                 },
25437                 "required": [
25438                     "icon"
25439                 ],
25440                 "additionalProperties": false
25441             },
25442 
25443             _timeDataSchema = {
25444                 "$schema": "http://json-schema.org/draft-04/schema#",
25445                 "type": "object",
25446                 "properties": {
25447                     "displayTimeoutInSecs": {
25448                         "type": "integer",
25449                         "oneOf": [
25450                             {
25451                                 "minimum": 3,
25452                                 "maximum": 3600
25453                             },
25454                             {
25455                                 "enum": [-1] /* displayTimeoutInSecs can be in range 3 and 3600, or -1. -1 indicates there is no upper limit for timer */
25456                             }
25457                         ]
25458                     },
25459                     "display": {
25460                         "type": "boolean"
25461                     },
25462                     "counterType": {
25463                         "type": "string",
25464                         "enum": ["COUNT_UP", "COUNT_DOWN"]
25465                     }
25466                 },
25467                 "oneOf": [{
25468                     "properties": {
25469                         "display": {
25470                             "type": "boolean",
25471                             "enum": [
25472                                 false
25473                             ]
25474                         }
25475                     },
25476                     "required": [
25477                         "displayTimeoutInSecs",
25478                         "display"
25479                     ],
25480                     "not": {
25481                         "required": ["counterType"]
25482                     },
25483                 },
25484                 {
25485                     "properties": {
25486                         "display": {
25487                             "type": "boolean",
25488                             "enum": [
25489                                 true
25490                             ]
25491                         }
25492                     },
25493                     "required": [
25494                         "displayTimeoutInSecs",
25495                         "display",
25496                         "counterType"
25497                     ]
25498                 }
25499                 ],
25500                 "additionalProperties": false
25501             },
25502 
25503             _actionDataSchema = {
25504                 "$schema": "http://json-schema.org/draft-04/schema#",
25505                 "type": "object",
25506                 "properties": {
25507                     "keepMaximised": {
25508                         "type": "boolean"
25509                     },
25510                     "dismissible": {
25511                         "type": "boolean"
25512                     },
25513                     "clientIdentifier": {
25514                         "type": "string"
25515                     },
25516                     "requiredActionText": {
25517                         "type": "string"
25518                     },
25519                     "buttons": {
25520                         "type": "array",
25521                         "minItems": 1,
25522                         "maxItems": 2,
25523                         "uniqueItems": true,
25524                         "items": [{
25525                             "type": "object",
25526                             "properties": {
25527                                 "id": {
25528                                     "type": "string"
25529                                 },
25530                                 "label": {
25531                                     "type": "string"
25532                                 },
25533                                 "type": {
25534                                     "type": "string"
25535                                 },
25536                                 "hoverText": {
25537                                     "type": "string"
25538                                 },
25539                                 "confirmButtons": {
25540                                     "type": "array",
25541                                     "uniqueItems": true,
25542                                     "items": [{
25543                                         "type": "object",
25544                                         "properties": {
25545                                             "id": {
25546                                                 "type": "string"
25547                                             },
25548                                             "label": {
25549                                                 "type": "string"
25550                                             },
25551                                             "hoverText": {
25552                                                 "type": "string"
25553                                             }
25554                                         },
25555                                         "required": [
25556                                             "id",
25557                                             "label"
25558                                         ]
25559                                     }]
25560                                 }
25561                             },
25562                             "required": [
25563                                 "id",
25564                                 "label",
25565                                 "type"
25566                             ]
25567                         }]
25568                     }
25569                 },
25570                 "oneOf": [{
25571                         "additionalProperties": false,
25572                         "properties": {
25573                             "requiredActionText": {
25574                                 "type": "string"
25575                             },
25576                             "clientIdentifier": {
25577                                 "type": "string"
25578                             },
25579                             "dismissible": {
25580                                 "type": "boolean",
25581                                 "enum": [
25582                                     false
25583                                 ]
25584                             },
25585                             "keepMaximised": {
25586                                 "type": "boolean"
25587                             },
25588                             "buttons": {
25589                                 "type": "array"
25590                             }
25591                         }
25592                     },
25593                     {
25594                         "additionalProperties": false,
25595                         "properties": {
25596                             "requiredActionText": {
25597                                 "type": "string"
25598                             },
25599                             "clientIdentifier": {
25600                                 "type": "string"
25601                             },
25602                             "dismissible": {
25603                                 "type": "boolean",
25604                                 "enum": [
25605                                     true
25606                                 ]
25607                             },
25608                             "keepMaximised": {
25609                                 "type": "boolean"
25610                             }
25611                         }
25612                     }
25613                 ],
25614                 "required": [
25615                     "dismissible"
25616                 ],
25617                 "additionalProperties": false
25618             },
25619 
25620             _headerDataSchema = {
25621                 "$schema": "http://json-schema.org/draft-04/schema#",
25622                 "type": "object",
25623                 "properties": {
25624                     "maxHeaderTitle": {
25625                         "type": "string"
25626                     },
25627                     "minHeaderTitle": {
25628                         "type": "string"
25629                     }
25630                 },
25631                 "additionalProperties": false
25632             };
25633 
25634         return {
25635 
25636             /**
25637              * @class
25638              * Finesse Voice component and Gadget(s) hosting digital services require 
25639              * the {@link finesse.containerservices.PopoverService} to display a popup
25640              * for incoming call and chat events. <BR>
25641              * This API provides the schema that is used in {@link finesse.containerservices.PopoverService} 
25642              * for managing various operations in the popup.
25643              * 
25644              * This schema has been defined as per http://json-schema.org/
25645              * <style>
25646              *   .schemaTable tr:nth-child(even) { background-color:#EEEEEE; }
25647              *   .schemaTable th { background-color: #999999; }
25648              *   .schemaTable th, td { border: none; }
25649              *   .pad30 {padding-left: 30px;}
25650              *   .pad60 {padding-left: 60px;}
25651              *   .inset {
25652              *          padding: 5px;
25653              *          border-style: inset; 
25654              *          background-color: #DDDDDD; 
25655              *          width: auto; 
25656              *          text-shadow: 2px 2px 3px rgba(255,255,255,0.5); 
25657              *          font: 12px arial, sans-serif; 
25658              *          color:rebeccapurple;
25659              *    }
25660              * </style>
25661              * 
25662              * @constructs
25663              */
25664             _fakeConstuctor: function () {
25665                 /* This is here so we can document init() as a method rather than as a constructor. */
25666             },
25667 
25668             /**
25669              * 
25670              * @example
25671              * <BR><BR><b>Example JSON for <i>BannerData</i>:</b>
25672              * <pre class='inset'>
25673 {
25674     "icon": { // Mandatory
25675         "type": "collab-icon",
25676         "value": "chat"
25677     },
25678     "content": [ // Optional. first 6 name/value pairs is shown in popover
25679         {
25680             "name": "Customer Name",
25681             "value": "Michael Littlefoot"
25682         },
25683         {
25684             "name": "Phone Number",
25685             "value": "+1-408-567-789"
25686         },
25687         {
25688             "name": "Account Number",
25689             "value": "23874567923"
25690         },
25691         {
25692             "name": "Issue", // For the below one, tool tip is displayed
25693             "value": "a very long text. a very long text.a very long text.a very long text.a very long text."
25694         }
25695     ]
25696     "headerContent" : {
25697         "maxHeaderTitle" : "Popover maximised title",
25698         "minHeaderTitle" : "Popover minimized title"
25699     }
25700 }            
25701              * </pre>
25702              * 
25703              * @returns
25704              * Schema for the validation of the below JSON definition
25705              * <table class="schemaTable">
25706              * <thead>
25707              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
25708              * </thead>
25709              * <tbody>
25710              * <tr><td>icon</td><td colspan='2'>Object</td><td>A icon that should be displayed in the Popover. It should be defined as below:</td></tr>
25711              * <tr><td class='pad30'>type</td><td>Enum</td><td>collab-icon</td>
25712              * <td>Takes either <i>collab-icon</i> or <i>url</i> as a value. 
25713              * <BR><i>collab-icon</i> applies a <a href="finesse.digital.ChannelSchema.html#cdIcons">stock icon</a>
25714              * <BR><i>url</i> applies a custom icon supplied by gadget. The icon could be located as part of gadget files in Finesse.</td></tr>
25715              * <tr><td class='pad30'>value</td><td>String</td><td>Chat</td><td>The <a href="finesse.digital.ChannelSchema.html#cdIcons">name</a> of the stock icon 
25716              * or the url of the custom icon</td></tr>
25717              * <tr><td>content [Optional]</td><td colspan='2'>Array</td><td>First six name/value pairs is shown in popover. A single property should be defined as below:</td></tr>
25718              * <tr><td class='pad30'>name</td><td>String</td><td>Customer Name</td><td>The property name that is displayed on the left</td></tr>
25719              * <tr><td class='pad30'>value</td><td>String</td><td>Michael Littlefoot</td><td>The corresponding property value that is displayed on the right. 
25720              * <BR>Note: For long property values, a tooltip is displayed.</td></tr>
25721              * <tr><td>headerContent</td><td colspan='2'>Object</td><td>The title of popover when it is shown or minimized. It should be defined as below</td></tr>
25722              * <tr><td class='pad30'>maxHeaderTitle</td><td>String</td><td>Chat from firstName lastName</td><td>Popover title when it is not minimized</td></tr>
25723              * <tr><td class='pad30'>minHeaderTitle</td><td>String</td><td>firstName</td><td>Popover title when it is minimized</td></tr> 
25724              * 
25725              * </tbody>
25726              * </table>
25727              * 
25728              */
25729             getBannerDataSchema: function () {
25730                 return _bannerDataSchema;
25731             },
25732 
25733             /**
25734              * 
25735              * @example
25736              * <BR><BR><b>Example JSON for <i>TimerData</i>:</b>
25737              * <pre class='inset'>
25738 {
25739     "displayTimeoutInSecs": 30, // mandatory. minimum is 3 and maximum is 3600. -1 indicates no upper limit
25740     "display": true, // false means no displayable UI for timer
25741     "counterType": COUNT_UP or COUNT_DOWN,
25742 }
25743              * </pre>
25744              * @returns
25745              * <table class="schemaTable">
25746              * <thead>
25747              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
25748              * </thead>
25749              * <tbody>
25750              * <tr><td>displayTimeoutInSecs [Mandatory]</td><td>Integer</td><td>30</td><td>Minimum is 3 and maximum is 3600. -1 indicates no upper limit</td></tr>
25751              * <tr><td>display</td><td>boolean</td><td>true</td><td>false indicates not to display any timer </td></tr>
25752              * <tr><td>counterType</td><td>enum</td><td>COUNT_UP</td><td>Takes value COUNT_UP or COUNT_DOWN. For scenarios like how long the chat has been active would require a COUNT_UP timer. 
25753              *  On the other hand before chat is autoclosed for a agent RONA, it would be apt to use a COUNT_DOWN timer. </td></tr>
25754              * 
25755              * </tbody>
25756              * </table>
25757              */
25758             getTimerDataSchema: function () {
25759                 return _timeDataSchema;
25760             },
25761 
25762 
25763             /**
25764              * 
25765              * 
25766              * @example
25767              * <BR><BR><b>Example JSON for <i>ActionData</i>:</b>
25768              * <pre class='inset'>
25769 { 
25770     "dismissible": true, // or "false"
25771     "clientIdentifier" : 'popup1', // A string to uniquely identify a specific popover
25772     "requiredActionText": "Please answer the call from your phone",
25773     "buttons": // Optional. Max 2  
25774     [
25775         {
25776             "id": "No",
25777             "label": "Decline",
25778             "type": "Decline",
25779             "hoverText": "",
25780             "confirmButtons": [ // confirmButtons is an optional property in actionData
25781                 {
25782                     "id": "Yes",
25783                     "label": "Reject - Return to campaign",
25784                     "hoverText": ""
25785                 },
25786                 {
25787                     "id": "No",
25788                     "label": "Close - Remove from campaign",
25789                     "hoverText": ""
25790                 }
25791             ]
25792         }
25793     ]
25794 }
25795              * </pre>
25796              * 
25797              * @returns
25798              * Schema for the validation of the below JSON definition
25799              * 
25800              * <table class="schemaTable">
25801              * <thead>
25802              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
25803              * </thead>
25804              * <tbody>
25805              * <tr><td>dismissible</td><td>boolean</td><td>true</td><td>True if button definition is optional. Flase if button definition is mandatory.</td></tr>
25806              * <tr><td>clientIdentifier</td><td>string</td><td>popover1</td><td>A unique identifier across all popovers. 
25807              * This is used in the callback for popover events</td></tr>
25808              * <tr><td>requiredActionText [Optional]</td><td>String</td><td>Please answer the call from your phone</td><td>This text is at the bottom of the popover to inform what action the user has to take.</td></tr>
25809              * <tr><td>buttons [Optional]</td><td colspan='2'>Array</td><td>Buttons displayed in the popover. Maximum 2 buttons. It can be defined as below:</td></tr>
25810              * <tr><td class='pad30'>id</td><td>string</td><td>ok1</td><td> A unique ID that represents a button </td></tr>
25811              * <tr><td class='pad30'>label</td><td>string</td><td>Accept</td><td>The display text of the action button</td></tr>
25812              * <tr><td class='pad30'>type</td><td>enum</td><td>Affirm</td><td>Affirm for green button. Decline for red button</td></tr>
25813              * <tr><td class='pad30'>hoverText [Optional]</td><td>String</td><td>Click here to accept chat</td><td>The tooltip that is displayed on mouse hover</td></tr>
25814              * <tr><td class='pad30'>confirmButtons [Optional]</td><td colspan='2'>Object</td><td>An additional confirmation message with the buttons to be displayed when the user clicks on the above action button. It can be defined as below: </td></tr>
25815              * <tr><td class='pad60'>id</td><td>string</td><td>id</td><td>Id of the confirmation button</td></tr>
25816              * <tr><td class='pad60'>label</td><td>string</td><td>Reject - Return to campaign</td><td>Label to displayed on the button</td></tr>
25817              * <tr><td class='pad60'>hoverText</td><td>string</td><td>Click here to reject<td>Tooltip message on the button</td></tr>
25818              * </tbody>
25819              * </table>
25820              */
25821             getActionDataSchema: function () {
25822                 return _actionDataSchema;
25823             }
25824         };
25825     }());
25826 
25827     window.finesse = window.finesse || {};
25828     window.finesse.containerservices = window.finesse.containerservices || {};
25829     window.finesse.containerservices.PopoverSchema = PopoverSchema;
25830 
25831     // CommonJS
25832     if (typeof module === "object" && module.exports) {
25833         module.exports = PopoverSchema;
25834     }
25835 
25836     return PopoverSchema;
25837 });
25838 /**
25839  * PopoverService provides API consists of methods that would allow a gadget to request Finesse to show popovers.
25840  *
25841  * @example
25842  *    containerServices = finesse.containerservices.ContainerServices.init();
25843  *    popoverService = finesse.containerservices.PopoverService.init(containerServices);
25844  *    popoverService.showPopover(bannerData, timerData, actionData, actionHandler);
25845  */
25846 /** The following comment is to prevent jslint errors about using variables before they are defined. */
25847 /*global window:true, define:true, finesse:true*/
25848 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
25849 define('containerservices/PopoverService',['require','exports','module','../utilities/Utilities','../clientservices/ClientServices','./PopoverSchema','../utilities/JsonValidator'],function (require, exports, module) {
25850 
25851     var PopoverService = (function () { /** @lends finesse.containerservices.PopoverService.prototype */
25852 
25853         const Utilities = require('../utilities/Utilities');
25854         const ClientService = require('../clientservices/ClientServices');
25855         const SchemaMgr = require('./PopoverSchema');
25856         const SchemaValidator = require('../utilities/JsonValidator');
25857 
25858         var
25859             /**
25860              * Open Ajax topic for the API to publish popover requests to Finesse Popover Component.
25861              * @private
25862              */
25863             _popoverTopic,
25864 
25865             /**
25866              * References to gadget's ContainerService
25867              * @private
25868              */
25869             _containerService,
25870 
25871             /**
25872              * Whether the PopoverService have been initialized or not.
25873              * @private
25874              */
25875             _inited = false,
25876 
25877             /*
25878              * References to ClientServices logger
25879              * @private
25880              */
25881             _logger = {
25882                 log: ClientService.log
25883             },
25884 
25885             /**
25886              * Popover Action enum.
25887              * @private
25888              */
25889             POPOVER_ACTION = Object.freeze({
25890                 SHOW: 'show',
25891                 DISMISS: 'dismiss',
25892                 UPDATE: 'update'
25893             }),
25894 
25895             /**
25896              * Ensure that PopoverService have been inited.
25897              * @private
25898              */
25899             _isInited = function () {
25900                 if (!_inited) {
25901                     throw new Error("PopoverService needs to be inited.");
25902                 }
25903                 return _inited;
25904             },
25905 
25906             /**
25907              * Dynamic registry object, which keeps a map of popover id and its action handler function reference.
25908              * This handler will be invoked on actions on the popover.
25909              * @private
25910              */
25911             _popoverRegistry = (function () {
25912                 var _map = {},
25913 
25914                     _add = function (id, handler) {
25915                         _map[id] = {
25916                             handler: handler
25917                         };
25918                     },
25919 
25920                     _clear = function (id) {
25921                         if (_map[id]) {
25922                             delete _map[id];
25923                         }
25924                     };
25925 
25926                 return {
25927                     add: _add,
25928                     clear: _clear,
25929                     isExist: function (id) {
25930                         return _map[id] !== undefined;
25931                     },
25932                     sendEvent: function (id, data) {
25933                         if (_map[id]) {
25934                             _map[id].handler(data);
25935                         }
25936                     }
25937                 };
25938             }()),
25939 
25940             /**
25941              * Utility function to make a subscription to a particular topic. Only one
25942              * callback function is registered to a particular topic at any time.
25943              *
25944              * @param {String} topic
25945              *     The full topic name. The topic name should follow the OpenAjax
25946              *     convention using dot notation (ex: finesse.api.User.1000).
25947              * @param {Function} handler
25948              *     The function that should be invoked with the data when an event
25949              *     is delivered to the specific topic.
25950              * @returns {Boolean}
25951              *     True if the subscription was made successfully and the callback was
25952              *     been registered. False if the subscription already exist, the
25953              *     callback was not overwritten.
25954              * @private
25955              */
25956             _subscribe = function (topic, handler) {
25957                 try {
25958                     return _containerService.addHandler(topic, handler);
25959                 } catch (error) {
25960                     _logger.log('PopoverService: Error while subscribing to open ajax topic : ' + topic + ', Exception:' + error.toString());
25961                     throw new Error('PopoverService: Unable to subscribe the popover topic with Open Ajax Hub : ' + error);
25962                 }
25963             },
25964 
25965             /**
25966              * Gets the id of the gadget.
25967              * @returns {number} the id of the gadget
25968              * @private
25969              */
25970             _getMyGadgetId = function () {
25971                 var gadgetId = _containerService.getMyGadgetId();
25972                 return gadgetId ? gadgetId : '';
25973             },
25974 
25975             /**
25976              * Function which processes the messages from Finesse Popover Component.
25977              * The messages received mainly for below cases,
25978              * <ul>
25979              *  <li> User Interaction with popover.
25980              *  <li> Timeout.
25981              * <ul>
25982              * The registered callbacks are invoked based on the data.
25983              *
25984              * @param {String} response
25985              *     The actual message sent by Finesse Popover Component via open ajax hub.
25986              * @private
25987              */
25988             _processResponseFromPopover = function (response) {
25989                 _logger.log('PopoverService: Received Message from PopoverComp: ' + JSON.stringify(response));
25990                 if (response) {
25991                     try {
25992                         // if the received data from topic is in text format, then parse it to JSON format
25993                         response = typeof response === 'string' ? JSON.parse(response) : response;
25994                     } catch (error) {
25995                         _logger.log('PopoverService: Error while parsing the popover message: ' + error.toString());
25996                         return;
25997                     }
25998 
25999                     // construct the response for gadget
26000                     var data = {};
26001                     data.popoverId = response.id;
26002                     data.source = response.source;
26003 
26004                     _logger.log('PopoverService: Calling gadget action handler');
26005 
26006                     // invoke the gadget's action handler to process the action taken in popover
26007                     _popoverRegistry.sendEvent(data.popoverId, data);
26008 
26009                     // clear the popover cached data, no more needed
26010                     _popoverRegistry.clear(data.popoverId);
26011                 }
26012             },
26013 
26014             /**
26015              * Validate the gadget passed JSON structure against the schema definition.
26016              *
26017              * @param {Object} bannerData
26018              *     Banner data JSON object as per the spec.
26019              * @param {Object} timerData
26020              *     Timer data JSON object as per the spec.
26021              * @param {Object} actionData
26022              *     Action data JSON object as per the spec.
26023              * @throws {Error} if the data is not as per defined format.
26024              * @private
26025              */
26026             _validateData = function (bannerData, timerData, actionData) {
26027                 var bannerDataResult = SchemaValidator.validateJson(bannerData, SchemaMgr.getBannerDataSchema());
26028                 /* if result.valid is false, then additional details about failure are contained in
26029                    result.error which contains json like below:
26030 
26031                     {
26032                         "code": 0,
26033                         "message": "Invalid type: string",
26034                         "dataPath": "/intKey",
26035                         "schemaPath": "/properties/intKey/type"
26036                     }
26037                 */
26038                 if (!bannerDataResult.valid) {
26039                     _logger.log("PopoverService: Banner data validation bannerDataResult : " + JSON.stringify(bannerDataResult));
26040                     throw new Error("PopoverService: Banner data structure is not in expected format. Refer finesse client logs for more details.");
26041                 }
26042                 var timerDataResult = SchemaValidator.validateJson(timerData, SchemaMgr.getTimerDataSchema());
26043                 if (!timerDataResult.valid) {
26044                     _logger.log("PopoverService: Timer data validation timerDataResult : " + JSON.stringify(timerDataResult));
26045                     throw new Error("PopoverService: Timer data structure is not in expected format. Refer finesse client logs for more details.");
26046                 }
26047                 var actionDataResult = SchemaValidator.validateJson(actionData, SchemaMgr.getActionDataSchema());
26048                 if (!actionDataResult.valid) {
26049                     _logger.log("PopoverService: Action data validation actionDataResult : " + JSON.stringify(actionDataResult));
26050                     throw new Error("PopoverService: Action data structure is not in expected format. Refer finesse client logs for more details.");
26051                 }
26052             },
26053 
26054             /**
26055              * Parse the data for JSON object.
26056              *
26057              * @param {String} data
26058              *     popover data structure.
26059              * @throws {Error} if the JSON parsing fails.
26060              * @private
26061              */
26062             _getJSONObject = function (data) {
26063                 if (data) {
26064                     try {
26065                         // parse the data as user may pass in string or object format
26066                         data = typeof data === 'string' ? JSON.parse(data) : data;
26067                     } catch (err) {
26068                         throw new Error("PopoverService: Data Parsing Failed. Popover data not in expected format. Error: " + err.toString());
26069                     }
26070                     return data;
26071                 }
26072             },
26073 
26074             /**
26075              * Requests Finesse to show notification popover with the given payload. The user interaction or timeout of the popover
26076              * will be notified to gadget through the registered action handler.
26077              *
26078              * @param {Boolean} isExistingPopover
26079              *     If Update or new Show operation .
26080              * @param {String} popoverId
26081              *     Popover Id
26082              * @param {Object} payload
26083              *     Action data JSON object as per the spec.
26084              * @param {Function} actionHandler
26085              *     Callback function invoked when the user interacts with the popover or popover times out.
26086              * @throws Error
26087              *     Throws error when the passed in popoverData is not as per defined format.
26088              */
26089             _display = function (isExistingPopover, popoverId, payload, actionHandler) {
26090                 // subscribe this popover topic with open ajax hub for processing the response from popover component
26091                 _subscribe(payload.topic, _processResponseFromPopover);
26092 
26093                 // cache the gadget action handler, so that it will be used when processing the popover response
26094                 if (!isExistingPopover) _popoverRegistry.add(popoverId, actionHandler);
26095 
26096                 _logger.log('PopoverService: Sending popover show request to Finesse Popover Component: ' + JSON.stringify(payload));
26097 
26098                 // publish the popover show request to popover UI component
26099                 _containerService.publish(_popoverTopic, payload);
26100             },
26101 
26102             /**
26103              * Requests Finesse to dismiss the notification popover.
26104              *
26105              * @param {String} popoverId
26106              *     Popover id which was returned from showPopover call
26107              * @throws Error
26108              *     Throws error if the service is not yet initialized.
26109              */
26110             _dismiss = function (popoverId) {
26111                 // check whether the service is inited
26112                 _isInited();
26113 
26114                 _logger.log('PopoverService: Received popover dismiss request for popover id ' + popoverId);
26115 
26116                 if (popoverId && _popoverRegistry.isExist(popoverId)) {
26117                     // construct the payload required for the popover component
26118                     var payload = {
26119                         id: popoverId,
26120                         action: POPOVER_ACTION.DISMISS
26121                     };
26122 
26123                     // publish the popover dismiss request to popover UI component
26124                     _containerService.publish(_popoverTopic, payload);
26125 
26126                     // clear the popover cached data, no more needed
26127                     _popoverRegistry.clear(popoverId);
26128                 }
26129             };
26130 
26131         return {
26132             /**
26133              * @class
26134              * Finesse Voice component and Gadget(s) hosting digital services require 
26135              * the {@link finesse.containerservices.PopoverService} to display a popup
26136              * for incoming call and chat events. <BR>
26137              * This API provides makes use of the schema as defined in {@link finesse.containerservices.PopoverSchema} 
26138              * and provides various operations for managing the popup.
26139              * 
26140              * <style>
26141              *   .popoverTable tr:nth-child(even) { background-color:#EEEEEE; }
26142              *   .popoverTable th { background-color: #999999; }
26143              *   .popoverTable th, td { border: none; }
26144              *   .pad30 {padding-left: 30px;}
26145              *   .inset {
26146              *          padding: 5px;
26147              *          border-style: inset; 
26148              *          background-color: #DDDDDD; 
26149              *          width: auto; 
26150              *          text-shadow: 2px 2px 3px rgba(255,255,255,0.5); 
26151              *          font: 12px arial, sans-serif; 
26152              *          color:rebeccapurple;
26153              *    } 
26154              * 
26155              * 
26156              * </style>
26157              *
26158              * @example
26159              * <BR><BR><b>Example demonstrating initialization</b>
26160              * 
26161 <pre class='inset'>      
26162     containerServices = finesse.containerservices.ContainerServices.init();
26163     popoverService = finesse.containerservices.PopoverService.init(containerServices);
26164     popoverService.showPopover(bannerData, timerData, actionData, actionHandler);
26165 </pre> 
26166              *    Details of the parameters are described in this API spec below.
26167              *    
26168              * @constructs
26169              */
26170             _fakeConstuctor: function () {
26171                 /* This is here so we can document init() as a method rather than as a constructor. */
26172             },
26173 
26174             /**
26175              * Initialize the PopoverService for use in gadget.
26176              * See example in <i> Class Detail </i> for initialization.
26177              *
26178              * @param {finesse.containerservices.ContainerServices} ContainerService instance. 
26179              * @returns {finesse.containerservices.PopoverService} instance.
26180              */
26181             init: function (containerService) {
26182                 if (!_inited) {
26183                     if (!containerService) {
26184                         throw new Error("PopoverService: Invalid ContainerService reference.");
26185                     }
26186                     _containerService = containerService;
26187                     _popoverTopic = containerService.Topics.FINEXT_POPOVER_EVENT;
26188                     window.finesse.clientservices.ClientServices.setLogger(window.finesse.cslogger.ClientLogger);
26189                     _inited = true;
26190                 }
26191                 return this;
26192             },
26193 
26194             /**
26195              * Combines multiple parameters and generates a single payload for use by the popover service.
26196              *
26197              * @param {Boolean} isExistingPopover
26198              *     True if this popover already exists. False if not.
26199              * @param {String} popoverId
26200              *     The unique identifier for the popover. This id was returned from showPopover call.
26201              * @param {Object} bannerData
26202              *     Refer {@link finesse.containerservices.PopoverSchema#getBannerDataSchema} for description about this JSON payload
26203              * @param {Object} timerData
26204              *     Refer {@link finesse.containerservices.PopoverSchema#getTimerDataSchema} for description about this JSON payload
26205              * @param {Object} actionData
26206              *     Refer {@link finesse.containerservices.PopoverSchema#getActionDataSchema} for description about this JSON payload
26207              * @throws 
26208              *     Throws error when the passed in popoverData is not as per defined format.
26209              */
26210             generatePayload: function (isExistingPopover, popoverId, bannerData, timerData, actionData) {
26211                 // parse the data
26212                 bannerData = _getJSONObject(bannerData);
26213                 timerData = _getJSONObject(timerData);
26214                 actionData = _getJSONObject(actionData);
26215                 var topic = _popoverTopic + '.' + popoverId;
26216 
26217                 // validate the data
26218                 _validateData(bannerData, timerData, actionData);
26219 
26220                 var action = isExistingPopover ? POPOVER_ACTION.UPDATE : POPOVER_ACTION.SHOW;
26221                 // construct the payload required for the popover component
26222                 var payload = {
26223                     id: popoverId,
26224                     topic: topic,
26225                     action: action,
26226                     data: {
26227                         bannerData: bannerData,
26228                         timerData: timerData
26229                     }
26230                 };
26231 
26232                 if (actionData) {
26233                     payload.data.actionData = actionData;
26234                 }
26235 
26236                 return payload;
26237             },
26238 
26239             /**
26240              * 
26241              * Display a popover with the given data. The user interaction or timeout of the popover
26242              * will be notified to gadget through the registered action handler.
26243              *
26244              * @param {Object} bannerData
26245              *     Refer {@link finesse.containerservices.PopoverSchema#getBannerDataSchema} for description about this JSON payload
26246              * @param {Object} timerData
26247              *     Refer {@link finesse.containerservices.PopoverSchema#getTimerDataSchema} for description about this JSON payload
26248              * @param {Object} actionData
26249              *     Refer {@link finesse.containerservices.PopoverSchema#getActionDataSchema} for description about this JSON payload
26250              * @param {Function} actionHandler
26251              *   This is a Handler that gets called for events due to user interaction. Following are the params of this function.
26252              *   <table class="popoverTable">
26253              *   <thead><tr><th>Parameter</th><th>Description</th></tr></thead>
26254              *   <tbody>
26255              *   <tr><td>popoverId</td><td>A unique popover id that was assigned to the new popover.</td></tr>
26256              *   <tr><td>source</td><td>The id of the source which generated the event. Examples are 'btn_[id]_click' or 'dismissed' or 'timeout'</td></tr>
26257              *   </table>
26258              *   
26259              * @returns {String} 
26260              *     Generated Popover-id, can be used for subsequent interaction with the service.
26261              * @throws 
26262              *     Throws error when the passed in popoverData is not as per defined format.
26263              */
26264             showPopover: function (bannerData, timerData, actionData, actionHandler) {
26265                 // check whether the service is inited
26266                 _isInited();
26267 
26268                 var isExistingPopover = false; 
26269 
26270                 // construct a unique id for the popover
26271                 var popoverId = Utilities.generateUUID(),
26272                     gadgetId = _getMyGadgetId();
26273 
26274                 _logger.log('PopoverService: Received new popover show request from gadgetId ' + gadgetId);
26275 
26276                 var payload = this.generatePayload(isExistingPopover, popoverId, bannerData, timerData, actionData);
26277 
26278                 // check for action handler
26279                 if (typeof actionHandler !== 'function') {
26280                     throw new Error('PopoverService: Action handler should be a function');
26281                 }
26282 
26283                 _display(isExistingPopover, popoverId, payload, actionHandler);
26284 
26285                 return popoverId;
26286             },
26287 
26288             /**
26289              * 
26290              * Modify an active popover's displayed content.
26291              * 
26292              * @param {String} popoverId
26293              *     A unique popover id that was returned in {@link #showPopover}.
26294              * @param {Object} bannerData
26295              *     Refer {@link finesse.containerservices.PopoverSchema#getBannerDataSchema} for description about this JSON payload
26296              * @param {Object} timerData
26297              *     Refer {@link finesse.containerservices.PopoverSchema#getTimerDataSchema} for description about this JSON payload
26298              * @param {Object} actionData
26299              *     Refer {@link finesse.containerservices.PopoverSchema#getActionDataSchema} for description about this JSON payload
26300              * @param {Function} actionHandler
26301              *     Refer The callback function requirements as described in {@link #showPopover}
26302              * @throws 
26303              *     Throws error when the passed in popoverData is not as per defined format.
26304              */
26305             updatePopover: function (popoverId, bannerData, timerData, actionData) {
26306                 // check whether the service is inited
26307                 _isInited();
26308 
26309                 var isExistingPopover = true; 
26310 
26311                 var gadgetId = _getMyGadgetId();
26312 
26313                 _logger.log('PopoverService: Received an update popover request from gadgetId ' + gadgetId);
26314 
26315                 var payload = this.generatePayload(isExistingPopover, popoverId, bannerData, timerData, actionData);
26316 
26317                 _display(isExistingPopover, popoverId, payload, null);
26318             },
26319 
26320             /**
26321              * Dismisses the notification popover.
26322              *
26323              * @param {String} popoverId
26324              *     The unique identifier for the popover. This id was returned from showPopover call.
26325              * @throws
26326              *     Throws error if the service is not yet initialized.
26327              */
26328             dismissPopover: function (popoverId) {
26329                 _dismiss(popoverId);
26330             },
26331 
26332             /**
26333              * ENUM: Determines how the time in popover should update.
26334              * [COUNT_UP, COUNT_DOWN]
26335              */
26336             COUNTER_TYPE: Object.freeze({
26337                 COUNT_UP: 'COUNT_UP',
26338                 COUNT_DOWN: 'COUNT_DOWN'
26339             })
26340         };
26341     }());
26342 
26343     window.finesse = window.finesse || {};
26344     window.finesse.containerservices = window.finesse.containerservices || {};
26345     window.finesse.containerservices.PopoverService = PopoverService;
26346 
26347     // CommonJS
26348     if (typeof module === "object" && module.exports) {
26349         module.exports = PopoverService;
26350     }
26351 
26352     return PopoverService;
26353 });
26354 /**
26355  * UCCX Email/Chat uses a JSON payload for to trigger workflow.
26356  * That payload has to conform to this schema.
26357  * This schema has been defined as per http://json-schema.org/
26358  *
26359  * @see utilities.JsonValidator
26360  */
26361 /** The following comment is to prevent jslint errors about using variables before they are defined. */
26362 /*global window:true, define:true*/
26363 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
26364 define('workflow/WorkflowSchema',['require','exports','module'],function (require, exports, module) {
26365 
26366     var WorkflowSchema = (function () {
26367         var _workflowParamSchema = {
26368             "$schema": "http://json-schema.org/draft-06/schema#",
26369             "additionalProperties": false,
26370             "properties": {
26371                 "mediaType": {
26372                     "type": "string",
26373                     "title": "media type , eg. email, chat etc."
26374                 },
26375                 "dialogId": {
26376                     "type": "string",
26377                     "title": "The Workflow requestId. this is will be used for logging purpose "
26378                 },
26379                 "state": {
26380                     "type": "string",
26381                     "title": "The When to performa ction Schema e.g. EMAIL_PRESENTED, EMAIL_READ etc"
26382                 },
26383                 "taskVariables": {
26384                     "type": "object"
26385                 }
26386             },
26387             "required": [
26388                 "dialogId",
26389                 "state",
26390                 "mediaType"
26391             ]
26392         };
26393 
26394         return {
26395             _fakeConstuctor: function () {
26396                 /* This is here so we can document init() as a method rather than as a constructor. */
26397             },
26398 
26399             /**
26400              * Returns schema that is applicable for workflow param JSON Structure.
26401              */
26402             getWorkflowParamSchema: function () {
26403                 return _workflowParamSchema;
26404             }
26405         };
26406     }());
26407 
26408     window.finesse = window.finesse || {};
26409     window.finesse.workflow = window.finesse.workflow || {};
26410     window.finesse.workflow.WorkflowSchema = WorkflowSchema;
26411 
26412     // CommonJS
26413     if (typeof module === "object" && module.exports) {
26414         module.exports = WorkflowSchema;
26415     }
26416 
26417     return WorkflowSchema;
26418 });
26419 
26420 /**
26421  * WorkflowService provides API consists of methods that would allow a gadgets to submit workflow task.
26422  *
26423  * @example
26424  *    var containerServices = finesse.containerservices.ContainerServices.init();
26425  *    workflowService = finesse.workflow.WorkflowService.init(containerServices);
26426  *    var payload = {
26427             "dialogId": "email1",
26428             "mediaType": "email",
26429             "state": "EMAIL_READ",
26430             "taskVariables": {
26431                 "from": "mme@cisco.com",
26432                 "cc": "yyy@cisco.com"
26433             }
26434         }
26435  *    workflowService.submitTask(payload);
26436  */
26437 /** @private */
26438 define('workflow/WorkflowService',['require','exports','module','./WorkflowSchema','../utilities/JsonValidator','../clientservices/ClientServices'],function (require, exports, module) {
26439     /** @lends finesse.workflow.WorkflowService.prototype */
26440 
26441     var WorkflowService = (function () {
26442         var WorkflowSchema = require('./WorkflowSchema'),
26443             SchemaValidator = require('../utilities/JsonValidator'),
26444             ClientService = require('../clientservices/ClientServices'),
26445             /**
26446              * References to ContainerService
26447              * @private
26448              */
26449             _containerService,
26450 
26451             /**
26452              * Whether the WorkflowService have been initialized or not.
26453              * @private
26454              */
26455             _inited = false,
26456 
26457             /**
26458              * References to ClientServices logger
26459              * @private
26460              */
26461             _logger = {
26462                 log: ClientService.log
26463             },
26464 
26465             /**
26466              * Ensure that WorkflowService have been initiated.
26467              * @private
26468              */
26469             _isInitiated = function () {
26470                 if (!_inited) {
26471                     throw new Error("WorkflowService needs to be initiated.");
26472                 }
26473             },
26474 
26475             /**
26476              * Gets the id of the gadget.
26477              * @returns {number} the id of the gadget
26478              * @private
26479              */
26480             _getMyGadgetId = function () {
26481                 var gadgetId = _containerService.getMyGadgetId();
26482                 return gadgetId || '';
26483             },
26484 
26485             /**
26486              * Validate the workflow parameter payload structure..
26487              *
26488              * @param {Object} payload
26489              *     workflow parameter.
26490              * @throws {Error} if the payload is not in defined format.
26491              * @private
26492              */
26493             _validateWorkflowParam = function (payload) {
26494                 return SchemaValidator.validateJson(payload, WorkflowSchema.getWorkflowParamSchema());
26495             },
26496 
26497             /**
26498              * Function which processes the the trigger workflow for digital channels.
26499              *
26500              * @param {Object} payload
26501              *     The payload containing workflow submit parameter.
26502              * @private
26503              */
26504             _processWorkflowRequest = function (payload) {
26505                 var result = _validateWorkflowParam(payload), data;
26506 
26507                 if (!result.valid) {
26508                     _logger.log("WorkflowService: Finesse workflow trigger API parameter  Validation result : " + JSON.stringify(result));
26509                     throw new Error("workflow parameter structure is not in expected format. Refer finesse client logs for more details.");
26510                 }
26511 
26512                 _logger.log("[WorkflowService] workflow task submitted for Gadget : " + _getMyGadgetId() + ' : Workflow dialogId: ' + payload.dialogId);
26513                 data = {gadgetId: _getMyGadgetId(), payload: payload};
26514 
26515                 _containerService.publish("finesse.workflow.digitalChannels", data);
26516             };
26517 
26518         return {
26519 
26520             /**
26521              * Method to trigger workflow for digital channels.
26522              *
26523              * @example
26524              * var containerServices = finesse.containerservices.ContainerServices.init();
26525              * workflowService = finesse.workflow.WorkflowService.init(containerServices);
26526              * var payload = {
26527                     "dialogId": "email1",
26528                     "mediaType": "email",
26529                     "state": "EMAIL_READ",
26530                     "taskVariables": {
26531                         "from": "mme@cisco.com",
26532                         "cc": "yyy@cisco.com"
26533                     }
26534                 }
26535              * workflowService.submitTask(payload);
26536              *
26537              * @param {Object} payload
26538              *
26539              */
26540             submitTask: function (payload) {
26541                 _isInitiated();
26542                 _processWorkflowRequest(payload);
26543             },
26544             /**
26545              * @class
26546              * WorkflowService provides API consists of methods that would allow a gadgets to submit workflow task.
26547              *
26548              * @example
26549              *    var containerServices = finesse.containerservices.ContainerServices.init();
26550              *    workflowService = finesse.workflow.WorkflowService.init(containerServices);
26551              *    workflowService.submitTask(payload);
26552              *
26553              * @constructs
26554              */
26555             _fakeConstuctor: function () {
26556                 /* This is here so we can document init() as a method rather than as a constructor. */
26557             },
26558 
26559             /**
26560              *
26561              * Initialize WorkflowService.
26562              *
26563              * @example
26564              * var containerServices = finesse.containerservices.ContainerServices.init();
26565              * workflowService = finesse.workflow.WorkflowService.init(containerServices);
26566              *
26567              * @returns {finesse.workflow.WorkflowService} instance.
26568              *
26569              */
26570             init: function (containerServices) {
26571                 _logger.log("WorkflowService is getting initiated.....");
26572                 if (!_inited) {
26573                     if (!containerServices) {
26574                         throw new Error("WorkflowService: Invalid ContainerService reference.");
26575                     }
26576                     _inited = true;
26577                     _containerService = containerServices;
26578                     window.finesse.clientservices.ClientServices.setLogger(window.finesse.cslogger.ClientLogger);
26579                 }
26580                 return this;
26581             }
26582         };
26583     }());
26584 
26585     window.finesse = window.finesse || {};
26586     window.finesse.workflow = window.finesse.workflow || {};
26587     window.finesse.workflow.WorkflowService = WorkflowService;
26588 
26589     // CommonJS
26590     if (typeof module === "object" && module.exports) {
26591         module.exports = WorkflowService;
26592     }
26593 
26594     return WorkflowService;
26595 });
26596 
26597 
26598 /**
26599  * JavaScript representation of the Finesse UserTeamMessages object for a Supervisor.
26600  *
26601  * @requires Class
26602  * @requires finesse.FinesseBase
26603  * @requires finesse.restservices.RestBase
26604  * @requires finesse.utilities.Utilities
26605  * @requires finesse.restservices.TeamMessage
26606  */
26607 
26608 /** The following comment is to prevent jslint errors about 
26609  * using variables before they are defined.
26610  */
26611 /*global Exception */
26612 
26613 /** @private */
26614 define('restservices/UserTeamMessages',[
26615     'restservices/RestCollectionBase',
26616     'restservices/RestBase',
26617     'utilities/Utilities',
26618     'restservices/TeamMessage',
26619     'restservices/TeamMessages'
26620 ],
26621 function (RestCollectionBase, RestBase,Utilities, TeamMessage, TeamMessages) {
26622     
26623     var UserTeamMessages = TeamMessages.extend({
26624     
26625       /**
26626        * @class
26627        * JavaScript representation of a LayoutConfig object for a Team. Also exposes
26628        * methods to operate on the object against the server.
26629        *
26630        * @param {Object} options
26631        *     An object with the following properties:<ul>
26632        *         <li><b>id:</b> The id of the object being constructed</li>
26633        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
26634        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
26635        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
26636        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
26637        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
26638        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
26639        *             <li><b>content:</b> {String} Raw string of response</li>
26640        *             <li><b>object:</b> {Object} Parsed object of response</li>
26641        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
26642        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
26643        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
26644        *             </ul></li>
26645        *         </ul></li>
26646        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
26647        * @constructs
26648        **/
26649       init: function (options) {
26650     	  var options = options || {};
26651     	  options.parentObj = this;
26652     	  this._super(options);
26653       },
26654     
26655       /**
26656        * @private
26657        * Gets the REST class for the current object - this is the TeamMessages class.
26658        * @returns {Object} The LayoutConfigs class.
26659        */
26660       getRestClass: function () {
26661           return TeamMessages;
26662       },
26663 
26664       getRestItemClass: function () {
26665             return TeamMessage;
26666         },
26667 
26668 	  getRestType: function () {
26669 		return "TeamMessages";
26670 	  }, 
26671     
26672       /**
26673        * @private
26674        * Override default to indicate that this object doesn't support making
26675        * requests.
26676        */
26677       supportsRequests: false,
26678     
26679       /**
26680        * @private
26681        * Override default to indicate that this object doesn't support subscriptions.
26682        */
26683       supportsSubscriptions: false,
26684 
26685       supportsRestItemSubscriptions: false,
26686     
26687       getTeamMessages: function (createdBy, handlers) {
26688 
26689           var self = this, contentBody, reasonCode, url;
26690           contentBody = {};
26691           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
26692           handlers = handlers || {};
26693           this.restRequest('/finesse/api/'+this.getRestType()+'?createdBy='+createdBy, {
26694               method: 'GET',
26695               success: function(rsp) {
26696             	  var teamMessage = rsp.object.teamMessages? rsp.object.teamMessages.TeamMessage: null
26697             	  handlers.success(teamMessage);
26698               },
26699 	          error: function (rsp) {
26700 	              handlers.error(rsp);
26701 	          },
26702 	          content: contentBody
26703           });
26704 
26705           return this; // Allow cascading
26706       },
26707       
26708      /**
26709       * 
26710       */
26711       deleteTeamMessage: function (messageId, handlers) {
26712           handlers = handlers || {};
26713           this.restRequest(this.getRestItemBaseUrl() +'/'+messageId, {
26714               method: 'DELETE',
26715               success: handlers.success,
26716               error: handlers.error,
26717               content: undefined
26718           });
26719           return this; // Allow cascading
26720       }
26721       
26722       });
26723         
26724     window.finesse = window.finesse || {};
26725     window.finesse.restservices = window.finesse.restservices || {};
26726     window.finesse.restservices.UserTeamMessages = UserTeamMessages;
26727       
26728     return UserTeamMessages;
26729 });
26730 /**
26731  * Utilities static class which provides utility methods for shortcut key  . 
26732  */
26733 /** @private */
26734 define('shortcutkey/Utilities',[],function () {
26735 
26736 	var Utilities = (function () {
26737 		return {
26738 			CONSTANTS: {
26739 				MODIFIER_KEYS_CTRL_SHIFT: 'ctrlKey+shiftKey',
26740 				MODIFIER_KEYS_ALT_SHIFT: 'altKey+shiftKey',
26741 				MODIFIER_KEYS_CTRL_ALT: 'ctrlKey+altKey',
26742 				MODIFIER_KEYS_SHIFT: 'shiftKey',
26743 				MODIFIER_KEYS_CTRL: 'ctrlKey',
26744 				MODIFIER_KEYS_ALT: 'altKey',
26745 				ACTIVE_TAB: 'activeTab',
26746 				ACTIVE_FRAME: 'activeFrame'
26747 			},
26748 			
26749 			getModifierKeys: function (keyParam) {
26750 				return keyParam.modifierKeys ? keyParam.modifierKeys.split(' ').join('') : Utilities.CONSTANTS.MODIFIER_KEYS_CTRL_SHIFT;
26751 			},
26752 			constrcutSKStoreObjKey: function (keyParam) {
26753 				var modifierKeys = this.getModifierKeys(keyParam);
26754 				return modifierKeys.replace('+', '_') + '_' + keyParam.key;
26755 			},
26756 
26757 			/**
26758 			 * 
26759 			 * @param {} keyEvent 
26760 			 * @returns keyId 
26761 			 * @example 'ctrlKey_shiftKey_60'
26762 			 */
26763 			constructIdFromKeyEvents: function (keyEvent) {
26764 
26765 				var matches = [], Const = Utilities.CONSTANTS;
26766 				
26767 				if (keyEvent.ctrlKey && keyEvent.shiftKey) {
26768 					matches.push(Const.MODIFIER_KEYS_CTRL);
26769 					matches.push(Const.MODIFIER_KEYS_SHIFT);
26770 				} else if(keyEvent.ctrlKey && keyEvent.altKey){
26771 					matches.push(Const.MODIFIER_KEYS_CTRL);
26772 					matches.push(Const.MODIFIER_KEYS_ALT);
26773 				} else if (keyEvent.altKey && keyEvent.shiftKey) {
26774 					matches.push(Const.MODIFIER_KEYS_ALT);
26775 					matches.push(Const.MODIFIER_KEYS_SHIFT);
26776 				} else if(keyEvent.ctrlKey && !keyEvent.shiftKey && !keyEvent.altKey) {
26777 					matches.push(Const.MODIFIER_KEYS_CTRL);
26778 				} else if(keyEvent.altKey && !keyEvent.shiftKey && !keyEvent.ctrlKey){
26779 					matches.push(Const.MODIFIER_KEYS_ALT);
26780 				} else if(keyEvent.shiftKey && !keyEvent.altKey && !keyEvent.ctrlKey){
26781 					matches.push(Const.MODIFIER_KEYS_SHIFT);
26782 				}
26783 				var shortcutKeyId = '';
26784 				matches.forEach(function(match){
26785 					shortcutKeyId += match + '_'
26786 				})
26787 
26788 				return shortcutKeyId + this._characterFromEvent(keyEvent);
26789 			},
26790 			/**
26791 			 * takes the event and returns the key character
26792 			 *
26793 			 * @param {Event} e
26794 			 * @return {string}
26795 			 */
26796 			_characterFromEvent: function (e) {
26797 				return String.fromCharCode(e.keyCode).toLowerCase();
26798 			},
26799 			Logger: {
26800 				log: function (params) {
26801 					//eslint-disable-next-line
26802 					if (window.finesse && window.finesse.cslogger) {
26803 						finesse.cslogger.ClientLogger.log(' [ShortcutKeyService] ' + params);
26804 					} else {
26805 						console.log(params);
26806 					}
26807 				}
26808 			},
26809 			isFinesseContainer: function(){
26810 				//eslint-disable-next-line
26811 				var container = window.finesse? window.finesse.container: null;
26812 				//eslint-disable-next-line
26813 				var gadget = window.finesse? window.finesse.gadget: null;
26814 				return container && container.Config || gadget && gadget.Config
26815 			}
26816 		};
26817 
26818 	}());
26819 	return Utilities;
26820 });
26821 /**
26822  * ShortcutKeyStore singleton class which provides API store and mange shortcut key payload . 
26823  */
26824 define('shortcutkey/ShortcutKeyStore',['require','./Utilities'],function (require) {
26825 
26826 	var ShortcutKeyStore = (function () {
26827 		var KEY_STORE = [],
26828 			Utilities = require('./Utilities'),
26829 			Const = Utilities.CONSTANTS,
26830 			Logger = Utilities.Logger,
26831 
26832 			_isKeyAlreadyExists = function (newKey) {
26833 				return KEY_STORE.filter(function (key) {
26834 					return key.accessKey === newKey;
26835 				});
26836 			},
26837 			// When gadget is reloaded , then igore the duplicate registration.
26838 			_ignoreDuplicateRegistrationOnRealodGadget = function (newKeys) {
26839 				
26840 				var duplicateRegistrations = 0;
26841 				newKeys.forEach(function (newKey) {
26842 					
26843 					KEY_STORE.forEach(function (existingKey, index) {
26844 						// if there is gadget
26845 						if(newKey.iframeId){
26846 							// If registrtion for the same key coming from same gadget then ignore the registration
26847 							if(existingKey.iframeId === newKey.iframeId && existingKey.id === newKey.id) {
26848 								Logger.log('Duplicate key registration found for gadget :' + existingKey.iframeId + ' and Key: '+existingKey.id);
26849 								duplicateRegistrations++;
26850 							}
26851 						}
26852 					});
26853 				})
26854 				return duplicateRegistrations > 0;
26855 			},
26856 			_getShortcutKeyById = function (keyId) {
26857 				return KEY_STORE.filter(function (shortcutKey) {
26858 					return shortcutKey.accessKey.toLowerCase() === keyId.toLowerCase()
26859 				});
26860 			},
26861 
26862 			_markAsConflicted = function (alreadyExistedKeys) {
26863 				alreadyExistedKeys.map(function (existingKey) {
26864 					existingKey.conflict = true;
26865 				});
26866 			},
26867 
26868 			_updateConflictField = function (accessKey, newKey) {
26869 
26870 				var alreadyExistedKeys = _isKeyAlreadyExists(accessKey);
26871 
26872 				//get al the keys with activeFrame executionScope
26873 				var allActiveFrameKeys = alreadyExistedKeys.filter(function(duplicateKey){
26874 					return duplicateKey.executionScope === Const.ACTIVE_FRAME;
26875 				});
26876 
26877 				// if all duplicate keys are within active frames , then there should not be any conflict;
26878 				if(allActiveFrameKeys.length === alreadyExistedKeys.length && newKey.executionScope ===  Const.ACTIVE_FRAME){
26879 					return;
26880 				}
26881 
26882 				//get all gadgets which are tab level of page level
26883 				var anyPageOrTabLevlGadget = alreadyExistedKeys.filter(function(duplicateKey){
26884 					return duplicateKey.executionScope === Const.ACTIVE_TAB;
26885 				})
26886 
26887 				if(anyPageOrTabLevlGadget.length > 0 && newKey.executionScope ===  Const.ACTIVE_FRAME){
26888 					newKey.conflict = true;
26889 					// mark rest of the gadget key as conflict
26890 					_markAsConflicted(alreadyExistedKeys);
26891 					return;
26892 				}
26893 
26894 				// check if key already exits	
26895 				if (alreadyExistedKeys.length > 0) {
26896 
26897 					// if there is more then one key and new registration is page level then all duplicate keys will be in conflict state
26898 					if (newKey.isPageLevel) {
26899 						newKey.conflict = true;
26900 						// mark rest of the gadget key as conflict
26901 						_markAsConflicted(alreadyExistedKeys);
26902 					} else {
26903 						// Any existing gadget is in page Level
26904 						var anyExistingPageLevelKey = alreadyExistedKeys.filter(function (existingKeys) {
26905 							return existingKeys.isPageLevel;
26906 						})
26907 
26908 						// if any already registered key gadget is page level then mark all the key as conflict including new key
26909 						if (anyExistingPageLevelKey.length > 0) {
26910 							newKey.conflict = true;
26911 							_markAsConflicted(anyExistingPageLevelKey);
26912 						} else {
26913 							// in case there is no conflict with page level gadget, then find gadgets which are in same tab
26914 							var sameTabGadgetKeys = alreadyExistedKeys.filter(function (existingKey) {
26915 								return newKey.tabId === existingKey.tabId;
26916 							});
26917 
26918 							if (sameTabGadgetKeys.length > 0) {
26919 								sameTabGadgetKeys.forEach(function(sameTabGadget){
26920 									// if keys are not registered from the same gadget. and have conflict with other gadget
26921 									if(sameTabGadget.componentName.replace(/\s/g,'') !== newKey.componentName.replace(/\s/g,'')){
26922 										// if keys are same, then mark it as conflict
26923 										if(sameTabGadget.accessKey === newKey.accessKey){
26924 											newKey.conflict = true;
26925 											_markAsConflicted(sameTabGadgetKeys);
26926 										}
26927 									} else {
26928 										// if keys are conflicting within same gadget then mark as conflict
26929 										if(newKey.iframeId === sameTabGadget.iframeId){
26930 											newKey.conflict = true;
26931 											_markAsConflicted(sameTabGadgetKeys);
26932 										}
26933 										// if even one of them has execution scope as activeTab, it should be marked as conflict
26934 										else if(sameTabGadget.executionScope === Const.ACTIVE_TAB || newKey.executionScope === Const.ACTIVE_TAB) {
26935 											newKey.conflict = true;
26936 											_markAsConflicted(sameTabGadgetKeys);
26937 										}
26938 									}
26939 								})
26940 							}
26941 						}
26942 					}
26943 				}
26944 			},
26945 
26946 			_getComponentTabID = function (componentID) {
26947 				if(window.cd.core.componentsMetaData[componentID]) {
26948 					return window.cd.core.componentsMetaData[componentID].hashVl;
26949 				}
26950 			},
26951 
26952 			_addShortcutKey = function (newKeys) {
26953 				if (Array.isArray(newKeys)) {
26954 					// When only the gadget is reloaded , then ignore the duplicate registration.
26955 					if (_ignoreDuplicateRegistrationOnRealodGadget(newKeys)) {
26956 						return;
26957 					}
26958 					newKeys.forEach(function (newKey) {
26959 						var conflict = 0;
26960 						if (newKey instanceof Object) {
26961 
26962 							var accessKey = Utilities.constrcutSKStoreObjKey(newKey);
26963 							newKey.accessKey = accessKey;
26964 							newKey.conflict = false;
26965 
26966 							if (Utilities.isFinesseContainer()) {
26967 								newKey.type = newKey.type === 'gadget' ? newKey.type : 'component';
26968 
26969 								// currently all the the finesse component which has shortcut key suports can be page level only
26970 								if (newKey.type === 'component') {
26971 									if(newKey.executionScope === Const.ACTIVE_FRAME) {
26972 										newKey.conflict = true;
26973 										Logger.log("Shortcut key for " + JSON.stringify(newKey) + " cannot have activeFrame executionScope since it is a component");
26974                                    	}
26975 									if(newKey.componentId && _getComponentTabID(newKey.componentId)) {
26976 										newKey.tabId = _getComponentTabID(newKey.componentId).substr(2);
26977 									} else {
26978 										newKey.isPageLevel = true;
26979 									}
26980 								}
26981 
26982 								if (newKey.tabId) {
26983 									newKey.tabLabel = document.querySelector('#leftNavUl [href^="#/' + newKey.tabId + '"]').getAttribute('title')
26984 								}
26985 							}
26986 
26987 							// update the conflict attribute as true if there is conflict.
26988 							_updateConflictField(accessKey, newKey);
26989 							KEY_STORE.push(newKey);
26990 
26991 							// Logic particular to finesse 
26992 							if (newKey.conflict === true && typeof (window.finesse.shortcutkey.ShortcutKeyService.updateIdentityMenu) === "function") {
26993 								window.finesse.shortcutkey.ShortcutKeyService.updateIdentityMenu(true);
26994 							}
26995 
26996 							//eslint-disable-next-line
26997 							if (window.frameElement) {
26998 								//eslint-disable-next-line
26999 								newKey.iframeId = window.frameElement.id;
27000 								//eslint-disable-next-line
27001 								if (window.gadgets && window.gadgets.util) {
27002 									//eslint-disable-next-line
27003 									var navItemRoute = window.gadgets.util.getUrlParameters()['up_navItemRoute'];
27004 									newKey.tabId = navItemRoute !== 'undefined' ? navItemRoute.substring(2, navItemRoute.length) : '';
27005 									newKey.isPageLevel = navItemRoute === 'undefined' ? true : false;
27006 									newKey.type = 'gadget';
27007 								}
27008 
27009 							}
27010 
27011 						} else {
27012 							throw Error('keys should be list of object');
27013 						}
27014 					});
27015 				} else {
27016 					Logger.log('keys should be array of objects');
27017 				}
27018 				return newKeys;
27019 			},
27020 			_getShortcutKeys = function () {
27021 				return KEY_STORE;
27022 			};
27023 
27024 		return {
27025 			_fakeConstuctor: function () {},
27026 			addShortcutKey: _addShortcutKey,
27027 			getShortcutKeys: _getShortcutKeys,
27028 			getShortcutKeyById: _getShortcutKeyById,
27029 			init: function () {
27030 				return this;
27031 			}
27032 		};
27033 
27034 	}());
27035 	return ShortcutKeyStore;
27036 });
27037 
27038 define('shortcutkey/RegistrationParamValidator',['require','./Utilities'],function(require) {
27039 
27040 	var RegistrationParamValidator = (function() {
27041 		var KEY_STORE = {},
27042 		Utilities = require('./Utilities'),
27043 		Const = Utilities.CONSTANTS,
27044 		ALLOWED_MODIFIERS = [ 
27045 			Const.MODIFIER_KEYS_CTRL_SHIFT,
27046 			Const.MODIFIER_KEYS_ALT_SHIFT, 
27047 			Const.MODIFIER_KEYS_CTRL_ALT,
27048 			Const.MODIFIER_KEYS_CTRL,
27049 			Const.MODIFIER_KEYS_SHIFT,
27050 			Const.MODIFIER_KEYS_ALT],
27051 		ALLOWED_EXECUTION_SCOPES = [ Const.ACTIVE_TAB,Const.ACTIVE_FRAME ],
27052 		RequiredRegParamFieldsSchema = ["id", "componentName", "actionName", "key", "handler" ], 
27053 		validateRequiredFields = function(param) {
27054 			RequiredRegParamFieldsSchema.forEach(function(field) {
27055 				if (!param.hasOwnProperty(field)) {
27056 					throw Error(field + " is reuired field");
27057 				}
27058 			});
27059 		};
27060 		return {
27061 			_fakeConstuctor : function() {
27062 			},
27063 			validateRequiredParameter : function(param) {
27064 				validateRequiredFields(param);
27065 				var paramFields = Object.keys(param);
27066 				paramFields.forEach(function(field) {
27067 					var value = param[field];
27068 					if (typeof value === 'string' && value.trim() === '') {
27069 						throw Error(field + " can not have empty value");
27070 					}
27071 
27072 					if (field === 'handler' && typeof value !== 'function') {
27073 						throw Error("handler must be a function");
27074 					}
27075 				});
27076 			},
27077 			validateModifireKeys : function(keyParam) {
27078 				if (ALLOWED_MODIFIERS.indexOf(Utilities
27079 						.getModifierKeys(keyParam)) === -1) {
27080 					throw Error('Invalid modifier key: '+keyParam.modifierKeys+', Allowed modifier keys: '
27081 							+ JSON.stringify(ALLOWED_MODIFIERS));
27082 				}
27083 			},
27084 			validateExecutionScopes : function(keyParam) {
27085 				if(Utilities.isFinesseContainer()){
27086 					if (ALLOWED_EXECUTION_SCOPES.indexOf(keyParam.executionScope) === -1) {
27087 						throw Error('Invalid executionScope. Allowed executionScope: '
27088 								+ JSON.stringify(ALLOWED_EXECUTION_SCOPES));
27089 					}
27090 				}
27091 			}
27092 		};
27093 
27094 	}());
27095 	return RegistrationParamValidator;
27096 });
27097 
27098 /**
27099  * ShortcutKeyService API allows component/gadget to create and execute shortcut key for any action.
27100  *
27101  */
27102 /**
27103  * The following comment is to prevent jslint errors about using variables
27104  * before they are defined.
27105  */
27106 /* global window:true, define:true */
27107 /* jslint nomen: true, unparam: true, sloppy: true, white: true */
27108 define('shortcutkey/ShortcutKeyService',['require','exports','module','./ShortcutKeyStore','./Utilities','./RegistrationParamValidator'],function (require, exports, module) {
27109 
27110 	var ShortcutKeyService = (function () {
27111 		/** @lends finesse.shortcutkey.ShortcutKeyService.prototype */
27112 
27113 		var ShortcutKeyStore = require('./ShortcutKeyStore'),
27114 			Utilities = require('./Utilities'),
27115 			Const = Utilities.CONSTANTS,
27116 			Logger = Utilities.Logger,
27117 			Validator = require('./RegistrationParamValidator'),
27118 			_initiated = false,
27119 			
27120 			_getNonConflictedShortcutKey = function (matchedRegisteredKeys) {
27121 				// get all the non conflicting keys matched with key press event key
27122 				return matchedRegisteredKeys.filter(function (shortcutKey) {
27123 					return shortcutKey.conflict !== true
27124 				});
27125 			},
27126 			_validateInputParam = function (keys, Validator) {
27127 				if(!Array.isArray(keys)){
27128 					throw Error('keys should be list of object');
27129 				}
27130 				keys.forEach(function (key) {
27131 					if (key instanceof Object) {
27132 						if (!key.modifierKeys || key.modifierKeys.trim() === '') {
27133 							key.modifierKeys = Const.MODIFIER_KEYS_CTRL_SHIFT;
27134 						}
27135 
27136 						if (Utilities.isFinesseContainer()) {
27137 							if (!key.executionScope || key.executionScope.trim() === '') {
27138 								key.executionScope = Const.ACTIVE_TAB;
27139 							}
27140 						}
27141 
27142 						Validator.validateRequiredParameter(key);
27143 						Validator.validateModifireKeys(key);
27144 						Validator.validateExecutionScopes(key);
27145 					} else {
27146 						throw Error('keys should be list of object');
27147 					}
27148 
27149 				});
27150 			},
27151 			_cloneEvent = function (keyEvent) {
27152 				return {
27153 					ctrlKey: keyEvent.ctrlKey,
27154 					altKey: keyEvent.altKey,
27155 					shiftKey: keyEvent.shiftKey,
27156 					keyCode: keyEvent.keyCode,
27157 					key: String.fromCharCode(keyEvent.keyCode),
27158 					which: keyEvent.which
27159 				};
27160 			},
27161 			_getGadget = function (iframeId) {
27162 				return document.getElementById(iframeId).contentWindow;
27163 			},
27164 			/**
27165 			 * Executes the handler for particular keyevent 
27166 			 * @param keyEvent 
27167 			 */
27168 			_executeShortcutKey = function (keyEvent) {
27169 				// Do not execute if the shortcut key feature is disabled
27170 				if (Utilities.isFinesseContainer() && window.finesse.container.Config.enableShortCutKeys === 'false') {
27171 					Logger.log("Shortcut Keys feature is disabled.");
27172 					return;
27173 				};
27174 				var keyId = Utilities.constructIdFromKeyEvents(keyEvent),
27175 					iframeId, validShortcutKeyObj,
27176 					matchedRegisteredKeys = ShortcutKeyStore.getShortcutKeyById(keyId);
27177 
27178 				if (matchedRegisteredKeys.length === 0) {
27179 					Logger.log("No key registered for " + keyId );
27180 					return;
27181 				}
27182 
27183 				// get non conflicted key only
27184 				var nonConflictedKeys = _getNonConflictedShortcutKey(matchedRegisteredKeys);
27185 				// if there is no key found without any conflict , log error.
27186 				if (nonConflictedKeys.length === 0) {
27187 					Logger.log('There are conflicts of shortcut keys: Following shortcut key will not work: ' + JSON.stringify(matchedRegisteredKeys));
27188 					return;
27189 				}
27190 				
27191 				// executionScope is only applicable for finesse based container
27192 				if (finesse.container && finesse.container.Tabs) {
27193 					const currentTab = finesse.container.Tabs.getActiveTab();
27194 					/** 
27195 					 * currentTabKeys: Stores list of shortcut keys in the current tab.
27196 					 * 3 possibilities: 
27197 					 * - current tab has one shortcut key registered with activeTab execution scope
27198 					 * - current tab has one shortcut key registered with activeFrame execution scope
27199 					 * - current tab has more than one gadgets with activeFrame execution scope
27200 					 * */  
27201 					const currentTabKeys = nonConflictedKeys.filter(function(shortcutKey) {
27202 						return shortcutKey.tabId === currentTab;
27203 					});
27204 					/** 
27205 					 * There could be more than one NON Conflicting keys if same gadget is included more than ones and
27206 					 * key executionScope is activeFrame. If there are more than one non conflicted keys then find the key of currently 
27207 					 * focussed gadget IFrame key
27208 					 */
27209 					if(nonConflictedKeys.length > 1 && currentTabKeys.length > 1){
27210 						validShortcutKeyObj = nonConflictedKeys.filter(function(matched){
27211 							return matched.iframeId === document.activeElement.id;
27212 						})[0];
27213 						
27214 					} else {
27215 						if(currentTabKeys[0]) {
27216 							validShortcutKeyObj = currentTabKeys[0];
27217 						} else {
27218 							validShortcutKeyObj = nonConflictedKeys[0];
27219 						}
27220 					}
27221 				} else {
27222 					/** 
27223 					 * There could be more than one NON Conflicting keys if same gadget is included more than ones and
27224 					 * key executionScope is activeFrame. If there are more than one non conflicted keys then find the key of currently 
27225 					 * focussed gadget IFrame key
27226 					 */
27227 					if(nonConflictedKeys.length > 1){
27228 						validShortcutKeyObj = nonConflictedKeys.filter(function(matched){
27229 							return matched.iframeId === document.activeElement.id;
27230 						})[0];
27231 					} else {
27232 						validShortcutKeyObj = nonConflictedKeys[0];
27233 					}
27234 				}
27235 				
27236 				if(!validShortcutKeyObj){
27237 					Logger.log("Shortcut key for " + JSON.stringify(keyEvent) + ", will only work if that gadget has active focus");
27238 					return;
27239 				}
27240 				iframeId = validShortcutKeyObj.iframeId;
27241 				// executionScope is only applicable for finesse based container
27242 				if (finesse.container && finesse.container.Tabs) {
27243 					/**
27244 					 * Stop the execution of shortcut key if gadget has executionScope as activeFrame and 
27245 					 * the particular gadget is not in focus
27246 					 * */
27247 					if (validShortcutKeyObj.executionScope === Const.ACTIVE_FRAME && validShortcutKeyObj.iframeId !== document.activeElement.id) {
27248 						Logger.log("Shortcut key for " + JSON.stringify(validShortcutKeyObj) + ", will only work if that gadget has active focus");
27249 						return
27250 					} else {
27251 						/**
27252 						 * Stop the execution of shortcut key if gadget has executionScope as activeTab and 
27253 						 * current active tab is not matching with the tab where gadget is placed 
27254 						 * */
27255 						if (validShortcutKeyObj.tabId !== finesse.container.Tabs.getActiveTab() && !validShortcutKeyObj.isPageLevel) {
27256 							Logger.log("Shortcut key for " + JSON.stringify(validShortcutKeyObj) + " gadget, will not work. The gadget is not present in current active tab");
27257 							return
27258 						}
27259 					}
27260 				}
27261 				
27262 				/**
27263 				 * If the shortcut key is defined for any gadget (i.e. iframe), 
27264 				 * then post the message to that iframe only to invoke the callbak method inside the iframe only.
27265 				 */
27266 				if (iframeId) {
27267 					var message = {
27268 						type: 'execute_shotcutkey_handler',
27269 						keyEvent: _cloneEvent(keyEvent)
27270 					};
27271 					Logger.log('Asking gadget to execute shortcut key handler : ' + iframeId);
27272 					_getGadget(iframeId).postMessage(message, '*');
27273 				} else {
27274 					Logger.log('Container is executing shortcut key handler for ' + validShortcutKeyObj.componentName + ' : ' + validShortcutKeyObj.actionName);
27275 					/**
27276 					 * If any shortcut key created at container level(e.g. component) then invoke the handler directly
27277 					 */
27278 					validShortcutKeyObj.handler(keyEvent);
27279 				}
27280 			},
27281 			_keyupEventHandler = function (keyEvent) {
27282 				if (_validModifierKeysPressed(keyEvent)) {
27283 					// If key is pressed with iframe in focus , send the message to parent
27284 					//eslint-disable-next-line
27285 					if (window !== top) {
27286 						var message = {
27287 							type: 'finesse_keyup_captured',
27288 							keyEvent: _cloneEvent(keyEvent)
27289 						};
27290 						//eslint-disable-next-line
27291 						parent.postMessage(message, "*");
27292 					} else {
27293 						_executeShortcutKey(keyEvent);
27294 					}
27295 				}
27296 			},
27297 			_messageHandler = function (e) {
27298 				var key = e.message ? "message" : "data";
27299 				var data = e[key];
27300 				if (data.type === 'register_shortcut_key') {
27301 					ShortcutKeyStore.addShortcutKey(JSON.parse(data.keys));
27302 				} else if (data.type == 'finesse_keyup_captured') {
27303 					_executeShortcutKey(data.keyEvent);
27304 				} else if (data.type == 'execute_shotcutkey_handler') {
27305 					var keyId = Utilities.constructIdFromKeyEvents(data.keyEvent);
27306 					var matchedRegisteredKeys = ShortcutKeyStore.getShortcutKeyById(keyId);
27307 					// get non conflicted key only
27308 					var nonConflictedKeys = _getNonConflictedShortcutKey(matchedRegisteredKeys);
27309 					if(nonConflictedKeys.length > 0){
27310 						validShortcutKeyObj = nonConflictedKeys[0];
27311 						Logger.log('Gadget is executing shortcut key handler for ' + validShortcutKeyObj.componentName + ' : ' + validShortcutKeyObj.actionName);
27312 						validShortcutKeyObj.handler(data.keyEvent);
27313 					}
27314 					
27315 				}
27316 			},
27317 			_validModifierKeysPressed = function (keyEvent) {
27318 				return ((keyEvent.ctrlKey && keyEvent.shiftKey) ||
27319 					(keyEvent.altKey && keyEvent.shiftKey) ||
27320 					(keyEvent.ctrlKey && keyEvent.altKey) ||
27321 					(keyEvent.ctrlKey) || (keyEvent.altKey) || (keyEvent.shiftKey)) && 
27322 					(keyEvent.key !== 'Shift' && keyEvent.key !== 'Control' && keyEvent.key !== 'Alt')
27323 			},
27324 			_sendKeyupEvent = function (keyEvent) {
27325 				var message = {
27326 					type: 'finesse_keyup_captured', // propagate the event to finesse container on finesse_keyup_captured topic
27327 					keyEvent: {
27328 						ctrlKey: keyEvent.ctrlKey,
27329 						altKey: keyEvent.altKey,
27330 						shiftKey: keyEvent.shiftKey,
27331 						keyCode: keyEvent.keyCode,
27332 						key: String.fromCharCode(keyEvent.keyCode),
27333 						which: keyEvent.which
27334 					}
27335 				};
27336 				//eslint-disable-next-line
27337 				parent.postMessage(message, "*");
27338 			};
27339 
27340 		return {
27341 
27342 			/**
27343 			 * @class ShortcutKeyService allows components or gadgets to create
27344 			 *        shortcut keys for any component or gadget related actions.
27345 			 * <style>
27346 			 *   .schemaTable tr:nth-child(even) { background-color:#EEEEEE; }
27347 			 *   .schemaTable th { background-color: #999999; }
27348 			 *   .schemaTable th, td { border: none; }
27349 			 *   .pad30 {padding-left: 30px;}
27350 			 *   .pad60 {padding-left: 60px;}
27351 			 *   .inset {
27352 			 *          padding: 5px;
27353 			 *          border-style: inset; 
27354 			 *          background-color: #DDDDDD; 
27355 			 *          width: auto; 
27356 			 *          text-shadow: 2px 2px 3px rgba(255,255,255,0.5); 
27357 			 *          font: 12px arial, sans-serif; 
27358 			 *          color:rebeccapurple;
27359 			 *    }
27360 			 * </style>       
27361 			 * 
27362 			 * @constructs
27363 			 */
27364 			_fakeConstuctor: function () {
27365 				/*
27366 				 * This is here so we can document init() as a method rather
27367 				 * than as a constructor.
27368 				 */
27369 			},
27370 
27371 			/**
27372 			 * This method is used to register the shortcut keys for the components or the gadgets
27373 			 * 
27374 			 * @param keys -
27375 			 *            Array of keys object:
27376 			 * @example
27377 			 * var arr = [ {
27378 			 *  "id": "cisco_callhandling_endCall"
27379              *  "componentName": "Call Control",
27380              *  "actionName": "End Call",
27381              *  "modifierKeys": "ctrlKey + shiftKey",
27382 			 *  "key": "e",
27383              *  "handler": function () {}
27384              * }];
27385                     
27386            	 * finesse.shortcutkey.ShortcutKeyService.registerShortcutKey(arr);
27387 			 * </pre>
27388 			 * <br/><br/>
27389 			 * 
27390 			 * <h3>Shortcut Key registration payload details</h3>
27391 			 * 
27392 			 * <table class="schemaTable">
27393              * <thead>
27394              * <tr><th>Attribute</th><th>Type</th><th>Description</th><th>Required</th></tr>
27395              * </thead>
27396              * <tbody>
27397              * <tr><td>id</td><td>String</td><td>A unique id generated by gadget. The pattern for this id can be companyName_gadgetId_operation. </td><td>Required</td></tr>
27398              * <tr><td>componentName</td><td>String</td><td>Name of the functionality, component, or the gadget.</td><td>Required</td></tr>
27399         	 * <tr><td>actionName</td><td>String</td><td>Name of the action or operation performed by the assigned shortcut keys.</td><td>Required</td></tr>
27400         	 * <tr><td>key</td><td>String</td><td>Actual key combined with the modifier keys. For example, Ctrl + Shift + e where Ctrl and Shift are the modifiers keys and e is the actual key.</td><td>Required</td></tr>
27401              * <tr><td>executionScope</td><td>ENUM</td><td><ul>
27402                     <li><i><b>ShortcutKeyService.CONSTANTS.EXECUTION_SCOPE.ACTIVE_TAB</b></i>: If the gadget is in the currently active tab, only then the shortcut keys of that gadget are executed.</li>
27403                     <li><i><b>ShortcutKeyService.CONSTANTS.EXECUTION_SCOPE.ACTIVE_FRAME</b></i>: If the gadget is in focus, only then the shortcut keys of that gadget are executed.</li>
27404                 </ul></td>
27405             	<td>Required</td>
27406         	 * </tr>
27407         	 * <tr><td>modifierKeys</td><td>ENUM</td><td>
27408 			 * Keybaord modifier key combinations. 
27409         	 * <br/>finesse.shortcutkey.ShortcutKeyService.CONSTANTS.MODIFIER_KEYS.* <ul><li>CTRL_SHIFT</li><li>ALT_SHIFT</li><li>CTRL_ALT</li><li>CTRL</li><li>SHIFT</li><li>ALT</li></ul></td><td>Optional</td></tr>
27410         	 * <tr><td>callback</td><td>function</td><td>Callback function that is invoked when the shortcut keys are pressed.</td><td>Required</td></tr>
27411              * </tbody>
27412              * </table>
27413 			 */
27414 			registerShortcutKey: function (keys) {
27415 
27416 				if (!_initiated) {
27417 					throw Error('ShortcutKeyService not intialized. Invoke init method to initialize');
27418 				}
27419 
27420 				// added to prevent double registration of shortcut keys during failover from autopilot iframe
27421 				if(window.frameElement && window.frameElement.id === "autopilot-frame") {
27422 					return;
27423 				}
27424 				
27425 				_validateInputParam(keys, Validator);
27426 				keys = ShortcutKeyStore.addShortcutKey(keys);
27427 				// Called from an iframe
27428 				//eslint-disable-next-line
27429 				if (window !== top) {
27430 					var message = {
27431 						type: 'register_shortcut_key',
27432 						keys: JSON.stringify(keys)
27433 					};
27434 					//eslint-disable-next-line
27435 					parent.postMessage(message, "*");
27436 				}
27437 
27438 			},
27439 			/**
27440 			 * This method allows you to get all the registered shortcut keys.
27441 			 * @returns {Array} array of objects
27442 			 */
27443 			getShorcutKeys: function () {
27444 				var registerKeys =  ShortcutKeyStore.getShortcutKeys();
27445 				return registerKeys.slice(0);
27446 			},
27447 
27448 			/**
27449 			 * @private
27450 			 * @returns {Array} of conflict keys
27451 			 */
27452 			getConflictShortcutKeys: function() {
27453 				return ShortcutKeyService.getShorcutKeys().filter(function(shortcutKey) {
27454 					return shortcutKey.conflict === true;
27455 				});
27456 			},
27457 
27458 			/**
27459 			 * This method initializes the ShortcutKeyService  for the Container or the gadgets.
27460 			 */
27461 			init: function () {
27462 				if (!_initiated) {
27463 					// Attaching keydown event listener only in case if shortcut key is used in non finesse environment
27464 					//eslint-disable-next-line
27465 					if (window.sessionStorage.getItem('finesseSkeyInitiated') !== 'true') {
27466 						// The reason to use keydown event is that, keyborad event object keyCode and charCodeAt(0) always matches
27467 						document.addEventListener('keyup', _keyupEventHandler, true);
27468 					}
27469 					//eslint-disable-next-line
27470 					window.addEventListener("message", _messageHandler, false);
27471 					_initiated = true;
27472 				}
27473 				return _initiated;
27474 			},
27475 			/**
27476 			 * If there is any custom iframe created by the gadget that is not controlled by Finesse, 
27477 			 * the Finesse shortcut key framework cannot capture the keyup event from that custom iframe to execute the shortcut keys.
27478 			 * This method can be used to send the Keyup event object to the Finesse container.
27479 			 * The keyup event object has to be captured inside the child iFrame and propagated to its immediate parent.
27480 			 * The parent again has to propagate the event to its immediate parent until the event reaches the Finesse container.
27481 			 * Once the immediate parent is the Finesse container, then use sendKeyupEvent to propagate the event to Finesse container.
27482 			 * Param object has to be serializable and cannot contain any functions.
27483 			 * 
27484 			 * @param keyEvent - Object
27485 			 * 
27486 			 * @example
27487 			 * var keyEvent = {
27488 			 *  ctrlKey: event.ctrlKey,
27489 			 *  altKey: event.altKey,
27490 			 *  shiftKey: event.shiftKey,
27491 			 *  keyCode: event.keyCode,
27492 			 *  key: event.key
27493 			 * }
27494 			 * 
27495 			 */
27496 			sendKeyupEvent: function(keyEvent){
27497 				_sendKeyupEvent(keyEvent)
27498 			},
27499 			CONSTANTS: {
27500 				MODIFIER_KEYS: {
27501 					CTRL_SHIFT: Const.MODIFIER_KEYS_CTRL_SHIFT,
27502 					ALT_SHIFT: Const.MODIFIER_KEYS_ALT_SHIFT,
27503 					CTRL_ALT: Const.MODIFIER_KEYS_CTRL_ALT,
27504 					CTRL: Const.MODIFIER_KEYS_CTRL,
27505 					SHIFT: Const.MODIFIER_KEYS_SHIFT,
27506 					ALT: Const.MODIFIER_KEYS_ALT
27507 				},
27508 				EXECUTION_SCOPE: {
27509 					ACTIVE_TAB: Const.ACTIVE_TAB,
27510 					ACTIVE_FRAME: Const.ACTIVE_FRAME
27511 				}
27512 			}
27513 
27514 		};
27515 	}());
27516 	//eslint-disable-next-line
27517 	window.finesse = window.finesse || {};
27518 	//eslint-disable-next-line
27519 	window.finesse.shortcutkey = window.finesse.shortcutkey || {};
27520 	//eslint-disable-next-line
27521 	window.finesse.shortcutkey.ShortcutKeyService = ShortcutKeyService;
27522 
27523 	// CommonJS
27524 	if (typeof module === "object" && module.exports) {
27525 		module.exports = ShortcutKeyService;
27526 	}
27527 
27528 	return ShortcutKeyService;
27529 });
27530 
27531 /**
27532  * @fileOverview This service will be initialized by the container and thereafter will be
27533  * available to the gadgets to interact with the IndexedDB cache.
27534  */
27535 
27536 define('utilities/RestCacheDataService',[], function () {
27537     var RestCacheDataService = (function () {
27538 		/** @lends finesse.utilities.DesktopCache.prototype */
27539 		var logger = window.finesse.cslogger.ClientLogger;
27540 		var listeners = {};
27541 		var group = null;
27542 		var utils = window.finesse.utilities.Utilities;
27543 		var IDB_ACTIONS = utils.IDB_ACTIONS;
27544 		var IDB_TOPICS = utils.IDB_TOPICS;
27545 
27546 		// returns the gadgetId to use as a reference
27547 		var _getGadetId = function () {
27548 			if (gadgets && gadgets.util && gadgets.util.getUrlParameters()) {
27549 				return gadgets.util.getUrlParameters().mid;
27550 			}
27551 		}
27552 
27553 		/**
27554 		 * Sends a post message from the gadget to the container to interact with the IDB cache.
27555 		 * @param requestId is a random ID generated by the gadget to identify a request to interact with the IDB cache.
27556 		 * @param action is the type of interaction, check window.finesse.utilities.Utilities.IDB_ACTIONS for a list of available actions.
27557 		 * @data is the payload to send with the request.
27558 		 */
27559 		var _sendMessage = function (requestId, action, data) {
27560 			logger.log('Sending request to gadget with gadgetid: ' + _getGadetId() + ' and requestid: ' + requestId);
27561 			var message = {};
27562 			message[IDB_TOPICS.REQUEST] = {
27563 				group: group ? group : _getGadetId(),
27564 				requestId: requestId,
27565 				gadgetId: _getGadetId(),
27566 				data: data,
27567 				action: action
27568 			};
27569 			parent.postMessage(message, '*');
27570 		}
27571 		// listens to the resonse sent by the container to the request sent by the gadgets and notifies the listeners.
27572 		window.addEventListener('message', function (e) {
27573 			if (e.data.hasOwnProperty(IDB_TOPICS.RESPONSE) && listeners[e.data[IDB_TOPICS.RESPONSE].requestId]){
27574 				var response = e.data[IDB_TOPICS.RESPONSE];
27575 				logger.log('Recieved response from gadget with gadgetid: ' + _getGadetId() + ' and requestid: ' + response.requestId);
27576 				listeners[response.requestId](response.error, response.data);
27577 				delete listeners[response.requestId];
27578 			}
27579 	    });
27580     	return {
27581 			/**
27582 			 * @class RestCacheDataService allows gadgets to cache there data locally
27583 			 * in IndexedDB that can be available across the failover on the other side without even making
27584 			 * a REST call.
27585 			 * <style>
27586 			 *   .schemaTable tr:nth-child(even) { background-color:#EEEEEE; }
27587 			 *   .schemaTable th { background-color: #999999; }
27588 			 *   .schemaTable th, td { border: none; }
27589 			 *   .pad30 {padding-left: 30px;}
27590 			 *   .pad60 {padding-left: 60px;}
27591 			 *   .inset {
27592 			 *          padding: 5px;
27593 			 *          border-style: inset; 
27594 			 *          background-color: #DDDDDD; 
27595 			 *          width: auto; 
27596 			 *          text-shadow: 2px 2px 3px rgba(255,255,255,0.5); 
27597 			 *          font: 12px arial, sans-serif; 
27598 			 *          color:rebeccapurple;
27599 			 *    }
27600 			 * </style>       
27601 			 * 
27602 			 * @constructs
27603 			 */
27604 			_fakeConstuctor: function () {
27605 				/*
27606 				 * This is here so we can document init() as a method rather
27607 				 * than as a constructor.
27608 				 */
27609 			},
27610 
27611 			  /**
27612     		   * Different gadgets from the same vendor can choose to keep the same group to reduce the redundancy in the data stored.
27613 			   * The group name set here will be used as a prefix to the key while saving that record.
27614     		   * @param groupName is the name which would prefix the keys while saving in the cache.
27615     		   */
27616 			  setGroup: function (groupName) {
27617 				  if(!group) {
27618 					group = groupName;
27619 				  } else {
27620 					logger.log('Group already set to ' + group + '. Cannot set again.');
27621 				  }
27622 			  },
27623     		  /**
27624     		   * Save or update a single or multiple records in the DB.
27625     		   * @param {Array} records is an array containing the data to be saved in the DB. It will always
27626     		   * be an array even if there is just one record that has to be saved/updated.
27627     		   * @param callback is a callback function to be passed as all the indexeddb operations are
27628     		   * asynchronous in nature.
27629 			   * @example 
27630 			   * 	finesse.utilities.DesktopCache.saveOrUpdateData({[
27631 			   * 		key: 'someKey'
27632 			   * 		data: 'someData'
27633 			   * 	]}, function (err, data){
27634 			   * 		if (!err) // do something
27635  			   * 	});
27636     		   */
27637     		  saveOrUpdateData: function saveOrUpdateData(records, callback) {
27638 				  var requestId = utils.generateUUID();
27639 				  listeners[requestId] = callback;
27640 				  _sendMessage(requestId, IDB_ACTIONS.SAVE_OR_UPDATE, records);
27641     		  },
27642 
27643     		  /**
27644     		   * Fetch a single or all records from DB.
27645     		   * @param key is through which one can retrieve a single record. Key should be unique, it is like a primary key.
27646     		   * @param callback is the callback to be called in case of successful/error fetching of the data
27647 			   * @example 
27648 			   * 	finesse.utilities.DesktopCache.fetchData('someKey', function (err, data){
27649 			   * 		if (!err) // do something
27650  			   * 	});
27651     		   */
27652     		  fetchData: function fetchData(key, callback) {
27653 					var requestId = utils.generateUUID();
27654 				    listeners[requestId] = callback;
27655 					_sendMessage(requestId, IDB_ACTIONS.FETCH, key);
27656     		  },
27657 
27658     		  /**
27659     		   * Delete a record from DB.
27660     		   * @param key is the Rest name which is used to identify the record to be deleted.
27661     		   * @param callback is a callback function to be passed as all the indexeddb operations are
27662     		   * asynchronous in nature.
27663     		   */
27664 			  deleteData: function _delete(key, callback) {
27665     		    var requestId = utils.generateUUID();
27666 				listeners[requestId] = callback;
27667 				_sendMessage(requestId, IDB_ACTIONS.DELETE, key);
27668 			  },
27669 
27670 			  /**
27671     		   * Clear all the records in the DB.
27672     		   * @param callback is a callback function to be passed as all the indexeddb operations are
27673     		   * asynchronous in nature.
27674     		   */
27675     		  clearData: function clearData(callback) {
27676     		    var requestId = utils.generateUUID();
27677 				listeners[requestId] = callback;
27678 				_sendMessage(requestId, IDB_ACTIONS.CLEAR_ALL);
27679     		  }
27680     	}
27681     	
27682     }());
27683     
27684     window.finesse = window.finesse || {};
27685 	window.finesse.utilities = window.finesse.utilities || {};
27686     window.finesse.utilities.DesktopCache = RestCacheDataService;
27687 	return RestCacheDataService;
27688 });
27689 
27690 define('finesse',[
27691     'restservices/Users',
27692     'restservices/Teams',
27693     'restservices/SystemInfo',
27694     'restservices/Media',
27695     'restservices/MediaDialogs',
27696     'restservices/DialogLogoutActions',
27697     'restservices/InterruptActions',
27698     'restservices/ReasonCodeLookup',
27699     'restservices/ChatConfig',
27700     'restservices/ECCVariableConfig.js',
27701     'utilities/I18n',
27702     'utilities/Logger',
27703     'utilities/SaxParser',
27704     'utilities/BackSpaceHandler',
27705     'utilities/JsonValidator',
27706     'cslogger/ClientLogger',
27707     'cslogger/FinesseLogger',
27708     'containerservices/ContainerServices',
27709     'containerservices/FinesseToaster',
27710     'interfaces/RestObjectHandlers',
27711     'interfaces/RequestHandlers',
27712     'gadget/Config',
27713     'digital/ChannelSchema',
27714     'digital/ChannelService',
27715     'containerservices/PopoverService',
27716     'containerservices/PopoverSchema',
27717     'workflow/WorkflowSchema',
27718     'workflow/WorkflowService',
27719     'restservices/UserTeamMessages',
27720     'shortcutkey/ShortcutKeyService',
27721     'utilities/RestCacheDataService'
27722 ],
27723 function () {
27724     /**
27725      * below object is set to true only if desktop url has query parameter 'bypassServerCache'
27726      * This object is being used in RestBase.js. Based on this, GET request will have a query parameter 'bypassServerCache=true' so that webproxy pass the request to finesse server
27727      */
27728 	window.finesse.restservices.bypassServerCache = (function () {
27729 		if ( window.document.referrer ) {
27730 			return window.document.referrer.indexOf('bypassServerCache=true') > -1 ? true : false;
27731 		} else {
27732 			return window.location.href.indexOf('bypassServerCache=true') > -1 ? true : false;
27733 		}
27734 		return false;
27735 	}());
27736     return window.finesse;
27737 });
27738 
27739 require(["finesse"]);
27740 return require('finesse'); }));
27741 
27742 // Prevent other JS files from wiping out window.finesse from the namespace
27743 var finesse = window.finesse;