1 /**
  2  * Cisco Finesse - JavaScript Library
  3  * Version 12.0(1)
  4  * Cisco Systems, Inc.
  5  * http://www.cisco.com/
  6  *
  7  * Portions created or assigned to Cisco Systems, Inc. are
  8  * Copyright (c) 2019 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         /**
1249          * @class
1250          * Utilities is collection of utility methods.
1251          * 
1252          * @augments finesse.restservices.RestBase
1253          * @see finesse.restservices.Contacts
1254          * @constructs
1255          */
1256         _fakeConstuctor: function () {
1257             /* This is here for jsdocs. */
1258         },
1259             
1260         /**
1261          * @private
1262          * Retrieves the specified item from window.localStorage
1263          * @param {String} key
1264          *     The key of the item to retrieve
1265          * @returns {String}
1266          *     The string with the value of the retrieved item; returns
1267          *     what the browser would return if not found (typically null or undefined)
1268          *     Returns false if window.localStorage feature is not even found.
1269          */
1270         getDOMStoreItem: function (key) {
1271             var store = window.localStorage;
1272             if (store) {
1273                 return store.getItem(key);
1274             }
1275         },
1276 
1277         /**
1278          * @private
1279          * Sets an item into window.localStorage
1280          * @param {String} key
1281          *     The key for the item to set
1282          * @param {String} value
1283          *     The value to set
1284          * @returns {Boolean}
1285          *     True if successful, false if window.localStorage is
1286          *     not even found.
1287          */
1288         setDOMStoreItem: function (key, value) {
1289             var store = window.localStorage;
1290             if (store) {
1291                 store.setItem(key, value);
1292                 return true;
1293             }
1294             return false;
1295         },
1296 
1297         /**
1298          * @private
1299          * Removes a particular item from window.localStorage
1300          * @param {String} key
1301          *     The key of the item to remove
1302          * @returns {Boolean}
1303          *     True if successful, false if not
1304          *     Returns false if window.localStorage feature is not even found.
1305          */
1306         removeDOMStoreItem: function (key) {
1307             var store = window.localStorage;
1308             if (store) {
1309                 store.removeItem(key);
1310                 return true;
1311             }
1312             return false;
1313         },
1314 
1315         /**
1316          * @private
1317          * Dumps all the contents of window.localStorage
1318          * @returns {Boolean}
1319          *     True if successful, false if not.
1320          *     Returns false if window.localStorage feature is not even found.
1321          */
1322         clearDOMStore: function () {
1323             var store = window.localStorage;
1324             if (store) {
1325                 store.clear();
1326                 return true;
1327             }
1328             return false;
1329         },
1330 
1331         /**
1332          * @private
1333          * Creates a message listener for window.postMessage messages.
1334          * @param {Function} callback
1335          *     The callback that will be invoked with the message. The callback
1336          *     is responsible for any security checks.
1337          * @param {String} [origin]
1338          *     The origin to check against for security. Allows all messages
1339          *     if no origin is provided.
1340          * @returns {Function}
1341          *     The callback function used to register with the message listener.
1342          *     This is different than the one provided as a parameter because
1343          *     the function is overloaded with origin checks.
1344          * @throws {Error} If the callback provided is not a function.
1345          */
1346         receiveMessage: function (callback, origin) {
1347             if (typeof callback !== "function") {
1348                 throw new Error("Callback is not a function.");
1349             }
1350 
1351             //Create a function closure to perform origin check.
1352             /** @private */
1353             var cb = function (e) {
1354                 // If an origin check is requested (provided), we'll only invoke the callback if it passes
1355                 if (typeof origin !== "string" || (typeof origin === "string" && typeof e.origin === "string" && e.origin.toLowerCase() === origin.toLowerCase())) {
1356                     callback(e);
1357                 }
1358             };
1359 
1360             if (window.addEventListener) { //Firefox, Opera, Chrome, Safari
1361                 window.addEventListener("message", cb, false);
1362             } else { //Internet Explorer
1363                 window.attachEvent("onmessage", cb);
1364             }
1365 
1366             //Return callback used to register with listener so that invoker
1367             //could use it to remove.
1368             return cb;
1369         },
1370 
1371         /**
1372          * @private
1373          * Sends a message to a target frame using window.postMessage.
1374          * @param {Function} message
1375          *     Message to be sent to target frame.
1376          * @param {Object} [target="parent"]
1377          *     An object reference to the target frame. Default us the parent.
1378          * @param {String} [targetOrigin="*"]
1379          *     The URL of the frame this frame is sending the message to.
1380          */
1381         sendMessage: function (message, target, targetOrigin) {
1382             //Default to any target URL if none is specified.
1383             targetOrigin = targetOrigin || "*";
1384 
1385             //Default to parent target if none is specified.
1386             target = target || parent;
1387 
1388             //Ensure postMessage is supported by browser before invoking.
1389             if (window.postMessage) {
1390                 target.postMessage(message, targetOrigin);
1391             }
1392         },
1393 
1394         /**
1395          * Returns the passed in handler, if it is a function.
1396          * @param {Function} handler
1397          *     The handler to validate
1398          * @returns {Function}
1399          *     The provided handler if it is valid
1400          * @throws Error
1401          *     If the handler provided is invalid
1402          */
1403         validateHandler: function (handler) {
1404             if (handler === undefined || typeof handler === "function") {
1405                 return handler;
1406             } else {
1407                 throw new Error("handler must be a function");
1408             }
1409         },
1410 
1411         /**
1412          * @private
1413          * Tries to get extract the AWS error code from a
1414          * finesse.clientservices.ClientServices parsed error response object.
1415          * @param {Object} rsp
1416          *     The handler to validate
1417          * @returns {String}
1418          *     The error code, HTTP status code, or undefined
1419          */
1420         getErrCode: function (rsp) {
1421             try { // Best effort to get the error code
1422                 return rsp.object.ApiErrors.ApiError.ErrorType;
1423             } catch (e) { // Second best effort to get the HTTP Status code
1424                 if (rsp && rsp.status) {
1425                     return "HTTP " + rsp.status;
1426                 } else if (rsp && rsp.isUnsent) { // If request was aborted/cancelled/timedout
1427                     return "Request could not be completed";
1428                 }
1429             } // Otherwise, don't return anything (undefined)
1430         },
1431 
1432         /**
1433          * @private
1434          * Tries to get extract the AWS error data from a
1435          * finesse.clientservices.ClientServices parsed error response object.
1436          * @param {Object} rsp
1437          *     The handler to validate
1438          * @returns {String}
1439          *     The error data, HTTP status code, or undefined
1440          */
1441         getErrData: function (rsp) {
1442             try { // Best effort to get the error data
1443                 return rsp.object.ApiErrors.ApiError.ErrorData;
1444             } catch (e) { // Second best effort to get the HTTP Status code
1445                 if (rsp && rsp.status) {
1446                     return "HTTP " + rsp.status;
1447                 } else if (rsp && rsp.isUnsent) { // If request was aborted/cancelled/timedout
1448                     return "Request could not be completed";
1449                 }
1450             } // Otherwise, don't return anything (undefined)
1451         },
1452         
1453         /**
1454          * @private
1455          * Tries to get extract the AWS error message from a
1456          * finesse.clientservices.ClientServices parsed error response object.
1457          * @param {Object} rsp
1458          *     The handler to validate
1459          * @returns {String}
1460          *     The error message, HTTP status code, or undefined
1461          */
1462         getErrMessage: function (rsp) {
1463             try { // Best effort to get the error message
1464                 return rsp.object.ApiErrors.ApiError.ErrorMessage;
1465             } catch (e) { // Second best effort to get the HTTP Status code
1466                 if (rsp && rsp.status) {
1467                     return "HTTP " + rsp.status;
1468                 } else if (rsp && rsp.isUnsent) { // If request was aborted/cancelled/timedout
1469                     return "Request could not be completed";
1470                 }
1471             } // Otherwise, don't return anything (undefined)
1472         },
1473         
1474         /**
1475          * @private
1476          * Tries to get extract the AWS overrideable boolean from a
1477          * finesse.clientservices.ClientServices parsed error response object.
1478          * @param {Object} rsp
1479          *     The handler to validate
1480          * @returns {String}
1481          *     The overrideable boolean, HTTP status code, or undefined
1482          */
1483         getErrOverrideable: function (rsp) {
1484             try { // Best effort to get the override boolean
1485                 return rsp.object.ApiErrors.ApiError.Overrideable;
1486             } catch (e) { // Second best effort to get the HTTP Status code
1487                 if (rsp && rsp.status) {
1488                     return "HTTP " + rsp.status;
1489                 }
1490             } // Otherwise, don't return anything (undefined)
1491         },
1492 
1493         /**
1494          * Trims leading and trailing whitespace from a string.
1495          * @param {String} str
1496          *     The string to trim
1497          * @returns {String}
1498          *     The trimmed string
1499          */
1500         trim: function (str) {
1501             return str.replace(/^\s*/, "").replace(/\s*$/, "");
1502         },
1503 
1504         /**
1505          * Utility method for getting the current time in milliseconds.
1506          * @returns {String}
1507          *     The current time in milliseconds
1508          */
1509         currentTimeMillis : function () {
1510             return (new Date()).getTime();
1511         },
1512 
1513        /**
1514         * Gets the current drift (between client and server)
1515         *
1516         * @returns {integer} which is the current drift (last calculated; 0 if we have not calculated yet)
1517         */
1518        getCurrentDrift : function () {
1519             var drift;
1520             
1521             //Get the current client drift from localStorage
1522             drift = window.sessionStorage.getItem("clientTimestampDrift");
1523             if (drift) {
1524                  drift = parseInt(drift, 10);
1525                  if (isNaN(drift)) {
1526                       drift = 0; 
1527                  }
1528             }
1529           return drift;
1530         },
1531 
1532        /**
1533         * Converts the specified clientTime to server time by adjusting by the current drift.
1534         *
1535         * @param clientTime is the time in milliseconds
1536         * @returns {int} serverTime in milliseconds
1537         */
1538         convertToServerTimeMillis : function(clientTime) {
1539             var drift = this.getCurrentDrift();
1540             return (clientTime + drift);
1541         },
1542 
1543         /**
1544          * Utility method for getting the current time,
1545          * adjusted by the calculated "drift" to closely
1546          * approximate the server time.  This is used
1547          * when calculating durations based on a server
1548          * timestamp, which otherwise can produce unexpected
1549          * results if the times on client and server are
1550          * off.
1551          * 
1552          * @returns {String}
1553          *     The current server time in milliseconds
1554          */
1555         currentServerTimeMillis : function () {
1556             var drift = this.getCurrentDrift();
1557             return (new Date()).getTime() + drift;
1558         },
1559 
1560         /**
1561          * Given a specified timeInMs, this method will builds a string which displays minutes and seconds. 
1562          *
1563          * @param timeInMs is the time in milliseconds
1564          * @returns {String} which corresponds to minutes and seconds (e.g. 11:23)
1565          */
1566         buildTimeString : function (timeInMs) {
1567            var min, sec, timeStr = "00:00";
1568           
1569            if (timeInMs && timeInMs !== "-1") {
1570               // calculate minutes, and seconds
1571               min = this.pad(Math.floor(timeInMs / 60000));
1572               sec = this.pad(Math.floor((timeInMs % 60000) / 1000));
1573               
1574               // construct MM:SS time string
1575               timeStr =  min + ":" + sec;
1576            }
1577            return timeStr;  
1578         },
1579         
1580         /**
1581          * Given a specified timeInMs, this method will builds a string which displays minutes and seconds (and optionally hours)
1582          *
1583          * @param timeInMs is the time in milliseconds
1584          * @returns {String} which corresponds to hours, minutes and seconds (e.g. 01:11:23 or 11:23)
1585          */
1586         buildTimeStringWithOptionalHours: function (timeInMs) {
1587            var hour, min, sec, timeStr = "00:00", optionalHour = "", timeInSecs;
1588           
1589            if (timeInMs && timeInMs !== "-1") {
1590               timeInSecs = timeInMs / 1000;
1591               
1592               // calculate {hours}, minutes, and seconds
1593               hour = this.pad(Math.floor(timeInSecs / 3600));
1594               min = this.pad(Math.floor((timeInSecs % 3600) / 60));
1595               sec = this.pad(Math.floor((timeInSecs % 3600) % 60));   
1596               
1597               //Optionally add the hour if we have hours
1598               if (hour > 0) {
1599                 optionalHour = hour + ":";
1600               }
1601               
1602               // construct MM:SS time string (or optionally HH:MM:SS)
1603               timeStr = optionalHour + min + ":" + sec; 
1604            }
1605            return timeStr;
1606         },
1607         
1608         
1609         /**
1610          * Builds a string which specifies the amount of time user has been in this state (e.g. 11:23).
1611          *
1612          * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1613          * @param stateStartTimeInMs is integer argument which specifies time call entered current state
1614          * @returns {String} which is the elapsed time (MINUTES:SECONDS) 
1615          *
1616          */
1617         buildElapsedTimeString : function (adjustedServerTimeInMs, stateStartTimeInMs) {
1618            var result, delta;
1619            
1620            result = "--:--";
1621            if (stateStartTimeInMs !== 0) {
1622              delta = adjustedServerTimeInMs - stateStartTimeInMs;
1623              
1624              if (delta > 0) {
1625                result = this.buildTimeString(delta);
1626              }
1627           }
1628           return result;
1629        },
1630        
1631         /**
1632          * 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).
1633          *
1634          * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1635          * @param startTimeInMs is integer argument which specifies the start time
1636          * @returns {String} which is the elapsed time (MINUTES:SECONDS) or (HOURS:MINUTES:SECONDS)
1637          *
1638          */
1639         buildElapsedTimeStringWithOptionalHours : function (adjustedServerTimeInMs, stateStartTimeInMs) {
1640            var result, delta;
1641            
1642            result = "--:--";
1643            if (stateStartTimeInMs !== 0) {
1644              delta = adjustedServerTimeInMs - stateStartTimeInMs;
1645              
1646              if (delta > 0) {
1647                result = this.buildTimeStringWithOptionalHours(delta);
1648              }
1649           }
1650           return result;
1651        },
1652        
1653        
1654        /**
1655         * Builds a string which displays the total call time in minutes and seconds.
1656         *
1657         * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1658         * @param callStartTimeInMs is integer argument which specifies time the call started
1659         * @returns {String} which is the elapsed time [MINUTES:SECONDS]
1660         */
1661        buildTotalTimeString : function (adjustedServerTimeInMs, callStartTimeInMs) {
1662           return this.buildElapsedTimeString(adjustedServerTimeInMs, callStartTimeInMs);
1663        },
1664        
1665        /**
1666         * Builds a string which displays the hold time in minutes and seconds.
1667         *
1668         * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1669         * @param holdStartTimeInMs is integer argument which specifies time the hold started
1670         * @returns {String} which is the elapsed time [MINUTES:SECONDS] 
1671         */
1672        buildHoldTimeString : function (adjustedServerTimeInMs, holdStartTimeInMs) {
1673           return this.buildElapsedTimeString(adjustedServerTimeInMs, holdStartTimeInMs);
1674       },
1675       
1676       /**
1677        * Builds a string which displays the elapsed time the call has been in wrap up.
1678        *
1679        * @param adjustedServerTimeInMs is integer argument which specifies the expected server time (accounting for clientdrift)
1680        * @param wrapupStartTimeInMs is integer argument which specifies time call entered wrapup state
1681        * @returns {String} which is the elapsed wrapup time
1682        *
1683        */
1684       buildWrapupTimeString : function (adjustedServerTimeInMs, wrapupStartTimeInMs) {
1685          return this.buildElapsedTimeString(adjustedServerTimeInMs, wrapupStartTimeInMs);
1686       },
1687       
1688       /**
1689        * Extracts a time from the timeStr.  Note: The timeStr could be empty.  In this case, the time returned will be 0.
1690        * @param timeStr is a time string in ISO8601 format (note: could be empty)
1691        * @returns {long} is the time 
1692        */
1693       extractTime : function (timeStr) {
1694          var result = 0, theDate;
1695          if (timeStr === "") {
1696            result = 0;
1697          } else if (timeStr === null) {
1698            result = 0;
1699          } else {
1700            theDate = this.parseDateStringISO8601(timeStr);
1701            result = theDate.getTime();
1702          }
1703          return result;
1704       },
1705       
1706       /**
1707        * @private
1708        * Generates an RFC1422v4-compliant UUID using pesudorandom numbers.
1709        * @returns {String}
1710        *     An RFC1422v4-compliant UUID using pesudorandom numbers.
1711        **/        
1712         generateUUID: function () {
1713             return Math.uuidCompact();
1714         },
1715 
1716         /** @private */
1717         xml2json: finesse.Converter.xml2json,
1718         
1719         
1720         /**
1721          * @private
1722          * Utility method to get the JSON parser either from gadgets.json
1723          * or from window.JSON (which will be initialized by CUIC if 
1724          * browser doesn't support
1725          */
1726         getJSONParser: function() {
1727             var _container = window.gadgets || {},
1728                 parser = _container.json || window.JSON;
1729             return parser;
1730         },
1731 
1732        /**
1733         * @private
1734         * Utility method to convert a javascript object to XML.
1735         * @param {Object} object
1736         *   The object to convert to XML.
1737         * @param {Boolean} escapeFlag
1738         *   If escapeFlag evaluates to true:
1739         *       - XML escaping is done on the element values.
1740         *       - Attributes, #cdata, and #text is not supported.
1741         *       - The XML is unformatted (no whitespace between elements).
1742         *   If escapeFlag evaluates to false:
1743         *       - Element values are written 'as is' (no escaping).
1744         *       - Attributes, #cdata, and #text is supported.
1745         *       - The XML is formatted.
1746         * @returns The XML string.
1747         */
1748         json2xml: function (object, escapeFlag) {
1749             var xml;
1750             if (escapeFlag) {
1751                 xml = this._json2xmlWithEscape(object);
1752             }
1753             else {
1754                 xml = finesse.Converter.json2xml(object, '\t');
1755             }
1756             return xml;
1757         },
1758 
1759         /**
1760          * @private
1761          * Utility method to convert XML string into javascript object.
1762          */
1763         xml2JsObj : function (event) {
1764             var parser = this.getJSONParser();
1765             return parser.parse(finesse.Converter.xml2json(jQuery.parseXML(event), ""));
1766         },
1767 
1768        /**
1769         * @private
1770         * Utility method to convert an XML string to a javascript object.
1771         * @desc This function calls to the SAX parser and responds to callbacks
1772         *     received from the parser. Entity translation is not handled here.
1773         * @param {String} xml
1774         *   The XML to parse.
1775         * @returns The javascript object.
1776         */
1777         xml2js: function (xml) {
1778             var STATES = {
1779                     INVALID: 0,
1780                     NEW_NODE: 1,
1781                     ATTRIBUTE_NODE: 2,
1782                     TEXT_NODE: 3,
1783                     END_NODE: 4
1784                 },
1785                 state = STATES.INVALID,
1786                 rootObj = {},
1787                 newObj,
1788                 objStack = [rootObj],
1789                 nodeName = "",
1790 
1791                 /**
1792                 * @private
1793                 * Adds a property to the current top JSO.
1794                 * @desc This is also where we make considerations for arrays.
1795                 * @param {String} name
1796                 *   The name of the property to add.
1797                 * @param (String) value
1798                 *     The value of the property to add.
1799                 */
1800                 addProperty = function (name, value) {
1801                     var current = objStack[objStack.length - 1];
1802                     if(current.hasOwnProperty(name) && current[name] instanceof Array){
1803                         current[name].push(value);
1804                     }else if(current.hasOwnProperty(name)){
1805                         current[name] = [current[name], value];
1806                     }else{
1807                         current[name] = value;
1808                     }
1809                 },
1810 
1811                 /**
1812                 * @private
1813                 * The callback passed to the SAX parser which processes events from
1814                 * the SAX parser in order to construct the resulting JSO.
1815                 * @param (String) type
1816                 *     The type of event received.
1817                 * @param (String) data
1818                 *     The data received from the SAX parser. The contents of this
1819                 *     parameter vary based on the type of event.
1820                 */
1821                 xmlFound = function (type, data) {
1822                     switch (type) {
1823                     case "StartElement":
1824                         // Because different node types have different expectations
1825                         // of parenting, we don't push another JSO until we know
1826                         // what content we're getting
1827 
1828                         // If we're already in the new node state, we're running
1829                         // into a child node. There won't be any text here, so
1830                         // create another JSO
1831                         if(state === STATES.NEW_NODE){
1832                             newObj = {};
1833                             addProperty(nodeName, newObj);
1834                             objStack.push(newObj);
1835                         }
1836                         state = STATES.NEW_NODE;
1837                         nodeName = data;
1838                         break;
1839                     case "EndElement":
1840                         // If we're in the new node state, we've found no content
1841                         // set the tag property to null
1842                         if(state === STATES.NEW_NODE){
1843                             addProperty(nodeName, null);
1844                         }else if(state === STATES.END_NODE){
1845                             objStack.pop();
1846                         }
1847                         state = STATES.END_NODE;
1848                         break;
1849                     case "Attribute":
1850                         // If were in the new node state, no JSO has yet been created
1851                         // for this node, create one
1852                         if(state === STATES.NEW_NODE){
1853                             newObj = {};
1854                             addProperty(nodeName, newObj);
1855                             objStack.push(newObj);
1856                         }
1857                         // Attributes are differentiated from child elements by a
1858                         // preceding "@" in the property name
1859                         addProperty("@" + data.name, data.value);
1860                         state = STATES.ATTRIBUTE_NODE;
1861                         break;
1862                     case "Text":
1863                         // In order to maintain backwards compatibility, when no
1864                         // attributes are assigned to a tag, its text contents are
1865                         // assigned directly to the tag property instead of a JSO.
1866 
1867                         // If we're in the attribute node state, then the JSO for
1868                         // this tag was already created when the attribute was
1869                         // assigned, differentiate this property from a child
1870                         // element by naming it "#text"
1871                         if(state === STATES.ATTRIBUTE_NODE){
1872                             addProperty("#text", data);
1873                         }else{
1874                             addProperty(nodeName, data);
1875                         }
1876                         state = STATES.TEXT_NODE;
1877                         break;
1878                     }
1879                 };
1880             SaxParser.parse(xml, xmlFound);
1881             return rootObj;
1882         },
1883 
1884        /**
1885         * @private
1886         * Traverses a plain-old-javascript-object recursively and outputs its XML representation.
1887         * @param {Object} obj
1888         *     The javascript object to be converted into XML.
1889         * @returns {String} The XML representation of the object.
1890         */
1891         js2xml: function (obj) {
1892             var xml = "", i, elem;
1893 
1894             if (obj !== null) {
1895                 if (obj.constructor === Object) {
1896                     for (elem in obj) {
1897                         if (obj[elem] === null || typeof(obj[elem]) === 'undefined') {
1898                             xml += '<' + elem + '/>';
1899                         } else if (obj[elem].constructor === Array) {
1900                             for (i = 0; i < obj[elem].length; i++) {
1901                                 xml += '<' + elem + '>' + this.js2xml(obj[elem][i]) + '</' + elem + '>';
1902                             }
1903                         } else if (elem[0] !== '@') {
1904                             if (this.js2xmlObjIsEmpty(obj[elem])) {
1905                                 xml += '<' + elem + this.js2xmlAtt(obj[elem]) + '/>';
1906                             } else if (elem === "#text") {
1907                                 xml += obj[elem];
1908                             } else {
1909                                 xml += '<' + elem + this.js2xmlAtt(obj[elem]) + '>' + this.js2xml(obj[elem]) + '</' + elem + '>';
1910                             }
1911                         }
1912                     }
1913                 } else {
1914                     xml = obj;
1915                 }
1916             }
1917 
1918             return xml;
1919         },
1920 
1921        /**
1922         * @private
1923         * Utility method called exclusively by js2xml() to find xml attributes.
1924         * @desc Traverses children one layer deep of a javascript object to "look ahead"
1925         * for properties flagged as such (with '@').
1926         * @param {Object} obj
1927         *   The obj to traverse.
1928         * @returns {String} Any attributes formatted for xml, if any.
1929         */
1930         js2xmlAtt: function (obj) {
1931             var elem;
1932 
1933             if (obj !== null) {
1934                 if (obj.constructor === Object) {
1935                     for (elem in obj) {
1936                         if (obj[elem] !== null && typeof(obj[elem]) !== "undefined" && obj[elem].constructor !== Array) {
1937                             if (elem[0] === '@'){
1938                                 return ' ' + elem.substring(1) + '="' + obj[elem] + '"';
1939                             }
1940                         }
1941                     }
1942                 }
1943             }
1944 
1945             return '';
1946         },
1947 
1948        /**
1949         * @private
1950         * Utility method called exclusively by js2xml() to determine if
1951         * a node has any children, with special logic for ignoring attributes.
1952         * @desc Attempts to traverse the elements in the object while ignoring attributes.
1953         * @param {Object} obj
1954         *   The obj to traverse.
1955         * @returns {Boolean} whether or not the JS object is "empty"
1956         */
1957         js2xmlObjIsEmpty: function (obj) {
1958             var elem;
1959 
1960             if (obj !== null) {
1961                 if (obj.constructor === Object) {
1962                     for (elem in obj) {
1963                         if (obj[elem] !== null) {
1964                             if (obj[elem].constructor === Array){
1965                                 return false;
1966                             }
1967 
1968                             if (elem[0] !== '@'){
1969                                 return false;
1970                             }
1971                         } else {
1972                             return false;
1973                         }
1974                     }
1975                 } else {
1976                     return false;
1977                 }
1978             }
1979 
1980             return true;
1981         },
1982 
1983         /**
1984          * Encodes the given string into base64.
1985          *<br>
1986          * <b>NOTE:</b> {input} is assumed to be UTF-8; only the first
1987          * 8 bits of each input element are significant.
1988          *
1989          * @param {String} input
1990          *     The string to convert to base64.
1991          * @returns {String}
1992          *     The converted string.
1993          */
1994         b64Encode: function (input) {
1995             var output = "", idx, data,
1996                 table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
1997 
1998             for (idx = 0; idx < input.length; idx += 3) {
1999                 data =  input.charCodeAt(idx) << 16 |
2000                             input.charCodeAt(idx + 1) << 8 |
2001                             input.charCodeAt(idx + 2);
2002 
2003                 //assume the first 12 bits are valid
2004                 output +=   table.charAt((data >>> 18) & 0x003f) +
2005                             table.charAt((data >>> 12) & 0x003f);
2006                 output +=   ((idx + 1) < input.length) ?
2007                             table.charAt((data >>> 6) & 0x003f) :
2008                             "=";
2009                 output +=   ((idx + 2) < input.length) ?
2010                             table.charAt(data & 0x003f) :
2011                             "=";
2012             }
2013 
2014             return output;
2015         },
2016 
2017         /**
2018          * Decodes the given base64 string.
2019          * <br>
2020          * <b>NOTE:</b> output is assumed to be UTF-8; only the first
2021          * 8 bits of each output element are significant.
2022          *
2023          * @param {String} input
2024          *     The base64 encoded string
2025          * @returns {String}
2026          *     Decoded string
2027          */
2028         b64Decode: function (input) {
2029             var output = "", idx, h, data,
2030                 table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
2031 
2032             for (idx = 0; idx < input.length; idx += 4) {
2033                 h = [
2034                     table.indexOf(input.charAt(idx)),
2035                     table.indexOf(input.charAt(idx + 1)),
2036                     table.indexOf(input.charAt(idx + 2)),
2037                     table.indexOf(input.charAt(idx + 3))
2038                 ];
2039 
2040                 data = (h[0] << 18) | (h[1] << 12) | (h[2] << 6) | h[3];
2041                 if (input.charAt(idx + 2) === '=') {
2042                     data = String.fromCharCode(
2043                         (data >>> 16) & 0x00ff
2044                     );
2045                 } else if (input.charAt(idx + 3) === '=') {
2046                     data = String.fromCharCode(
2047                         (data >>> 16) & 0x00ff,
2048                         (data >>> 8) & 0x00ff
2049                     );
2050                 } else {
2051                     data = String.fromCharCode(
2052                         (data >>> 16) & 0x00ff,
2053                         (data >>> 8) & 0x00ff,
2054                         data & 0x00ff
2055                     );
2056                 }
2057                 output += data;
2058             }
2059 
2060             return output;
2061         },
2062 
2063         /**
2064          * @private
2065          * Extracts the username and the password from the Base64 encoded string.
2066          * @params {String}
2067          *     A base64 encoded string containing credentials that (when decoded)
2068          *     are colon delimited.
2069          * @returns {Object}
2070          *     An object with the following structure:
2071          *     {id:string, password:string}
2072          */
2073         getCredentials: function (authorization) {
2074             var credObj = {},
2075                 credStr = this.b64Decode(authorization),
2076                 colonIndx = credStr.indexOf(":");
2077 
2078             //Check to ensure that string is colon delimited.
2079             if (colonIndx === -1) {
2080                 throw new Error("String is not colon delimited.");
2081             }
2082 
2083             //Extract ID and password.
2084             credObj.id = credStr.substring(0, colonIndx);
2085             credObj.password = credStr.substring(colonIndx + 1);
2086             return credObj;
2087         },
2088 
2089         /**
2090          * Takes a string and removes any spaces within the string.
2091          * @param {String} string
2092          *     The string to remove spaces from
2093          * @returns {String}
2094          *     The string without spaces
2095          */
2096         removeSpaces: function (string) {
2097             return string.split(' ').join('');
2098         },
2099 
2100         /**
2101          * Escapes spaces as encoded " " characters so they can
2102          * be safely rendered by jQuery.text(string) in all browsers.
2103          *
2104          * (Although IE behaves as expected, Firefox collapses spaces if this function is not used.)
2105          *
2106          * @param text
2107          *    The string whose spaces should be escaped
2108          *
2109          * @returns
2110          *    The string with spaces escaped
2111          */
2112         escapeSpaces: function (string) {
2113             return string.replace(/\s/g, '\u00a0');
2114         },
2115 
2116         /**
2117          * Adds a span styled to line break at word edges around the string passed in.
2118          * @param str String to be wrapped in word-breaking style.
2119          * @private
2120          */
2121         addWordWrapping : function (str) {
2122             return '<span style="word-wrap: break-word;">' + str + '</span>';
2123         },
2124 
2125         /**
2126          * Takes an Object and determines whether it is an Array or not.
2127          * @param {Object} obj
2128          *     The Object in question
2129          * @returns {Boolean}
2130          *     true if the object is an Array, else false.
2131          */
2132         isArray: function (obj) {
2133             return obj.constructor.toString().indexOf("Array") !== -1;
2134         },
2135 
2136         /**
2137          * @private
2138          * Takes a data object and returns an array extracted
2139          * @param {Object} data
2140          *     JSON payload
2141          *
2142          * @returns {array}
2143          *     extracted array
2144          */
2145         getArray: function (data) {
2146             if (this.isArray(data)) {
2147                 //Return if already an array.
2148                 return data;
2149             } else {
2150                 //Create an array, iterate through object, and push to array. This
2151                 //should only occur with one object, and therefore one obj in array.
2152                 var arr = [];
2153                 arr.push(data);
2154                 return arr;
2155             }
2156         },
2157 
2158         /**
2159          * @private
2160          * Extracts the ID for an entity given the Finesse REST URI. The ID is
2161          * assumed to be the last element in the URI (after the last "/").
2162          * @param {String} uri
2163          *     The Finesse REST URI to extract the ID from.
2164          * @returns {String}
2165          *     The ID extracted from the REST URI.
2166          */
2167         getId: function (uri) {
2168             if (!uri) {
2169                 return "";
2170             }
2171             var strLoc = uri.lastIndexOf("/");
2172             return uri.slice(strLoc + 1);
2173         },
2174 
2175         /**
2176          * Compares two objects for equality.
2177          * @param {Object} obj1 
2178          *      First of two objects to compare.
2179          * @param {Object} obj2
2180          *      Second of two objects to compare.
2181          */
2182         getEquals: function (objA, objB) {
2183             var key;
2184 
2185             for (key in objA) {
2186                 if (objA.hasOwnProperty(key)) {
2187                     if (!objA[key]) {
2188                         objA[key] = "";
2189                     }
2190 
2191                     if (typeof objB[key] === 'undefined') {
2192                         return false;
2193                     }
2194                     if (typeof objB[key] === 'object') {
2195                         if (!objB[key].equals(objA[key])) {
2196                             return false;
2197                         }
2198                     }
2199                     if (objB[key] !== objA[key]) {
2200                         return false;
2201                     }
2202                 }
2203             }
2204             return true;
2205         },
2206 
2207         /**
2208          * Regular expressions used in translating HTML and XML entities
2209          */
2210         ampRegEx : new RegExp('&', 'gi'),
2211         ampEntityRefRegEx : new RegExp('&', 'gi'),
2212         ltRegEx : new RegExp('<', 'gi'),
2213         ltEntityRefRegEx : new RegExp('<', 'gi'),
2214         gtRegEx : new RegExp('>', 'gi'),
2215         gtEntityRefRegEx : new RegExp('>', 'gi'),
2216         xmlSpecialCharRegEx: new RegExp('[&<>"\']', 'g'),
2217         entityRefRegEx: new RegExp('&[^;]+(?:;|$)', 'g'),
2218 
2219         /**
2220          * Translates between special characters and HTML entities
2221          *
2222          * @param text
2223          *     The text to translate
2224          *
2225          * @param makeEntityRefs
2226          *    If true, encode special characters as HTML entities; if
2227          *    false, decode HTML entities back to special characters
2228          *
2229          * @private
2230          */
2231         translateHTMLEntities: function (text, makeEntityRefs) {
2232             if (typeof(text) !== "undefined" && text !== null && text !== "") {
2233                 if (makeEntityRefs) {
2234                     text = text.replace(this.ampRegEx, '&');
2235                     text = text.replace(this.ltRegEx, '<');
2236                     text = text.replace(this.gtRegEx, '>');
2237                 } else {
2238                     text = text.replace(this.gtEntityRefRegEx, '>');
2239                     text = text.replace(this.ltEntityRefRegEx, '<');
2240                     text = text.replace(this.ampEntityRefRegEx, '&');
2241                 }
2242             }
2243 
2244             return text;
2245         },
2246 
2247         /**
2248          * Translates between special characters and XML entities
2249          *
2250          * @param text
2251          *     The text to translate
2252          *
2253          * @param makeEntityRefs
2254          *    If true, encode special characters as XML entities; if
2255          *    false, decode XML entities back to special characters
2256          *
2257          * @private
2258          */
2259         translateXMLEntities: function (text, makeEntityRefs) {
2260             /** @private */
2261             var escape = function (character) {
2262                 switch (character) {
2263                     case "&":
2264                         return "&";
2265                     case "<":
2266                         return "<";
2267                     case ">":
2268                         return ">";
2269                     case "'":
2270                         return "'";
2271                     case "\"":
2272                         return """;
2273                     default:
2274                         return character;
2275                 }
2276             },
2277             /** @private */
2278             unescape = function (entity) {
2279                 switch (entity) {
2280                     case "&":
2281                         return "&";
2282                     case "<":
2283                         return "<";
2284                     case ">":
2285                         return ">";
2286                     case "'":
2287                         return "'";
2288                     case """:
2289                         return "\"";
2290                     default:
2291                         if (entity.charAt(1) === "#" && entity.charAt(entity.length - 1) === ";") {
2292                             if (entity.charAt(2) === "x") {
2293                                 return String.fromCharCode(parseInt(entity.slice(3, -1), 16));
2294                             } else {
2295                                 return String.fromCharCode(parseInt(entity.slice(2, -1), 10));
2296                             }
2297                         } else {
2298                             throw new Error("Invalid XML entity: " + entity);
2299                         }
2300                 }
2301             };
2302 
2303             if (typeof(text) !== "undefined" && text !== null && text !== "") {
2304                 if (makeEntityRefs) {
2305                     text = text.replace(this.xmlSpecialCharRegEx, escape);
2306                 } else {
2307                     text = text.replace(this.entityRefRegEx, unescape);
2308                 }
2309             }
2310 
2311             return text;
2312         },
2313 
2314         /**
2315          * @private
2316          * Utility method to pad the number with a leading 0 for single digits
2317          * @param (Number) num
2318          *     the number to pad
2319          */
2320         pad : function (num) {
2321             if (num < 10) {
2322                 return "0" + num;
2323             }
2324 
2325             return String(num);
2326         },
2327         
2328         /**
2329          * Pad with zeros based on a padWidth.
2330          *
2331          * @param num
2332          * @param padWidth
2333          * @returns {String} with padded zeros (based on padWidth)
2334          */
2335         padWithWidth : function (num, padWidth) {
2336             var value, index, result;
2337             
2338             result = "";
2339             for(index=padWidth;index>1;index--)
2340             {
2341                 value = Math.pow(10, index-1);
2342                 
2343                 if (num < value) {
2344                    result = result + "0";
2345                 }
2346             }
2347             result = result + num;
2348             
2349             return String(result);
2350         },
2351         
2352         /**
2353          * Converts a date to an ISO date string.
2354          *
2355          * @param aDate
2356          * @returns {String} in ISO date format
2357          *
2358          * Note: Some browsers don't support this method (e.g. IE8).
2359          */
2360         convertDateToISODateString : function (aDate) {
2361              var result;
2362              
2363              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";
2364              return result;
2365         },
2366         
2367        /**
2368         * Get the date in ISO date format. 
2369         * 
2370         * @param aDate is the date
2371         * @returns {String} date in ISO format
2372         *
2373         * Note: see convertDateToISODateString() above.
2374         */
2375         dateToISOString : function (aDate) {
2376              var result;
2377              
2378              try {
2379                 result = aDate.toISOString();
2380              } catch (e) {
2381                 result = this.convertDateToISODateString(aDate);
2382              }
2383              return result;
2384         },
2385         
2386         /**
2387          * Parse string (which is formated as ISO8601 date) into Javascript Date object.
2388          *
2389          * @param s ISO8601 string
2390          * @return {Date}
2391          * Note: Some browsers don't support Date constructor which take ISO8601 date (e.g. IE 8).
2392          */
2393         parseDateStringISO8601 : function (s) {
2394              var i, re = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:.(\d+))?(Z|[+\-]\d{2})(?::(\d{2}))?/,
2395              d = s.match(re);
2396              if( !d ) {
2397                 return null;
2398              }
2399              for( i in d ) {
2400                 d[i] = ~~d[i];
2401              }
2402              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);
2403         },
2404         
2405         /**
2406          * Utility method to render a timestamp value (in seconds) into HH:MM:SS format.
2407          * @param {Number} time
2408          *     The timestamp in ms to render
2409          * @returns {String}
2410          * Time string in HH:MM:SS format.
2411          */
2412         getDisplayTime : function (time) {
2413             var hour, min, sec, timeStr = "00:00:00";
2414 
2415             if (time && time !== "-1") {
2416                 // calculate hours, minutes, and seconds
2417                 hour = this.pad(Math.floor(time / 3600));
2418                 min = this.pad(Math.floor((time % 3600) / 60));
2419                 sec = this.pad(Math.floor((time % 3600) % 60));
2420                 // construct HH:MM:SS time string
2421                 timeStr = hour + ":" + min + ":" + sec;
2422             }
2423 
2424             return timeStr;
2425         },
2426 
2427         /**
2428          * Checks if the string is null. If it is, return empty string; else return
2429          * the string itself.
2430          * 
2431          * @param  {String} str 
2432          * The string to check
2433          * @return {String}     
2434          * Empty string or string itself
2435          */
2436         convertNullToEmptyString : function (str) {
2437             return str || "";
2438         },
2439 
2440         /**
2441          * Utility method to render a timestamp string (of format
2442          * YYYY-MM-DDTHH:MM:SSZ) into a duration of HH:MM:SS format.
2443          * 
2444          * @param {String} timestamp
2445          *           The timestamp to render
2446          * @param {Date} [now]
2447          *            Optional argument to provide the time from which to
2448          *            calculate the duration instead of using the current time
2449          * @returns {String}
2450          * Duration string in HH:MM:SS format.
2451          */
2452         convertTsToDuration : function (timestamp, now) {
2453             return this.convertTsToDurationWithFormat(timestamp, false, now); 
2454         },
2455         
2456         /**
2457          * Utility method to render a timestamp string (of format
2458          * YYYY-MM-DDTHH:MM:SSZ) into a duration of HH:MM:SS format,
2459          * with optional -1 for null or negative times.
2460          * 
2461          * @param {String} timestamp
2462          *             The timestamp to render
2463          * @param {Boolean} forFormat
2464          *            If True, if duration is null or negative, return -1 so that the duration can be formated
2465          *            as needed in the Gadget. 
2466          * @param {Date} [now]
2467          *             Optional argument to provide the time from which to
2468          *            calculate the duration instead of using the current time
2469          * @returns {String}
2470          * Duration string in HH:MM:SS format.
2471          */
2472         convertTsToDurationWithFormat : function (timestamp, forFormat, now) {
2473             var startTimeInMs, nowInMs, durationInSec = "-1";
2474             
2475             // Calculate duration
2476             if (timestamp && typeof timestamp === "string") {
2477                 // first check it '--' for a msg in grid
2478                 if (timestamp === '--' || timestamp ==="" || timestamp === "-1") {
2479                     return "-1";
2480                 }
2481                 // else try convert string into a time
2482                 startTimeInMs = Date.parse(timestamp);
2483                 if (!isNaN(startTimeInMs)) {
2484                     if (!now || !(now instanceof Date)) {
2485                         nowInMs = this.currentServerTimeMillis();
2486                     } else {
2487                         nowInMs = this.convertToServerTimeMillis(now.getTime());
2488                     }
2489                     durationInSec = Math.floor((nowInMs - startTimeInMs) / 1000);
2490                     // Since currentServerTime is not exact (lag between sending and receiving
2491                     // messages will differ slightly), treat a slightly negative (less than 1 sec) 
2492                     // value as 0, to avoid "--" showing up when a state first changes.
2493                     if (durationInSec === -1) {
2494                         durationInSec = 0;
2495                     }
2496                     
2497                     if (durationInSec < 0) {
2498                         if (forFormat) {
2499                             return "-1";
2500                         } else {
2501                             return this.getDisplayTime("-1");
2502                         }
2503                     }
2504                 }
2505             }else {
2506                 if(forFormat){
2507                     return "-1";
2508                 }
2509             }
2510             return this.getDisplayTime(durationInSec);
2511          },
2512          
2513          /**
2514           * @private
2515           * Takes the time in seconds and duration in % and return the duration in milliseconds.
2516           *
2517           * @param time in seconds
2518           * @param duration in %
2519           */
2520          
2521          getRefreshTime :function(expiryTime , duration){
2522           var durationInMs = Math.floor((expiryTime * duration * 1000) / 100);
2523             return durationInMs;
2524          },
2525          
2526         /**
2527          * Takes a string (typically from window.location) and finds the value which corresponds to a name. For
2528          * example: http://www.company.com/?param1=value1¶m2=value2
2529          *
2530          * @param str is the string to search
2531          * @param name is the name to search for
2532          */
2533         getParameterByName : function(str, name) {
2534             var regex, results;
2535             name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
2536             regex = new RegExp("[\\?&]" + name + "=([^&#]*)");
2537             results = regex.exec(str);
2538             return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
2539         }, 
2540         
2541         /**
2542          *
2543          * Returns the base64 encoded user authorization String.
2544          * @returns {String} the Authorization String
2545          * 
2546          */
2547         getUserAuthString: function () {
2548             var authString = window.sessionStorage.getItem('userFinesseAuth');
2549             return authString;
2550         },
2551         
2552         /**
2553          * Return the user access token as JSON Object.
2554          * @returns {Object} the access token JSON object
2555          * 
2556          */
2557         getAuthTokenObj: function(){
2558            var authTokenString = window.sessionStorage.getItem('ssoTokenObject');
2559            return this.getJSONParser().parse(authTokenString);
2560         },
2561         
2562         /**
2563          * Returns the user access token as String.
2564          * @returns {String} the access token
2565          * 
2566          */
2567 
2568         getToken: function () {
2569             var tokenString = window.sessionStorage.getItem('ssoTokenObject'), tokenObj;
2570 					if (tokenString && typeof tokenString === "string") {
2571 						tokenObj = this.getJSONParser().parse(tokenString);
2572 						if (tokenObj.token) {
2573 							return tokenObj.token;
2574 						} else {
2575 							throw new Error(
2576 									"Unable to retrieve token : Invalid token Object in browser session");
2577 						}
2578 					} 
2579         },
2580         
2581         /**
2582 		 * The authorization header based on SSO or non SSO deployment.
2583 		 *          Can be "Bearer " or "Basic "
2584 		 * @returns {String} The authorization header string.
2585 		 */
2586 		 getAuthHeaderString : function(configObj) {
2587 					var authHeader;
2588 					if (configObj.systemAuthMode === this.getAuthModes().SSO) {
2589 						authHeader = "Bearer " + configObj.authToken;
2590 					} else if (configObj.systemAuthMode === this.getAuthModes().NONSSO) {
2591 						authHeader = "Basic " + configObj.authorization;
2592 					} else {
2593 						throw new Error("Unknown auth mode "+configObj.systemAuthMode);
2594 					}
2595 					return authHeader;
2596 				},
2597 		
2598 		/**
2599 		 * Can be used as a constant for auth modes
2600 		 *          Can be "SSO" , "NON_SSO" or "HYBRID"
2601 		 * @returns {String} The authorization header string.
2602 		 */		
2603 		getAuthModes : function(){
2604 		     return {
2605                     SSO: "SSO",
2606 		            NONSSO: "NON_SSO",
2607 		            HYBRID: "HYBRID"
2608 		     };
2609 		},
2610 		
2611 		/**
2612 		 * Encodes the node name
2613 		 */
2614 		encodeNodeName : function(node){
2615 			if (node === null){
2616 			    return null;
2617 			}
2618 			var originalChars, encodedChars,encodedNode, i;
2619 			originalChars = ["?", "@", "&","'"];
2620 			encodedChars = ["?3F", "?40", "?26","?27"];
2621 			encodedNode = node;
2622 			
2623 			if(encodedNode.indexOf(originalChars[0]) !== -1){
2624 			   encodedNode = encodedNode.replace(/\?/g, encodedChars[0]);
2625 			}
2626 			for (i = 1; i < originalChars.length; i++){
2627 			    if(encodedNode.indexOf(originalChars[i]) !== -1){
2628 			        encodedNode = encodedNode.replace(new RegExp(originalChars[i], "g"), encodedChars[i]);
2629 			    }
2630 			}
2631 			return encodedNode;
2632 		},
2633 		
2634 		/**
2635 		 * @private Utility method to convert milliseconds into minutes.
2636 		 * @param {String} Time in milliseconds
2637 		 * @returns {String} Time in minutes
2638 		 */
2639 		convertMilliSecondsToMinutes : function(millisec){
2640 			if(!millisec || isNaN(millisec)){
2641 				throw new Error("passed argument is not a number");
2642 			}else{
2643 				var minutes = Math.floor(millisec / (1000 * 60));
2644 	            return minutes;
2645 			}
2646 		},
2647 
2648         
2649         /**
2650 		 * @private Adds a new cookie to the page with a default domain.
2651 		 * @param {String}
2652 		 *            key the key to assign a value to
2653 		 * @param {String}
2654 		 *            value the value to assign to the key
2655 		 * @param {Number}
2656 		 *            days number of days (from current) until the cookie should
2657 		 *            expire
2658 		 * @param {String}
2659 		 *            domain the domain for the cookie   
2660 		 */
2661         addCookie : function (key, value, days, domain) {
2662             var date, expires = "",
2663                 cookie = key + "=" + escape(value);
2664             if (typeof days === "number") {
2665                 date = new Date();
2666                 date.setTime(date.getTime() + (days * 24 * 3600 * 1000));
2667                 cookie += "; expires=" + date.toGMTString();
2668             }
2669             
2670             if (domain) {
2671             	cookie += "; domain=" + domain;
2672             }
2673             
2674             document.cookie = cookie + "; path=/";
2675         },
2676 
2677         /**
2678          * @private
2679          * Get the value of a cookie given a key.
2680          * @param {String} key
2681          *      a key to lookup
2682          * @returns {String}
2683          *      the value mapped to a key, null if key doesn't exist
2684          */
2685         getCookie : function (key) {
2686             var i, pairs, pair;
2687             if (document.cookie) {
2688                 pairs = document.cookie.split(";");
2689                 for (i = 0; i < pairs.length; i += 1) {
2690                     pair = this.trim(pairs[i]).split("=");
2691                     if (pair[0] === key) {
2692                         return unescape(pair[1]);
2693                     }
2694                 }
2695             }
2696             return null;
2697         },
2698         
2699         getCookieEntriesThatStartsWith : function (key) {
2700        	 var i, pairs, pair, entries = [];
2701             if (document.cookie) {
2702                 pairs = document.cookie.split(";");
2703                 for (i = 0; i < pairs.length; i += 1) {
2704                     pair = this.trim(pairs[i]).split("=");
2705                     if (pair[0].startsWith(key)) {
2706                    	 entries.push({key: pair[0], value: unescape(pair[1])});
2707                     }
2708                 }
2709             }
2710             return entries;
2711        },
2712 
2713         /**
2714          * @private
2715          * Deletes the cookie mapped to specified key.
2716          * @param {String} key
2717          *      the key to delete
2718          */
2719         deleteCookie : function (key, domain) {
2720             this.addCookie(key, "", -1, domain);
2721         },
2722 
2723         /**
2724          * @private
2725          * Case insensitive sort for use with arrays or Dojox stores
2726          * @param {String} a
2727          *      first value
2728          * @param {String} b
2729          *      second value
2730          */
2731         caseInsensitiveSort: function (a, b) {
2732             var ret = 0, emptyString = "";
2733             a = a + emptyString;
2734             b = b + emptyString;
2735             a = a.toLowerCase();
2736             b = b.toLowerCase();
2737             if (a > b) {
2738                 ret = 1;
2739             }
2740             if (a < b) { 
2741                 ret = -1;
2742             }
2743             return ret;
2744         },
2745 
2746         /**
2747          * @private
2748         * Calls the specified function to render the dojo wijit for a gadget  when the gadget first becomes visible.
2749         *
2750         * The displayWjitFunc function will be called once and only once when the div for our wijit 
2751         * becomes visible for the first time.  This is necessary because some dojo wijits such as the grid
2752         * throw exceptions and do not render properly if they are created in a display:none div.
2753         * If our gadget is visisble the function will be called immediately.
2754         * If our gadget is not yet visisble, then it sets a timer and waits for it to become visible.
2755         * NOTE:  The timer may seem inefficent, originally I tried connecting to the tab onclick handler, but
2756         * there is a problem with dojo.connnect to an iframe's parent node in Internet Explorer. 
2757         * In Firefox the click handler works OK, but it happens before the node is actually visisble, so you
2758         * end up waiting for the node to become visisble anyway.
2759         * @displayWjitFunc:  A function to be called once our gadget has become visisble for th first time.
2760         */  
2761         onGadgetFirstVisible: function (displayWjitFunc) {
2762             var i, q, frameId, gadgetNbr, gadgetTitleId, panelId, panelNode, link, iterval, once = false, active = false, tabId = "#finesse-tab-selector";
2763             try {
2764                 frameId = dojo.attr(window.frameElement, "id"); // Figure out what gadget number we are by looking at our frameset
2765                 gadgetNbr = frameId.match(/\d+$/)[0];  // Strip the number off the end of the frame Id, that's our gadget number
2766                 gadgetTitleId = "#finesse_gadget_" + gadgetNbr + "_title";  // Create a a gadget title id from the number
2767                 
2768                 // Loop through all of the tab panels to find one that has our gadget id
2769                 dojo.query('.tab-panel', window.parent.document).some(function (node, index, arr) {
2770                     q = dojo.query(gadgetTitleId, node);  // Look in this panel for our gadget id
2771                     if (q.length > 0) {  // You found it
2772                         panelNode = node;
2773                         panelId = dojo.attr(panelNode, "id");  // Get panel id  e.g. panel_Workgroups
2774                         active = dojo.hasClass(panelNode, "active");
2775                         tabId = "#tab_" + panelId.slice(6);  // Turn it into a tab id e.g.tab_Workgroups
2776                         return;
2777                     }
2778                 });
2779                 // If panel is already active - execute the function - we're done
2780                 if (active) {
2781                     //?console.log(frameId + " is visible display it");
2782                     setTimeout(displayWjitFunc);
2783                 } 
2784                 // If its not visible - wait for the active class to show up.
2785                 else {
2786                     //?console.log(frameId  + " (" + tabId + ") is NOT active wait for it");
2787                     iterval = setInterval(dojo.hitch(this, function () {
2788                         if (dojo.hasClass(panelNode, "active")) {
2789                             //?console.log(frameId  + " (" + tabId + ") is visible display it");
2790                             clearInterval(iterval);
2791                             setTimeout(displayWjitFunc);
2792                         } 
2793                     }), 250);
2794                 }
2795             } catch (err) {
2796                 //?console.log("Could not figure out what tab " + frameId + " is in: " + err);
2797             }
2798         },
2799 
2800         /**
2801          * @private
2802          * Downloads the specified url using a hidden iframe. In order to cause the browser to download rather than render
2803          * in the hidden iframe, the server code must append the header "Content-Disposition" with a value of 
2804          * "attachment; filename=\"<WhateverFileNameYouWant>\"".
2805          */
2806         downloadFile : function (url) {
2807             var iframe = document.getElementById("download_iframe");
2808 
2809             if (!iframe)
2810             {
2811                 iframe = document.createElement("iframe");
2812                 $(document.body).append(iframe);
2813                 $(iframe).css("display", "none");
2814             }
2815 
2816             iframe.src = url;
2817         },
2818 
2819         /**
2820          * @private
2821          * bitMask has functions for testing whether bit flags specified by integers are set in the supplied value
2822          */
2823         bitMask: {
2824             /** @private */
2825             isSet: function (value, mask) {
2826                 return (value & mask) === mask;
2827             },
2828             /**
2829              * Returns true if all flags in the intArray are set on the specified value
2830              * @private 
2831              */
2832             all: function (value, intArray) {
2833                 var i = intArray.length;
2834                 if (typeof(i) === "undefined")
2835                 {
2836                     intArray = [intArray];
2837                     i = 1;
2838                 }
2839                 while ((i = i - 1) !== -1)
2840                 {
2841                     if (!this.isSet(value, intArray[i]))
2842                     {
2843                         return false;
2844                     }
2845                 }
2846                 return true;
2847             },
2848             /**
2849              * @private
2850              * Returns true if any flags in the intArray are set on the specified value
2851              */
2852             any: function (value, intArray) {
2853                 var i = intArray.length;
2854                 if (typeof(i) === "undefined")
2855                 {
2856                     intArray = [intArray];
2857                     i = 1;
2858                 }
2859                 while ((i = i - 1) !== -1)
2860                 {
2861                     if (this.isSet(value, intArray[i]))
2862                     {
2863                         return true;
2864                     }
2865                 }
2866                 return false;
2867             }
2868         },
2869 
2870         /** @private */
2871         renderDojoGridOffScreen: function (grid) {
2872             var offscreenDiv = $("<div style='position: absolute; left: -5001px; width: 5000px;'></div>")[0];
2873             $(document.body).append(offscreenDiv);
2874             grid.placeAt(offscreenDiv);
2875             grid.startup();
2876             document.body.removeChild(offscreenDiv);
2877             return grid;
2878         },
2879 
2880         /** @private */
2881         initializeSearchInput: function(searchInput, changeCallback, callbackDelay, callbackScope, placeholderText) {
2882             var timerId = null,
2883                 theControl = typeof(searchInput) === "string" ? $("#" + searchInput) : $(searchInput),
2884                 theInputControl = theControl.find("input"),
2885                 theClearButton = theControl.find("a"),
2886                 inputControlWidthWithClear = 204,
2887                 inputControlWidthNoClear = 230,
2888                 sPreviousInput = theInputControl.val(),
2889                 /** @private **/
2890                 toggleClearButton = function(){
2891                     if (theInputControl.val() === "") {
2892                         theClearButton.hide();
2893                         theControl.removeClass("input-append");
2894                         theInputControl.width(inputControlWidthNoClear);
2895                     } else {
2896                         theInputControl.width(inputControlWidthWithClear);
2897                         theClearButton.show();
2898                         theControl.addClass("input-append");
2899                     }
2900                 };
2901 
2902             // set placeholder text
2903             theInputControl.attr('placeholder', placeholderText);
2904 
2905             theInputControl.unbind('keyup').bind('keyup', function() {
2906                 if (sPreviousInput !== theInputControl.val()) {
2907                     window.clearTimeout(timerId);
2908                     sPreviousInput = theInputControl.val();
2909                     timerId = window.setTimeout(function() {
2910                         changeCallback.call((callbackScope || window), theInputControl.val());
2911                         theInputControl[0].focus();
2912                     }, callbackDelay);
2913                 }
2914 
2915                 toggleClearButton();
2916             });
2917 
2918             theClearButton.bind('click', function() {
2919                 theInputControl.val('');
2920                 changeCallback.call((callbackScope || window), '');
2921 
2922                 toggleClearButton();
2923                 theInputControl[0].focus(); // jquery and dojo on the same page break jquery's focus() method
2924             });
2925 
2926             theInputControl.val("");
2927             toggleClearButton();
2928         },
2929 
2930         DataTables: {
2931             /** @private */
2932             createDataTable: function (options, dataTableOptions) {
2933                 var grid,
2934                     table = $('<table cellpadding="0" cellspacing="0" border="0" class="finesse"><thead><tr></tr></thead></table>'),
2935                     headerRow = table.find("tr"),
2936                     defaultOptions = {
2937                         "aaData": [],
2938                         "bPaginate": false,
2939                         "bLengthChange": false,
2940                         "bFilter": false,
2941                         "bInfo": false,
2942                         "sScrollY": "176",
2943                         "oLanguage": {
2944                             "sEmptyTable": "",
2945                             "sZeroRecords": ""
2946                         }
2947                     },
2948                     gridOptions = $.extend({}, defaultOptions, dataTableOptions),
2949                     columnDefs = [],
2950                     columnFormatter;
2951 
2952                 // Create a header cell for each column, and set up the datatable definition for the column
2953                 $(options.columns).each(function (index, column) {
2954                     headerRow.append($("<th></th>"));
2955                     columnDefs[index] = {
2956                         "mData": column.propertyName,
2957                         "sTitle": column.columnHeader,
2958                         "sWidth": column.width,
2959                         "aTargets": [index],
2960                         "bSortable": column.sortable,
2961                         "bVisible": column.visible,
2962                         "mRender": column.render
2963                     };
2964                     if (typeof(column.renderFunction) === "function")
2965                     {
2966                         /** @ignore **/
2967                         columnDefs[index].mRender = /** @ignore **/ function (value, type, dataObject) { 
2968                             var returnValue;
2969 
2970                             //Apply column render logic to value before applying extra render function
2971                             if (typeof(column.render) === "function")
2972                             {
2973                                 value = column.render.call(value, value, value);
2974                             }
2975 
2976                             if (typeof(type) === "string")
2977                             {
2978                                 switch (type)
2979                                 {
2980                                 case "undefined":
2981                                 case "sort":
2982                                     returnValue = value;
2983                                     break;
2984                                 case "set":
2985                                     throw new Error("Unsupported set data in Finesse Grid");
2986                                 case "filter":
2987                                 case "display":
2988                                 case "type":
2989                                     returnValue = column.renderFunction.call(dataObject, value, dataObject);
2990                                     break;
2991                                 default:
2992                                     break;
2993                                 }
2994                             }
2995                             else
2996                             {
2997                                 throw new Error("type param not specified in Finesse DataTable mData");
2998                             }
2999 
3000                             return  returnValue;
3001                         };
3002                     }
3003                 });
3004                 gridOptions.aoColumnDefs = columnDefs;
3005 
3006                 // Set the height
3007                 if (typeof(options.bodyHeightPixels) !== "undefined" && options.bodyHeightPixels !== null)
3008                 {
3009                     gridOptions.sScrollY = options.bodyHeightPixels + "px";
3010                 }
3011 
3012                 // Place it into the DOM
3013                 if (typeof(options.container) !== "undefined" && options.container !== null)
3014                 {
3015                     $(options.container).append(table);
3016                 }
3017 
3018                 // Create the DataTable
3019                 table.dataTable(gridOptions);
3020 
3021                 return table;
3022             }
3023         },
3024         
3025         /**
3026          * @private
3027          * Sets a dojo button to the specified disable state, removing it from
3028          * the tab order if disabling, and restoring it to the tab order if enabling.
3029          * @param {Object} dojoButton Reference to the dijit.form.Button object. This is not the DOM element.
3030          * @param {bool} disabled
3031          */
3032         setDojoButtonDisabledAttribute: function (dojoButton, disabled) {
3033             var labelNode,
3034                 tabIndex;
3035 
3036             dojoButton.set("disabled", disabled);
3037 
3038             // Remove the tabindex attribute on disabled buttons, store it, 
3039             // and replace it when it becomes enabled again
3040             labelNode = $("#" + dojoButton.id + "_label");
3041             if (disabled)
3042             {
3043                 labelNode.data("finesse:dojoButton:tabIndex", labelNode.attr("tabindex"));
3044                 labelNode.removeAttr("tabindex");
3045             }
3046             else
3047             {
3048                 tabIndex = labelNode.data("finesse:dojoButton:tabIndex");
3049                 if (typeof(tabIndex) === "string")
3050                 {
3051                     labelNode.attr("tabindex", Number(tabIndex));
3052                 }
3053             }
3054         },
3055 
3056         /**
3057          * @private
3058          * Use this utility to disable the tab stop for a Dojo Firebug iframe within a gadget.
3059          *
3060          * Dojo sometimes adds a hidden iframe for enabling a firebug lite console in older
3061          * browsers. Unfortunately, this adds an additional tab stop that impacts accessibility.
3062          */
3063         disableTabStopForDojoFirebugIframe: function () {
3064             var iframe = $("iframe[src*='loadFirebugConsole']");
3065 
3066             if ((iframe.length) && (iframe.attr("tabIndex") !== "-1")) {
3067                 iframe.attr('tabIndex', '-1'); 
3068             }
3069         },
3070 
3071         /**
3072          * @private
3073          * Measures the given text using the supplied fontFamily and fontSize
3074          * @param  {string} text       text to measure
3075          * @param  {string} fontFamily
3076          * @param  {string} fontSize
3077          * @return {number} pixel width
3078          */
3079         measureText: function (text, fontFamily, fontSize) {
3080             var width,
3081                 element = $("<div></div>").text(text).css({
3082                     "fontSize": fontSize,
3083                     "fontFamily": fontFamily
3084                 }).addClass("offscreen").appendTo(document.body);
3085 
3086             width = element.width();
3087             element.remove();
3088 
3089             return width;
3090         },
3091 
3092         /**
3093          * Adjusts the gadget height. Shindig's gadgets.window.adjustHeight fails when
3094          * needing to resize down in IE. This gets around that by calculating the height
3095          * manually and passing it in.
3096          * @return {undefined}
3097          */
3098         "adjustGadgetHeight": function () {
3099             var bScrollHeight = $("body").height() + 20;
3100             gadgets.window.adjustHeight(bScrollHeight);
3101         },
3102 
3103         /**
3104         * Private helper method for converting a javascript object to xml, where the values of the elements are
3105         * appropriately escaped for XML.
3106         * This is a simple implementation that does not implement cdata or attributes. It is also 'unformatted' in that
3107         * there is no whitespace between elements.
3108         * @param object The javascript object to convert to XML.
3109         * @returns The XML string.
3110         * @private
3111         */
3112         _json2xmlWithEscape: function(object) {
3113             var that = this,
3114                 xml = "",
3115                 m,
3116                 /** @private **/
3117                 toXmlHelper = function(value, name) {
3118                 var xml = "",
3119                     i,
3120                     m;
3121                 if (value instanceof Array) {
3122                     for (i = 0; i < value.length; ++i) {
3123                         xml += toXmlHelper(value[i], name);
3124                     }
3125                 }
3126                 else if (typeof value === "object") {
3127                     xml += "<" + name + ">";
3128                     for (m in value) {
3129                         if (value.hasOwnProperty(m)) {
3130                            xml += toXmlHelper(value[m], m);
3131                         }
3132                     }
3133                     xml += "</" + name + ">";
3134                 }
3135                 else {
3136                     // is a leaf node
3137                     xml += "<" + name + ">" + that.translateHTMLEntities(value.toString(), true) +
3138                         "</" + name + ">";
3139                 }
3140                 return xml;
3141             };
3142             for (m in object) {
3143                 if (object.hasOwnProperty(m)) {
3144                     xml += toXmlHelper(object[m], m);
3145                 }
3146             }
3147             return xml;
3148         },
3149 
3150         /**
3151          * Private method for returning a sanitized version of the user agent string.
3152          * @returns the user agent string, but sanitized!
3153          * @private
3154          */
3155         getSanitizedUserAgentString: function () {
3156             return this.translateXMLEntities(navigator.userAgent, true);
3157         },
3158 
3159         /**
3160          * Use JQuery's implementation of Promises (Deferred) to execute code when 
3161          * multiple async processes have finished. An example use:
3162          *
3163          * var asyncProcess1 = $.Deferred(),
3164          *     asyncProcess2 = $.Deferred();
3165          *     
3166          * finesse.utilities.Utilities.whenAllDone(asyncProcess1, asyncProcess2) // WHEN both asyncProcess1 and asyncProcess2 are resolved or rejected ...
3167          *     .then(
3168          *         // First function passed to then() is called when all async processes are complete, regardless of errors
3169          *         function () {
3170          *             console.log("all processes completed");
3171          *         },
3172          *         // Second function passed to then() is called if any async processed threw an exception
3173          *         function (failures) { // Array of failure messages
3174          *             console.log("Number of failed async processes: " + failures.length);
3175          *         });
3176          *
3177          * Found at:
3178          * http://stackoverflow.com/a/15094263/1244030
3179          *
3180          * Pass in any number of $.Deferred instances.
3181          * @returns {Object}
3182          */
3183         whenAllDone: function () {
3184             var deferreds = [],
3185                 result = $.Deferred();
3186 
3187             $.each(arguments, function(i, current) {
3188                 var currentDeferred = $.Deferred();
3189                 current.then(function() {
3190                     currentDeferred.resolve(false, arguments);
3191                 }, function() {
3192                     currentDeferred.resolve(true, arguments);
3193                 });
3194                 deferreds.push(currentDeferred);
3195             });
3196 
3197             $.when.apply($, deferreds).then(function() {
3198                 var failures = [],
3199                     successes = [];
3200 
3201                 $.each(arguments, function(i, args) {
3202                     // If we resolved with `true` as the first parameter
3203                     // we have a failure, a success otherwise
3204                     var target = args[0] ? failures : successes,
3205                         data = args[1];
3206                     // Push either all arguments or the only one
3207                     target.push(data.length === 1 ? data[0] : args);
3208                 });
3209 
3210                 if(failures.length) {
3211                     return result.reject.apply(result, failures);
3212                 }
3213 
3214                 return result.resolve.apply(result, successes);
3215             });
3216 
3217             return result;
3218         },
3219 
3220         /**
3221          * Private method to format a given string by replacing the place holders (like {0}) with the
3222          * corresponding supplied arguments. For example, calling this method as follows:
3223          *     formatString("Hello {0}, {1} rocks!", "there", "Finesse");
3224          * results in the following output string:
3225          *     "Hello there, Finesse rocks!"
3226          * 
3227          * @param  {String} format - a string that holds the place holder to be replaced
3228          * 
3229          * @returns {String} - string where the place holders are replaced with respective values
3230          * @private
3231          */
3232         formatString : function(format) {
3233             if (!format || arguments.length <= 1) {
3234                 return format;
3235             }
3236 
3237             var i, retStr = format;
3238             for (i = 1; i < arguments.length; i += 1) {
3239                 retStr = retStr.replace(new RegExp("\\{" + (i - 1) + "\\}", "g"), arguments[i]);
3240             }
3241 
3242             // in order to fix French text with single quotes in it, we need to replace \' with '
3243             return retStr.replace(/\\'/g, "'");
3244         },
3245         
3246         /**
3247          *  @private
3248          *  This method is used to make the scheme and port secure when the admin gadgets are loaded in some other container.
3249          *  @param {object} config - The config which is passed to client services to handle restRequest in secure or non secure mode.  
3250          *  
3251          *  @returns {object} config - The modified config if scheme was https.
3252          */
3253         setSchemeAndPortForHttps : function(config) {
3254 			var scheme = window.location.protocol;
3255 			if(scheme === "https:") {
3256 				config["restScheme"] = "https";
3257 				config["localhostPort"] = "8445";
3258 			}
3259 			
3260 			return config;
3261         },
3262 
3263         /**
3264          * 
3265          * @private
3266          * If the browser tab is inactive, any calls to javascript function setTimeout(0,...) will not be executed
3267          * immediately since browser throttles events from background tabs. The delay sometimes could be about 5 seconds.
3268          * This utilitiy function provides a wrapper around ES5 promise to resolve this issue.
3269          * 
3270          * @param {function} funcOther Target function that will be invoked at the end of browser events.
3271          * 
3272          * @returns {Object} The promise
3273          */
3274         executeAsync: function(funcOther) {
3275             var promise = new Promise(
3276                 function( resolve, reject ) {              
3277                     resolve();
3278                 }
3279             );
3280             promise.then( funcOther );
3281 
3282             return promise;
3283         },
3284         
3285         /**
3286          * Extract hostname from a given url string
3287          */
3288         extractHostname : function (url) {
3289         	var hostname;
3290             //find & remove protocol (http, ftp, etc.) and get hostname
3291 
3292             if (url.indexOf("//") > -1) {
3293                 hostname = url.split('/')[2];
3294             }
3295             else {
3296                 hostname = url.split('/')[0];
3297             }
3298 
3299             //find & remove port number
3300             hostname = hostname.split(':')[0];
3301             //find & remove "?"
3302             hostname = hostname.split('?')[0];
3303 
3304             return hostname;
3305         },
3306         
3307         /**
3308          * Get the value of a querystring
3309          * @param  {String} field The field to get the value of
3310          * @param  {String} url   The URL to get the value from (optional)
3311          * @return {String}       The field value
3312          */
3313         getQueryString : function ( field, url ) {
3314             var href = url ? url : window.location.href;
3315             var reg = new RegExp( '[?&]' + field + '=([^&#]*)', 'i' );
3316             var string = reg.exec(href);
3317             return string ? decodeURIComponent(string[1]) : '';
3318         },
3319         
3320     };
3321 
3322     window.finesse = window.finesse || {};
3323     window.finesse.utilities = window.finesse.utilities || {};
3324     window.finesse.utilities.Utilities = Utilities;
3325     
3326     return Utilities;
3327 });
3328 
3329 /**
3330  * Allows gadgets to call the log function to publish client logging messages over the hub.
3331  *
3332  * @requires OpenAjax
3333  */
3334 /** @private */
3335 define('cslogger/ClientLogger',[], function () {
3336 
3337     var ClientLogger = ( function () { /** @lends finesse.cslogger.ClientLogger.prototype */
3338         var _hub, _logTopic, _originId, _sessId, _host,
3339             MONTH = { 0 : "Jan", 1 : "Feb", 2 : "Mar", 3 : "Apr", 4 : "May", 5 : "Jun", 
3340                       6 : "Jul", 7 : "Aug", 8 : "Sep", 9 : "Oct", 10 : "Nov", 11 : "Dec"},
3341  
3342         /**
3343          * Gets timestamp drift stored in sessionStorage
3344          * @returns drift in seconds if it is set in sessionStorage otherwise returns undefined.
3345          * @private
3346         */
3347         getTsDrift = function() {
3348             if (window.sessionStorage.getItem('clientTimestampDrift') !== null) {
3349                 return parseInt(window.sessionStorage.getItem('clientTimestampDrift'), 10);
3350             }
3351             else { 
3352                 return undefined;
3353             }
3354         },
3355          
3356         /**
3357           * Sets timestamp drift in sessionStorage
3358           * @param delta is the timestamp drift between server.and client.
3359           * @private
3360          */
3361         setTsDrift = function(delta) {
3362              window.sessionStorage.setItem('clientTimestampDrift', delta.toString());
3363         },
3364           
3365         /**
3366          * Gets Finesse server timezone offset from GMT in seconds 
3367          * @returns offset in seconds if it is set in sessionStorage otherwise returns undefined.
3368          * @private
3369         */
3370         getServerOffset = function() {
3371             if (window.sessionStorage.getItem('serverTimezoneOffset') !== null) {
3372                 return parseInt(window.sessionStorage.getItem('serverTimezoneOffset'), 10);
3373             }
3374             else { 
3375                 return undefined;
3376             }
3377         },
3378          
3379         /**
3380           * Sets server timezone offset 
3381           * @param sec is the server timezone GMT offset in seconds.
3382           * @private
3383          */
3384         setServerOffset = function(sec) {
3385              window.sessionStorage.setItem('serverTimezoneOffset', sec.toString());
3386         },
3387  
3388         /**
3389          * Checks to see if we have a console.
3390          * @returns Whether the console object exists.
3391          * @private
3392          */
3393         hasConsole = function () {
3394             try {
3395                 if (window.console !== undefined) {
3396                     return true;
3397                 }
3398             } 
3399             catch (err) {
3400               // ignore and return false
3401             }
3402     
3403             return false;
3404         },
3405         
3406         /**
3407          * Gets a short form (6 character) session ID from sessionStorage
3408          * @private
3409         */
3410         getSessId = function() {
3411             if (!_sessId) {
3412                //when _sessId not defined yet, initiate it
3413                if (window.sessionStorage.getItem('enableLocalLog') === 'true') {
3414                   _sessId= " "+window.sessionStorage.getItem('finSessKey');
3415                }
3416                else {
3417                   _sessId=" ";
3418                }
3419             }
3420             return _sessId;
3421          },
3422 
3423         /**
3424          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
3425          * @param num is the number to pad to 2 digits
3426          * @returns a two digit padded string
3427          * @private
3428          */
3429         padTwoDigits = function (num)
3430         {
3431             return (num < 10) ? '0' + num : num;
3432         },
3433         
3434         /**
3435          * Pads a single digit number for display purposes (e.g. '4' shows as '004')
3436          * @param num is the number to pad to 3 digits
3437          * @returns a three digit padded string
3438          * @private
3439          */
3440         padThreeDigits = function (num)
3441         {
3442             if (num < 10)
3443             {
3444               return '00'+num;
3445             }
3446             else if (num < 100)
3447             {
3448               return '0'+num;
3449             }
3450             else  
3451             {
3452                return num;
3453             }
3454         },
3455               
3456         /**
3457          * Compute the "hour"
3458          * 
3459          * @param s is time in seconds
3460          * @returns {String} which is the hour
3461          * @private
3462          */
3463         ho = function (s) {
3464              return ((s/60).toString()).split(".")[0];
3465         },
3466           
3467         /**
3468          * Gets local timezone offset string.
3469          * 
3470          * @param t is the time in seconds
3471          * @param s is the separator character between hours and minutes, e.g. ':'
3472          * @returns {String} is local timezone GMT offset in the following format: [+|-]hh[|:]MM
3473          * @private
3474          */
3475         getGmtOffString = function (min,s) {
3476             var t, sign;
3477             if (min<0) {
3478                t = -min;
3479                sign = "-";
3480             }
3481             else {
3482                t = min;
3483                sign = "+";
3484             }
3485             
3486             if (s===':') {
3487                 return sign+padTwoDigits(ho(t))+s+padTwoDigits(t%60);
3488             }
3489             else {
3490                 return sign+padTwoDigits(ho(t))+padTwoDigits(t%60);
3491             }    
3492         },
3493 
3494         /**
3495          * Gets short form of a month name in English 
3496          * 
3497          * @param monthNum is zero-based month number 
3498          * @returns {String} is short form of month name in English
3499          * @private
3500          */
3501         getMonthShortStr = function (monthNum) {
3502             var result;
3503             try {
3504                 result = MONTH[monthNum];
3505             } 
3506             catch (err) {
3507                 if (hasConsole()) {
3508                     window.console.log("Month must be between 0 and 11");
3509                 }
3510             }
3511             return result;
3512         },
3513           
3514         /**
3515           * Gets a timestamp.
3516           * @param aDate is a javascript Date object
3517           * @returns {String} is a timestamp in the following format: yyyy-mm-ddTHH:MM:ss.SSS [+|-]HH:MM
3518           * @private
3519           */
3520         getDateTimeStamp = function (aDate)
3521         {
3522             var date, off, timeStr;
3523             if (aDate === null) {
3524                 date = new Date();
3525             }
3526             else {
3527                 date = aDate;
3528             }
3529             off = -1*date.getTimezoneOffset();
3530             timeStr = date.getFullYear().toString() + "-" +
3531                       padTwoDigits(date.getMonth()+1) + "-" +
3532                       padTwoDigits (date.getDate()) + "T"+
3533                       padTwoDigits(date.getHours()) + ":" + 
3534                       padTwoDigits(date.getMinutes()) + ":" +
3535                       padTwoDigits(date.getSeconds())+"." + 
3536                       padThreeDigits(date.getMilliseconds()) + " "+
3537                       getGmtOffString(off, ':');
3538     
3539             return timeStr;
3540         },
3541         
3542         /**
3543          * Gets drift-adjusted timestamp.
3544          * @param aTimestamp is a timestamp in milliseconds
3545          * @param drift is a timestamp drift in milliseconds
3546          * @param serverOffset is a timezone GMT offset in minutes
3547          * @returns {String} is a timestamp in the Finesse server log format, e.g. Jan 07 2104 HH:MM:ss.SSS -0500
3548          * @private
3549          */
3550         getDriftedDateTimeStamp = function (aTimestamp, drift, serverOffset)
3551         {
3552            var date, timeStr, localOffset;
3553            if (aTimestamp === null) {
3554                return "--- -- ---- --:--:--.--- -----";
3555            }
3556            else if (drift === undefined || serverOffset === undefined) {
3557                if (hasConsole()) {
3558                    window.console.log("drift or serverOffset must be a number");
3559                }
3560                return "--- -- ---- --:--:--.--- -----";
3561            }
3562            else {
3563                //need to get a zone diff in minutes
3564                localOffset = (new Date()).getTimezoneOffset();
3565                date = new Date(aTimestamp+drift+(localOffset+serverOffset)*60000);
3566                timeStr = getMonthShortStr(date.getMonth()) + " "+
3567                          padTwoDigits (date.getDate())+ " "+
3568                          date.getFullYear().toString() + " "+
3569                          padTwoDigits(date.getHours()) + ":" + 
3570                          padTwoDigits(date.getMinutes()) + ":" +
3571                          padTwoDigits(date.getSeconds())+"." + 
3572                          padThreeDigits(date.getMilliseconds())+" "+
3573                          getGmtOffString(serverOffset, '');
3574                 return timeStr;
3575             }
3576         },
3577     
3578         /**
3579         * Logs a message to a hidden textarea element on the page
3580         *
3581         * @param msg is the string to log.
3582         * @private
3583         */
3584         writeToLogOutput = function (msg) {
3585             var logOutput = document.getElementById("finesseLogOutput");
3586     
3587             if (logOutput === null)
3588             {
3589                 logOutput = document.createElement("textarea");
3590                 logOutput.id = "finesseLogOutput";
3591                 logOutput.style.display = "none";
3592                 document.body.appendChild(logOutput);
3593             }
3594     
3595             if (logOutput.value === "")
3596             {
3597                 logOutput.value = msg;
3598             }
3599             else
3600             {
3601                 logOutput.value = logOutput.value + "\n" + msg;
3602             }
3603         },
3604 
3605         /*
3606          * Logs a message to console 
3607         * @param str is the string to log.         * @private
3608          */
3609         logToConsole = function (str, error)
3610         {
3611             var now, msg, timeStr, driftedTimeStr, sessKey=getSessId();
3612             now = new Date();
3613             timeStr = getDateTimeStamp(now);
3614             if (getTsDrift() !== undefined) {
3615                 driftedTimeStr = getDriftedDateTimeStamp(now.getTime(), getTsDrift(), getServerOffset());
3616             }
3617             else {
3618                driftedTimeStr = getDriftedDateTimeStamp(null, 0, 0);
3619             }
3620             msg = timeStr + ":"+sessKey+": "+ _host + ": "+driftedTimeStr+ ": " + str;
3621             // Log to console
3622             if (hasConsole()) {
3623                 if (error) {
3624                     window.console.error(msg, error);
3625                 } else {
3626                     window.console.log(msg);
3627                 }
3628             }
3629     
3630             //Uncomment to print logs to hidden textarea.
3631             //writeToLogOutput(msg);
3632     
3633             return msg;
3634         };
3635         return {
3636     
3637             /**
3638              * Publishes a Log Message over the hub.
3639              *
3640              * @param {String} message
3641              *     The string to log.
3642              * @param {Object} error - optional
3643              *     Javascript error object
3644              * @example
3645              * _clientLogger.log("This is some important message for MyGadget");
3646              * 
3647              */
3648             log : function (message, error) {
3649                 if(_hub) {
3650                     _hub.publish(_logTopic, logToConsole(_originId + message, error));
3651                 }
3652             },
3653             
3654             /**
3655              * @class
3656              * Allows gadgets to call the log function to publish client logging messages over the hub.
3657              * 
3658              * @constructs
3659              */
3660             _fakeConstuctor: function () {
3661                 /* This is here so we can document init() as a method rather than as a constructor. */
3662             },
3663             
3664             /**
3665              * Initiates the client logger with a hub a gadgetId and gadget's config object.
3666              * @param {Object} hub
3667              *      The hub to communicate with.
3668              * @param {String} gadgetId
3669              *      A unique string to identify which gadget is doing the logging.
3670              * @param {finesse.gadget.Config} config
3671              *      The config object used to get host name for that thirdparty gadget
3672              * @example
3673              * var _clientLogger = finesse.cslogger.ClientLogger;
3674              * _clientLogger.init(gadgets.Hub, "MyGadgetId", config);
3675              * 
3676              */
3677             init: function (hub, gadgetId, config) {
3678                 _hub = hub;
3679                 _logTopic = "finesse.clientLogging." + gadgetId;
3680                 _originId = gadgetId + " : ";
3681                 if ((config === undefined) || (config === "undefined")) 
3682                 {
3683                      _host = ((finesse.container && finesse.container.Config && finesse.container.Config.host)?finesse.container.Config.host : "?.?.?.?");
3684                  } 
3685                 else 
3686                 {
3687                      _host = ((config && config.host)?config.host : "?.?.?.?");
3688                  }
3689             }
3690         };
3691     }());
3692     
3693     window.finesse = window.finesse || {};
3694     window.finesse.cslogger = window.finesse.cslogger || {};
3695     window.finesse.cslogger.ClientLogger = ClientLogger;
3696     
3697     finesse = finesse || {};
3698     /** @namespace Supports writing messages to a central log. */
3699     finesse.cslogger = finesse.cslogger || {};
3700 
3701     return ClientLogger;
3702 });
3703 
3704 /** The following comment is to prevent jslint errors about 
3705  * using variables before they are defined.
3706  */
3707 /*global finesse*/
3708 
3709 /**
3710  * Initiated by the Master to create a shared BOSH connection.
3711  *
3712  * @requires Utilities
3713  */
3714 
3715 /**
3716  * @class
3717  * Establishes a shared event connection by creating a communication tunnel
3718  * with the notification server and consume events which could be published.
3719  * Public functions are exposed to register to the connection status information
3720  * and events.
3721  * @constructor
3722  * @param {String} host
3723  *     The host name/ip of the Finesse server.
3724  * @throws {Error} If required constructor parameter is missing.
3725  */
3726 /** @private */
3727 define('clientservices/MasterTunnel',["utilities/Utilities", "cslogger/ClientLogger"], function (Utilities, ClientLogger) {
3728      var MasterTunnel = function (host, scheme) { 
3729         if (typeof host !== "string" || host.length === 0) {
3730             throw new Error("Required host parameter missing.");
3731         }
3732 
3733         var
3734 
3735         /**
3736          * Flag to indicate whether the tunnel frame is loaded.
3737          * @private
3738          */
3739         _isTunnelLoaded = false,
3740 
3741         /**
3742          * Short reference to the Finesse utility.
3743          * @private
3744          */
3745         _util = Utilities,
3746 
3747         /**
3748          * The URL with host and port to the Finesse server.
3749          * @private
3750          */
3751         _tunnelOrigin,
3752 
3753         /**
3754          * Location of the tunnel HTML URL.
3755          * @private
3756          */
3757         _tunnelURL,
3758         
3759         /**
3760          * The port on which to connect to the Finesse server to load the eventing resources.
3761          * @private
3762          */
3763         _tunnelOriginPort,
3764         
3765         /**
3766          * Flag to indicate whether we have processed the tunnel config yet.
3767          * @private
3768          */
3769         _isTunnelConfigInit = false,
3770 
3771         /**
3772          * The tunnel frame window object.
3773          * @private
3774          */
3775         _tunnelFrame,
3776 
3777         /**
3778          * The handler registered with the object to be invoked when an event is
3779          * delivered by the notification server.
3780          * @private
3781          */
3782         _eventHandler,
3783         
3784         /**
3785          * The handler registered with the object to be invoked when presence is
3786          * delivered by the notification server.
3787          * @private
3788          */
3789         _presenceHandler,
3790 
3791         /**
3792          * The handler registered with the object to be invoked when the BOSH
3793          * connection has changed states. The object will contain the "status"
3794          * property and a "resourceID" property only if "status" is "connected".
3795          * @private
3796          */
3797         _connInfoHandler,
3798 
3799         /**
3800          * The last connection status published by the JabberWerx library.
3801          * @private
3802          */
3803         _statusCache,
3804 
3805         /**
3806          * The last event sent by notification server.
3807          * @private
3808          */
3809         _eventCache,
3810 
3811         /**
3812          * The ID of the user logged into notification server.
3813          * @private
3814          */
3815         _id,
3816    
3817         /**
3818          * The domain of the XMPP server, representing the portion of the JID
3819          * following '@': userid@domain.com
3820          * @private
3821          */
3822         _xmppDomain,
3823 
3824         /**
3825          * The password of the user logged into notification server.
3826          * @private
3827          */
3828         _password,
3829 
3830         /**
3831          * The jid of the pubsub service on the XMPP server
3832          * @private
3833          */
3834         _pubsubDomain,
3835 
3836         /**
3837          * The resource to use for the BOSH connection.
3838          * @private
3839          */
3840         _resource,
3841 
3842         /**
3843          * The resource ID identifying the client device (that we receive from the server).
3844          * @private
3845          */
3846         _resourceID,
3847         
3848         /**
3849          * The xmpp connection protocol type.
3850          * @private
3851          */
3852         _notificationConnectionType,
3853 
3854         /**
3855          * The different types of messages that could be sent to the parent frame.
3856          * The types here should be understood by the parent frame and used to
3857          * identify how the message is formatted.
3858          * @private
3859          */
3860         _TYPES = {
3861             EVENT: 0,
3862             ID: 1,
3863             PASSWORD: 2,
3864             RESOURCEID: 3,
3865             STATUS: 4,
3866             XMPPDOMAIN: 5,
3867             PUBSUBDOMAIN: 6,
3868             SUBSCRIBE: 7,
3869             UNSUBSCRIBE: 8,
3870             PRESENCE: 9,
3871             CONNECT_REQ: 10,
3872             DISCONNECT_REQ: 11,
3873             NOTIFICATION_CONNECTION_TYPE: 12,
3874             LOGGING: 13,
3875             SUBSCRIPTIONS_REQ: 14
3876         },
3877 
3878         _handlers = {
3879             subscribe: {},
3880             unsubscribe: {}
3881         },
3882         
3883 
3884         /**
3885          * Create a connection info object.
3886          * @returns {Object}
3887          *     A connection info object containing a "status" and "resourceID".
3888          * @private
3889          */
3890         _createConnInfoObj = function () {
3891             return {
3892                 status: _statusCache,
3893                 resourceID: _resourceID
3894             };
3895         },
3896 
3897         /**
3898          * Utility function which sends a message to the dynamic tunnel frame
3899          * event frame formatted as follows: "type|message".
3900          * @param {Number} type
3901          *     The category type of the message.
3902          * @param {String} message
3903          *     The message to be sent to the tunnel frame.
3904          * @private
3905          */
3906         _sendMessage = function (type, message) {
3907             message = type + "|" + message;
3908             _util.sendMessage(message, _tunnelFrame, _tunnelOrigin);
3909         },
3910 
3911         /**
3912          * Utility to process the response of a subscribe request from
3913          * the tunnel frame, then invoking the stored callback handler
3914          * with the respective data (error, when applicable)
3915          * @param {String} data
3916          *     The response in the format of "node[|error]"
3917          * @private
3918          */
3919         _processSubscribeResponse = function (data) {
3920             var dataArray = data.split("|"),
3921             node = dataArray[0],
3922             err;
3923             
3924             //Error is optionally the second item in the array
3925             if (dataArray.length) {
3926                 err = dataArray[1];
3927             }
3928             
3929             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
3930             if (_handlers.subscribe[node]) {
3931                 _handlers.subscribe[node](err);
3932                 delete _handlers.subscribe[node];
3933             }
3934         },
3935 
3936         /**
3937          * Utility to process the response of an unsubscribe request from
3938          * the tunnel frame, then invoking the stored callback handler
3939          * with the respective data (error, when applicable)
3940          * @param {String} data
3941          *     The response in the format of "node[|error]"
3942          * @private
3943          */
3944         _processUnsubscribeResponse = function (data) {
3945             var dataArray = data.split("|"),
3946             node = dataArray[0],
3947             err;
3948             
3949             //Error is optionally the second item in the array
3950             if (dataArray.length) {
3951                 err = dataArray[1];
3952             }
3953             
3954             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
3955             if (_handlers.unsubscribe[node]) {
3956                 _handlers.unsubscribe[node](err);
3957                 delete _handlers.unsubscribe[node];
3958             }
3959         },
3960 
3961 
3962         /**
3963          * Utility to process the reponse of an getSubscriptions request from
3964          * the tunnel frame, then invoking the stored callback handler
3965          * with the respective data (error, when applicable)
3966          * @param {String} data
3967          *     The response containing subscriptions
3968          * @private
3969          */
3970         _processAllSubscriptionsResponse = function (data) {
3971             var dataArray = data.split("|"),
3972             content = dataArray[0],
3973             err;
3974 
3975             //Error is optionally the second item in the array
3976             if (dataArray.length) {
3977                 err = dataArray[1];
3978             }
3979             
3980             // These response handlers are short lived and should be removed and cleaned up immediately after invocation.
3981             if (_handlers.subscriptions) {
3982                 _handlers.subscriptions(content, err);
3983                 delete _handlers.subscriptions;
3984             }
3985         },
3986 
3987         /**
3988          * Handler for messages delivered by window.postMessage. Listens for events
3989          * published by the notification server, connection status published by
3990          * the JabberWerx library, and the resource ID created when the BOSH
3991          * connection has been established.
3992          * @param {Object} e
3993          *     The message object as provided by the window.postMessage feature.
3994          * @private
3995          */
3996         _messageHandler = function (e) {
3997             var
3998 
3999             //Extract the message type and message data. The expected format is
4000             //"type|data" where type is a number represented by the TYPES object.
4001             delimPos = e.data.indexOf("|"),
4002             type = Number(e.data.substr(0, delimPos)),
4003             data =  e.data.substr(delimPos + 1);
4004             
4005             //Accepts messages and invoke the correct registered handlers.
4006             switch (type) {
4007             case _TYPES.EVENT:
4008                 _eventCache = data;
4009                 if (typeof _eventHandler === "function") {
4010                     _eventHandler(data);
4011                 }
4012                 break;
4013             case _TYPES.STATUS:
4014                 _statusCache = data;
4015 
4016                 //A "loaded" status means that the frame is ready to accept
4017                 //credentials for establishing a BOSH connection.
4018                 if (data === "loaded") {
4019                     _isTunnelLoaded = true;
4020                     if(_resource) {
4021                         _sendMessage(_TYPES.RESOURCEID, _resource);
4022                     }
4023                     
4024                	    _sendMessage(_TYPES.NOTIFICATION_CONNECTION_TYPE, _notificationConnectionType);
4025                     _sendMessage(_TYPES.ID, _id);
4026                     _sendMessage(_TYPES.XMPPDOMAIN, _xmppDomain);
4027                     _sendMessage(_TYPES.PASSWORD, _password);
4028                     _sendMessage(_TYPES.PUBSUBDOMAIN, _pubsubDomain);
4029 
4030 
4031                 } else if (typeof _connInfoHandler === "function") {
4032                     _connInfoHandler(_createConnInfoObj());
4033                 }
4034                 break;
4035             case _TYPES.RESOURCEID:
4036                 _resourceID = data;
4037                 break;
4038             case _TYPES.SUBSCRIBE:
4039                 _processSubscribeResponse(data);
4040                 break;
4041             case _TYPES.UNSUBSCRIBE:
4042                 _processUnsubscribeResponse(data);
4043                 break;
4044             case _TYPES.SUBSCRIPTIONS_REQ:
4045                 _processAllSubscriptionsResponse(data);
4046                 break;
4047             case _TYPES.PRESENCE:
4048                 if (typeof _presenceHandler === "function") {
4049                     _presenceHandler(data);
4050                 }
4051                 break;
4052             case _TYPES.LOGGING:
4053             	ClientLogger.log(data);
4054                 break;
4055             default:
4056                 break;
4057             }
4058         },
4059 
4060         /**
4061          * Initialize the tunnel config so that the url can be http or https with the appropriate port
4062          * @private
4063          */
4064         _initTunnelConfig = function () {
4065             if (_isTunnelConfigInit === true) {
4066                 return;
4067             }
4068             
4069             //Initialize tunnel origin
4070             //Determine tunnel origin based on host and scheme
4071             _tunnelOriginPort = (scheme && scheme.indexOf("https") !== -1) ? "7443" : "7071";
4072             if (scheme) {
4073                 _tunnelOrigin = scheme + "://" + host + ":" + _tunnelOriginPort;
4074             } else {
4075                 _tunnelOrigin = "http://" + host + ":" + _tunnelOriginPort;
4076             }
4077             _tunnelURL = _tunnelOrigin + "/tunnel/";
4078             
4079             _isTunnelConfigInit = true;
4080         },
4081 
4082         /**
4083          * Create the tunnel iframe which establishes the shared BOSH connection.
4084          * Messages are sent across frames using window.postMessage.
4085          * @private
4086          */
4087         _createTunnel = function () {
4088             var tunnelID = ((self === parent) ? "tunnel-frame" : "autopilot-tunnel-frame"),
4089             iframe = document.createElement("iframe");         
4090             iframe.style.display = "none";
4091             iframe.setAttribute("id", tunnelID);
4092             iframe.setAttribute("name", tunnelID);
4093             iframe.setAttribute("src", _tunnelURL);
4094             document.body.appendChild(iframe);
4095             _tunnelFrame = window.frames[tunnelID];
4096         };
4097 
4098         /**
4099          * Sends a message via postmessage to the EventTunnel to attempt to connect to the XMPP server
4100          * @private
4101          */
4102         this.makeConnectReq = function () {
4103             _sendMessage(_TYPES.PASSWORD, _password);
4104         };
4105         
4106         /**
4107          * @private
4108          * Returns the host of the Finesse server.
4109          * @returns {String}
4110          *     The host specified during the creation of the object.
4111          */
4112         this.getHost = function () {
4113             return host;
4114         };
4115 
4116         /**
4117          * @private
4118          * The resource ID of the user who is logged into the notification server.
4119          * @returns {String}
4120          *     The resource ID generated by the notification server.
4121          */
4122         this.getResourceID = function () {
4123             return _resourceID;
4124         };
4125 
4126         /**
4127          * @private
4128          * Indicates whether the tunnel frame is loaded.
4129          * @returns {Boolean}
4130          *     True if the tunnel frame is loaded, false otherwise.
4131          */
4132         this.isTunnelLoaded = function () {
4133             return _isTunnelLoaded;
4134         };
4135 
4136         /**
4137          * @private
4138          * The location of the tunnel HTML URL.
4139          * @returns {String}
4140          *     The location of the tunnel HTML URL.
4141          */
4142         this.getTunnelURL = function () {
4143             return _tunnelURL;
4144         };
4145 
4146         /**
4147          * @private
4148          * Tunnels a subscribe request to the eventing iframe.
4149          * @param {String} node
4150          *     The node to subscribe to
4151          * @param {Function} handler
4152          *     Handler to invoke upon success or failure
4153          */
4154         this.subscribe = function (node, handler) {
4155             if (handler && typeof handler !== "function") {
4156                 throw new Error("Parameter is not a function.");
4157             }
4158             _handlers.subscribe[node] = handler;
4159             _sendMessage(_TYPES.SUBSCRIBE, node);
4160         };
4161 
4162 
4163         /**
4164          * @private
4165          * Tunnels a get subscription request to the eventing iframe.
4166          * @param {Function} handler
4167          *     Handler to invoke upon success or failure
4168          */
4169         this.getSubscriptions = function (handler) {
4170             if (handler && typeof handler !== "function") {
4171                 throw new Error("Parameter is not a function.");
4172             }
4173             _handlers.subscriptions = handler;
4174             _sendMessage(_TYPES.SUBSCRIPTIONS_REQ);
4175         };
4176 
4177         /**
4178          * @private
4179          * Tunnels an unsubscribe request to the eventing iframe.
4180          * @param {String} node
4181          *     The node to unsubscribe from
4182          * @param {Function} handler
4183          *     Handler to invoke upon success or failure
4184          */
4185         this.unsubscribe = function (node, handler) {
4186             if (handler && typeof handler !== "function") {
4187                 throw new Error("Parameter is not a function.");
4188             }
4189             _handlers.unsubscribe[node] = handler;
4190             _sendMessage(_TYPES.UNSUBSCRIBE, node);
4191         };
4192 
4193         /**
4194          * @private
4195          * Registers a handler to be invoked when an event is delivered. Only one
4196          * is registered at a time. If there has already been an event that was
4197          * delivered, the handler will be invoked immediately.
4198          * @param {Function} handler
4199          *     Invoked when an event is delivered through the event connection.
4200          */
4201         this.registerEventHandler = function (handler) {
4202             if (typeof handler !== "function") {
4203                 throw new Error("Parameter is not a function.");
4204             }
4205             _eventHandler = handler;
4206             if (_eventCache) {
4207                 handler(_eventCache);
4208             }
4209         };
4210 
4211         /**
4212          * @private
4213          * Unregisters the event handler completely.
4214          */
4215         this.unregisterEventHandler = function () {
4216             _eventHandler = undefined;
4217         };
4218         
4219         /**
4220          * @private
4221          * Registers a handler to be invoked when a presence event is delivered. Only one
4222          * is registered at a time. 
4223          * @param {Function} handler
4224          *     Invoked when a presence event is delivered through the event connection.
4225          */
4226         this.registerPresenceHandler = function (handler) {
4227             if (typeof handler !== "function") {
4228                 throw new Error("Parameter is not a function.");
4229             }
4230             _presenceHandler = handler;
4231         };
4232         
4233         /**
4234          * @private
4235          * Unregisters the presence event handler completely.
4236          */
4237         this.unregisterPresenceHandler = function () {
4238             _presenceHandler = undefined;
4239         };
4240 
4241         /**
4242          * @private
4243          * Registers a handler to be invoked when a connection status changes. The
4244          * object passed will contain a "status" property, and a "resourceID"
4245          * property, which will contain the most current resource ID assigned to
4246          * the client. If there has already been an event that was delivered, the
4247          * handler will be invoked immediately.
4248          * @param {Function} handler
4249          *     Invoked when a connection status changes.
4250          */
4251         this.registerConnectionInfoHandler = function (handler) {
4252             if (typeof handler !== "function") {
4253                 throw new Error("Parameter is not a function.");
4254             }
4255             _connInfoHandler = handler;
4256             if (_statusCache) {
4257                 handler(_createConnInfoObj());
4258             }
4259         };
4260 
4261         /**
4262          * @private
4263          * Unregisters the connection information handler.
4264          */
4265         this.unregisterConnectionInfoHandler = function () {
4266             _connInfoHandler = undefined;
4267         };
4268 
4269         /**
4270          * @private
4271          * Start listening for events and create a event tunnel for the shared BOSH
4272          * connection.
4273          * @param {String} id
4274          *     The ID of the user for the notification server.
4275          * @param {String} password
4276          *     The password of the user for the notification server.
4277          * @param {String} xmppDomain
4278          *     The XMPP domain of the notification server
4279          * @param {String} pubsubDomain
4280          *     The location (JID) of the XEP-0060 PubSub service
4281          * @param {String} resource
4282          *     The resource to connect to the notification servier with.
4283          * @param {String} notificationConnectionType
4284          *     The xmpp connection protocol type : websocket or BOSH.
4285          */
4286         this.init = function (id, password, xmppDomain, pubsubDomain, resource, notificationConnectionType) {
4287             
4288             if (typeof id !== "string" || typeof password !== "string" || typeof xmppDomain !== "string" || typeof pubsubDomain !== "string" || typeof notificationConnectionType !== "string") {
4289                 throw new Error("Invalid or missing required parameters.");
4290             }
4291 
4292             _initTunnelConfig();
4293             
4294             _id = id;
4295             _password = password;
4296             _xmppDomain = xmppDomain;
4297             _pubsubDomain = pubsubDomain;
4298             _resource = resource;
4299             _notificationConnectionType = notificationConnectionType;
4300 
4301             //Attach a listener for messages sent from tunnel frame.
4302             _util.receiveMessage(_messageHandler, _tunnelOrigin);
4303 
4304             //Create the tunnel iframe which will establish the shared connection.
4305             _createTunnel();
4306         };
4307 
4308         //BEGIN TEST CODE//
4309 //        /**
4310 //         * Test code added to expose private functions that are used by unit test
4311 //         * framework. This section of code is removed during the build process
4312 //         * before packaging production code. The [begin|end]TestSection are used
4313 //         * by the build to identify the section to strip.
4314 //         * @ignore
4315 //         */
4316 //        this.beginTestSection = 0;
4317 //
4318 //        /**
4319 //         * @ignore
4320 //         */
4321 //        this.getTestObject = function () {
4322 //            //Load mock dependencies.
4323 //            var _mock = new MockControl();
4324 //            _util = _mock.createMock(finesse.utilities.Utilities);
4325 //
4326 //            return {
4327 //                //Expose mock dependencies
4328 //                mock: _mock,
4329 //                util: _util,
4330 //
4331 //                //Expose internal private functions
4332 //                types: _TYPES,
4333 //                createConnInfoObj: _createConnInfoObj,
4334 //                sendMessage: _sendMessage,
4335 //                messageHandler: _messageHandler,
4336 //                createTunnel: _createTunnel,
4337 //                handlers: _handlers,
4338 //                initTunnelConfig : _initTunnelConfig
4339 //            };
4340 //        };
4341 //
4342 //        /**
4343 //         * @ignore
4344 //         */
4345 //        this.endTestSection = 0;
4346 //        //END TEST CODE//
4347     };
4348     
4349     /** @namespace JavaScript class objects and methods to handle the subscription to Finesse events.*/
4350     finesse.clientservices = finesse.clientservices || {};
4351 
4352     window.finesse = window.finesse || {};
4353     window.finesse.clientservices = window.finesse.clientservices || {};
4354     window.finesse.clientservices.MasterTunnel = MasterTunnel;
4355 
4356     return MasterTunnel;
4357 
4358 });
4359 
4360 /**
4361  * Contains a list of topics used for client side pubsub.
4362  *
4363  */
4364 
4365 /** @private */
4366 define('clientservices/Topics',[], function () {
4367     
4368    var Topics = (function () {
4369 
4370         /**
4371          * @private
4372          * The namespace prepended to all Finesse topics.
4373          */
4374         this.namespace = "finesse.info";
4375     
4376         /**
4377          * @private
4378          * Gets the full topic name with the Finesse namespace prepended.
4379          * @param {String} topic
4380          *     The topic category.
4381          * @returns {String}
4382          *     The full topic name with prepended namespace.
4383          */
4384         var _getNSTopic = function (topic) {
4385             return this.namespace + "." + topic;
4386         };
4387         
4388         /** @scope finesse.clientservices.Topics */
4389         return {
4390             /** 
4391              * @private
4392              * Client side request channel. 
4393              */
4394             REQUESTS: _getNSTopic("requests"),
4395     
4396             /** 
4397              * @private
4398              * Client side response channel. 
4399              */
4400             RESPONSES: _getNSTopic("responses"),
4401 
4402             /** 
4403              * @private
4404              * Connection status. 
4405              */
4406             EVENTS_CONNECTION_INFO: _getNSTopic("connection"),
4407             
4408             /** 
4409              * @private
4410              * Presence channel 
4411              */
4412             PRESENCE: _getNSTopic("presence"),
4413             
4414             /**
4415              * Topic for listening to token refresh events.
4416              * The provided callback will be invoked when the access token is refreshed.
4417              * This event is only meant for updating the access token in gadget Config object
4418              */
4419             ACCESS_TOKEN_REFRESHED_EVENT: _getNSTopic("accessTokenRefresh"),
4420     
4421             /**
4422              * @private
4423              * Convert a Finesse REST URI to a OpenAjax compatible topic name.
4424              */
4425             getTopic: function (restUri) {
4426                 //The topic should not start with '/' else it will get replaced with
4427                 //'.' which is invalid.
4428                 //Thus, remove '/' if it is at the beginning of the string
4429                 if (restUri.indexOf('/') === 0) {
4430                     restUri = restUri.substr(1);
4431                 }
4432     
4433                 //Replace every instance of "/" with ".". This is done to follow the
4434                 //OpenAjaxHub topic name convention.
4435                 return restUri.replace(/\//g, ".");
4436             }
4437         };
4438     }());
4439     window.finesse = window.finesse || {};
4440     window.finesse.clientservices = window.finesse.clientservices || {};
4441     /** @private */
4442     window.finesse.clientservices.Topics = Topics;
4443     
4444     return Topics;
4445 });
4446 /** The following comment is to prevent jslint errors about 
4447  * using variables before they are defined.
4448  */
4449 /*global finesse*/
4450 
4451 /**
4452  * Registers with the MasterTunnel to receive events, which it
4453  *     could publish to the OpenAjax gadget pubsub infrastructure.
4454  *
4455  * @requires OpenAjax, finesse.clientservices.MasterTunnel, finesse.clientservices.Topics
4456  */
4457 
4458 /** @private */
4459 define('clientservices/MasterPublisher',[
4460     "clientservices/MasterTunnel",
4461     "clientservices/Topics",
4462     "utilities/Utilities"
4463 ],
4464 function (MasterTunnel, Topics, Utilities) {
4465     
4466      var MasterPublisher = function (tunnel, hub) {
4467         if (!(tunnel instanceof MasterTunnel)) {
4468             throw new Error("Required tunnel object missing or invalid.");
4469         }
4470 
4471         var
4472         
4473         ClientServices = finesse.clientservices.ClientServices,
4474 
4475         /**
4476          * Reference to the gadget pubsub Hub instance.
4477          * @private
4478          */
4479         _hub = hub,
4480 
4481         /**
4482          * Reference to the Topics class.
4483          * @private
4484          */
4485         _topics = Topics,
4486         
4487         /**
4488          * Reference to conversion utilities class.
4489          * @private
4490          */
4491         _utils = Utilities,
4492         
4493         /**
4494          * References to ClientServices logger methods
4495          * @private
4496          */
4497         _logger = {
4498             log: ClientServices.log
4499         },
4500         
4501         /**
4502          * Store the passed in tunnel.
4503          * @private
4504          */
4505         _tunnel = tunnel,
4506 
4507         /**
4508          * Caches the connection info event so that it could be published if there
4509          * is a request for it.
4510          * @private
4511          */
4512         _connInfoCache = {},
4513 
4514         /**
4515          * The types of possible request types supported when listening to the
4516          * requests channel. Each request type could result in different operations.
4517          * @private
4518          */
4519         _REQTYPES = {
4520             CONNECTIONINFO: "ConnectionInfoReq",
4521             SUBSCRIBE: "SubscribeNodeReq",
4522             UNSUBSCRIBE: "UnsubscribeNodeReq",
4523             SUBSCRIPTIONSINFO: "SubscriptionsInfoReq",
4524             CONNECT: "ConnectionReq"
4525         },
4526 
4527         /**
4528          * Will store list of nodes that have OF subscriptions created
4529          *     _nodesList[node][subscribing].reqIds[subid]
4530          *     _nodesList[node][active].reqIds[subid]
4531          *     _nodesList[node][unsubscribing].reqIds[subid]
4532          *     _nodesList[node][holding].reqIds[subid]
4533          * @private
4534          */
4535         _nodesList = {},
4536         
4537         /**
4538          * The states that a subscription can be in
4539          * @private
4540          */
4541         _CHANNELSTATES = {
4542             UNINITIALIZED: "Uninitialized",
4543             PENDING: "Pending",
4544             OPERATIONAL: "Operational"
4545         },
4546 
4547         /**
4548           * Checks if the payload is JSON 
4549           * @returns {Boolean}
4550           * @private
4551           */
4552         _isJsonPayload = function(event) {
4553             var delimStart, delimEnd, retval = false;
4554             
4555             try { 
4556               delimStart = event.indexOf('{');
4557               delimEnd = event.lastIndexOf('}');
4558 
4559               if ((delimStart !== -1 ) && (delimEnd === (event.length - 1))) {
4560                 retval = true;  //event contains JSON payload
4561               }
4562             } catch (err) {
4563               _logger.log("MasterPublisher._isJsonPayload() - Caught error: " + err);
4564             }
4565             return retval;
4566         },
4567         
4568                 /**
4569           * Parses a JSON event and then publishes.
4570           *
4571           * @param {String} event
4572           *     The full event payload.
4573           * @throws {Error} If the payload object is malformed.
4574           * @private
4575           */
4576         _parseAndPublishJSONEvent = function(event) {
4577             var topic, eventObj, publishEvent,
4578             delimPos = event.indexOf("{"),
4579             node, parser,
4580             eventJson = event,
4581             returnObj = {node: null, data: null};
4582 
4583             try {
4584                //Extract and strip the node path from the message
4585                if (delimPos > 0) 
4586                {
4587                   //We need to decode the URI encoded node path
4588                   //TODO: make sure this is kosher with OpenAjax topic naming
4589                   node = decodeURI(event.substr(0, delimPos));
4590                   eventJson = event.substr(delimPos);
4591                   
4592                   //Converting the node path to openAjaxhub topic
4593                   topic = _topics.getTopic(node);
4594                   
4595                   returnObj.node = node;
4596                   returnObj.payload = eventJson;
4597                } 
4598                else 
4599                {
4600                   _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] node is not given in postMessage: " + eventJson);
4601                   throw new Error("node is not given in postMessage: " + eventJson);
4602                }
4603 
4604                parser = _utils.getJSONParser();
4605 
4606                eventObj = parser.parse(eventJson);
4607                returnObj.data = eventObj;
4608 
4609             } catch (err) {
4610                _logger.log("MasterPublisher._parseAndPublishJSONEvent() - [ERROR] Malformed event payload: " + err);
4611                throw new Error("Malformed event payload : " + err);
4612             }
4613             
4614             _logger.log("MasterPublisher._parseAndPublishJSONEvent() - Received JSON event on node '" + node + "': " + eventJson); 
4615             
4616             publishEvent = {content : event, object : eventObj };
4617 
4618             //Publish event to proper event topic.
4619             if (topic && eventObj) {
4620                _hub.publish(topic, publishEvent);
4621             }
4622         },
4623         
4624         /**
4625           * Parses an XML event and then publishes.
4626           *
4627           * @param {String} event
4628           *     The full event payload.
4629           * @throws {Error} If the payload object is malformed.
4630           * @private
4631           */
4632         _parseAndPublishXMLEvent = function(event) {
4633             var topic, eventObj, publishEvent, restTopic,
4634             delimPos = event.indexOf("<"),
4635             node,
4636             eventXml = event;
4637             
4638             try {
4639                //Extract and strip the node path from the message
4640                if (delimPos > 0) {
4641                   //We need to decode the URI encoded node path
4642                   //TODO: make sure this is kosher with OpenAjax topic naming
4643                   node = decodeURI(event.substr(0, delimPos));
4644                   eventXml = event.substr(delimPos);
4645                   //Converting the node path to openAjaxhub topic
4646                   topic = _topics.getTopic(node);
4647                } else {
4648                   _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] node is not given in postMessage: " + eventXml);
4649                   throw new Error("node is not given in postMessage: " + eventXml);
4650                }
4651 
4652                eventObj = _utils.xml2JsObj(eventXml);
4653                   
4654            } catch (err) {
4655                _logger.log("MasterPublisher._parseAndPublishXMLEvent() - [ERROR] Malformed event payload: " + err);
4656                throw new Error("Malformed event payload : " + err);
4657            }
4658            
4659            _logger.log("MasterPublisher._parseAndPublishXMLEvent() - Received XML event on node '" + node + "': " + eventXml);
4660            
4661            publishEvent = {content : event, object : eventObj };
4662 
4663            //Publish event to proper event topic.
4664            if (topic && eventObj) {
4665                _hub.publish(topic, publishEvent);
4666            }
4667         },
4668         
4669         /**
4670          * Publishes events to the appropriate topic. The topic name is determined
4671          * by fetching the source value from the event.
4672          * @param {String} event
4673          *     The full event payload.
4674          * @throws {Error} If the payload object is malformed.
4675          * @private
4676          */
4677         _eventHandler = function (event) {
4678             
4679             //Handle JSON or XML events
4680             if (!_isJsonPayload(event))
4681             {
4682                //XML
4683                _parseAndPublishXMLEvent(event);
4684             }
4685             else
4686             {
4687                //JSON
4688                _parseAndPublishJSONEvent(event);
4689             }
4690         },
4691         
4692         
4693         /**
4694          * Handler for when presence events are sent through the MasterTunnel.
4695          * @returns {Object}
4696          *     A presence xml event.
4697          * @private
4698          */
4699         _presenceHandler = function (event) {
4700             var eventObj = _utils.xml2JsObj(event), publishEvent;
4701             
4702             publishEvent = {content : event, object : eventObj};
4703             
4704             if (eventObj) {
4705                 _hub.publish(_topics.PRESENCE, publishEvent);
4706             }
4707         },
4708 
4709         /**
4710          * Clone the connection info object from cache.
4711          * @returns {Object}
4712          *     A connection info object containing a "status" and "resourceID".
4713          * @private
4714          */
4715         _cloneConnInfoObj = function () {
4716             if (_connInfoCache) {
4717                 return {
4718                     status: _connInfoCache.status,
4719                     resourceID: _connInfoCache.resourceID
4720                 };
4721             } else {
4722                 return null;
4723             }
4724         },
4725 
4726         /**
4727          * Cleans up any outstanding subscribe/unsubscribe requests and notifies them of errors.
4728          * This is done if we get disconnected because we cleanup explicit subscriptions on disconnect.
4729          * @private
4730          */
4731         _cleanupPendingRequests = function () {
4732             var node, curSubid, errObj = {
4733                 error: {
4734                     errorType: "Disconnected",
4735                     errorMessage: "Outstanding request will never complete."
4736                 }
4737             };
4738 
4739             // Iterate through all outstanding subscribe requests to notify them that it will never return
4740             for (node in _nodesList) {
4741                 if (_nodesList.hasOwnProperty(node)) {
4742                     for (curSubid in _nodesList[node].subscribing.reqIds) {
4743                         if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
4744                             // Notify this outstanding subscribe request to give up and error out
4745                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4746                         }
4747                     }
4748                     for (curSubid in _nodesList[node].unsubscribing.reqIds) {
4749                         if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
4750                             // Notify this outstanding unsubscribe request to give up and error out
4751                             _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4752                         }
4753                     }
4754                 }
4755             }
4756         },
4757 
4758         /**
4759          * Publishes the connection info to the connection info topic.
4760          * @param {Object} connInfo
4761          *     The connection info object containing the status and resource ID.
4762          * @private
4763          */
4764         _connInfoHandler = function (connInfo) {
4765             var before = _connInfoCache.status;
4766             _connInfoCache = connInfo;
4767             _logger.log("MasterPublisher._connInfoHandler() - Connection status: " + connInfo.status);
4768             _hub.publish(_topics.EVENTS_CONNECTION_INFO, _cloneConnInfoObj());
4769             if (before === "connected" && connInfo.status !== "connected") {
4770                 // Fail all pending requests when we transition to disconnected
4771                 _cleanupPendingRequests();
4772             }
4773         },
4774 
4775         /**
4776          * Get's all the subscriptions in the Openfire through EventTunnel and publishes the info to the topic
4777          * @param {String} invokeId
4778          *      The request id
4779          * @private
4780          */
4781         _getAllSubscriptions = function(invokeId) {
4782             // _logger.log("MasterPublisher._getAllSubscriptions() - Getting all subscriptions ");
4783 
4784             _tunnel.getSubscriptions (function (content) {
4785                 _hub.publish(_topics.RESPONSES + "." + invokeId, content);
4786             });
4787             
4788         },
4789 
4790         
4791         /**
4792          * Utility method to bookkeep node subscription requests and determine
4793          * whehter it is necessary to tunnel the request to JabberWerx.
4794          * @param {String} node
4795          *     The node of interest
4796          * @param {String} reqId
4797          *     A unique string identifying the request/subscription
4798          * @private
4799          */
4800         _subscribeNode = function (node, subid) {
4801             if (_connInfoCache.status !== "connected") {
4802                 _hub.publish(_topics.RESPONSES + "." + subid, {
4803                     error: {
4804                         errorType: "Not connected",
4805                         errorMessage: "Cannot subscribe without connection."
4806                     }
4807                 });
4808                 return;
4809             }
4810             // NODE DOES NOT YET EXIST
4811             if (!_nodesList[node]) {
4812                 _nodesList[node] = {
4813                     "subscribing": {
4814                         "reqIds": {},
4815                         "length": 0
4816                     },
4817                     "active": {
4818                         "reqIds": {},
4819                         "length": 0
4820                     },
4821                     "unsubscribing": {
4822                         "reqIds": {},
4823                         "length": 0
4824                     },
4825                     "holding": {
4826                         "reqIds": {},
4827                         "length": 0
4828                     }
4829                 };
4830             }
4831             if (_nodesList[node].active.length === 0) {
4832                 if (_nodesList[node].unsubscribing.length === 0) {
4833                     if (_nodesList[node].subscribing.length === 0) {
4834                         _nodesList[node].subscribing.reqIds[subid] = true;
4835                         _nodesList[node].subscribing.length += 1;
4836 
4837                         _logger.log("MasterPublisher._subscribeNode() - Attempting to subscribe to node '" + node + "'");
4838                         _tunnel.subscribe(node, function (err) {
4839                             var errObj, curSubid;
4840                             if (err) {
4841                                 errObj = {
4842                                     subscribe: {
4843                                         content: err
4844                                     }
4845                                 };
4846 
4847                                 try {
4848                                     errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
4849                                 } catch (e) {
4850                                     errObj.error = {
4851                                         errorType: "parseError",
4852                                         errorMessage: "Could not serialize XML: " + e
4853                                     };
4854                                 }
4855                                 _logger.log("MasterPublisher._subscribeNode() - Error subscribing to node '" + node + "': " + err);
4856                             } else {
4857                                 _logger.log("MasterPublisher._subscribeNode() - Subscribed to node '" + node + "'");
4858                             }
4859 
4860                             for (curSubid in _nodesList[node].subscribing.reqIds) {
4861                                 if (_nodesList[node].subscribing.reqIds.hasOwnProperty(curSubid)) {
4862                                     _hub.publish(_topics.RESPONSES + "." + curSubid, errObj);
4863                                     if (!err) {
4864                                         _nodesList[node].active.reqIds[curSubid] = true;
4865                                         _nodesList[node].active.length += 1;
4866                                     }
4867                                     delete _nodesList[node].subscribing.reqIds[curSubid];
4868                                     _nodesList[node].subscribing.length -= 1;
4869                                 }
4870                             }
4871                         });
4872                         
4873                     } else { //other ids are subscribing
4874                         _nodesList[node].subscribing.reqIds[subid] = true;
4875                         _nodesList[node].subscribing.length += 1;
4876                     }                       
4877                 } else { //An unsubscribe request is pending, hold onto these subscribes until it is done
4878                     _nodesList[node].holding.reqIds[subid] = true;
4879                     _nodesList[node].holding.length += 1;
4880                 }
4881             } else { // The node has active subscriptions; add this subid and return successful response
4882                 _nodesList[node].active.reqIds[subid] = true;
4883                 _nodesList[node].active.length += 1;
4884                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4885             }
4886         },
4887 
4888         /**
4889          * Utility method to bookkeep node unsubscribe requests and determine
4890          * whehter it is necessary to tunnel the request to JabberWerx.
4891          * @param {String} node
4892          *     The node to unsubscribe from
4893          * @param {String} reqId
4894          *     A unique string identifying the subscription to remove
4895          * @param {Boolean} isForceOp
4896          *     Boolean flag if true then the subscription will be forefully removed even when active references to the node are nil.
4897          * @private
4898          */
4899         _unsubscribeNode = function (node, subid, isForceOp) {
4900             if (!_nodesList[node] && !isForceOp ) { //node DNE, publish success response
4901                 _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4902             } else {
4903                 if (_connInfoCache.status !== "connected") {
4904                     _hub.publish(_topics.RESPONSES + "." + subid, {
4905                         error: {
4906                             errorType: "Not connected",
4907                             errorMessage: "Cannot unsubscribe without connection."
4908                         }
4909                     });
4910                     return;
4911                 }
4912                 if (_nodesList[node] && _nodesList[node].active.length > 1) {
4913                     delete _nodesList[node].active.reqIds[subid];
4914                     _hub.publish(_topics.RESPONSES + "." + subid, undefined); 
4915                     _nodesList[node].active.length -= 1;
4916                 } else if (_nodesList[node] && _nodesList[node].active.length === 1 || isForceOp) { // transition subid from active category to unsubscribing category
4917 
4918                     if (_nodesList[node]) {
4919                         _nodesList[node].unsubscribing.reqIds[subid] = true;
4920                         _nodesList[node].unsubscribing.length += 1;
4921                         delete _nodesList[node].active.reqIds[subid];
4922                         _nodesList[node].active.length -= 1;
4923                     }
4924 
4925                     _logger.log("MasterPublisher._unsubscribeNode() - Attempting to unsubscribe from node '" + node + "'");
4926                     var requestId = subid;
4927                     _tunnel.unsubscribe(node, function (err) {
4928                         var errObj, curSubid;
4929                         if (err) {
4930                             errObj = {
4931                                 subscribe: {
4932                                     content: err
4933                                 }
4934                             };
4935 
4936                             try {
4937                                 errObj.subscribe.object = gadgets.json.parse((_utils.xml2json(jQuery.parseXML(err), "")));
4938                             } catch (e) {
4939                                 errObj.error = {
4940                                     errorType: "parseError",
4941                                     errorMessage: "Could not serialize XML: " + e
4942                                 };
4943                             }
4944                             _logger.log("MasterPublisher._unsubscribeNode() - Error unsubscribing from node '" + node + "': " + err);
4945                         } else {
4946                             _logger.log("MasterPublisher._unsubscribeNode() - Unsubscribed from node '" + node + "'");
4947                         }
4948 
4949                         if (_nodesList[node]) {
4950 
4951                             for (curSubid in _nodesList[node].unsubscribing.reqIds) {
4952                                 if (_nodesList[node].unsubscribing.reqIds.hasOwnProperty(curSubid)) {
4953                                     // publish to all subids whether unsubscribe failed or succeeded
4954                                     _hub.publish(_topics.RESPONSES + "." + curSubid, errObj); 
4955                                     if (!err) {
4956                                         delete _nodesList[node].unsubscribing.reqIds[curSubid];
4957                                         _nodesList[node].unsubscribing.length -= 1;
4958                                     } else { // Just remove the subid from unsubscribing; the next subscribe request will operate with node already created
4959                                         delete _nodesList[node].unsubscribing.reqIds[curSubid];
4960                                         _nodesList[node].unsubscribing.length -= 1;
4961                                     }   
4962                                 }
4963                             }
4964                             
4965                             if (!err && _nodesList[node].holding.length > 0) { // if any subscribe requests came in while unsubscribing from OF, now transition from holding to subscribing
4966                                 for (curSubid in _nodesList[node].holding.reqIds) {
4967                                     if (_nodesList[node].holding.reqIds.hasOwnProperty(curSubid)) {
4968                                         delete _nodesList[node].holding.reqIds[curSubid];
4969                                         _nodesList[node].holding.length -= 1;
4970                                         _subscribeNode(node, curSubid);                             
4971                                     }
4972                                 }
4973                             }
4974                         } else {
4975                             _hub.publish(_topics.RESPONSES + "." + requestId, undefined);
4976                         }
4977                     });
4978                 } else { // length <= 0?
4979                     _hub.publish(_topics.RESPONSES + "." + subid, undefined);
4980                 }
4981             }
4982         },
4983 
4984         
4985         
4986         /**
4987          * Handles client requests to establish a BOSH connection.
4988          * @param {String} id
4989          *     id of the xmpp user
4990          * @param {String} password
4991          *     password of the xmpp user
4992          * @param {String} xmppDomain
4993          *     xmppDomain of the xmpp user account
4994          * @private
4995          */
4996         _connect = function (id, password, xmppDomain) {
4997             _tunnel.makeConnectReq(id, password, xmppDomain);
4998         },
4999 
5000         /**
5001          * Handles client requests made to the request topic. The type of the
5002          * request is described in the "type" property within the data payload. Each
5003          * type can result in a different operation.
5004          * @param {String} topic
5005          *     The topic which data was published to.
5006          * @param {Object} data
5007          *     The data containing requests information published by clients.
5008          * @param {String} data.type
5009          *     The type of the request. Supported: "ConnectionInfoReq"
5010          * @param {Object} data.data
5011          *     May contain data relevant for the particular requests.
5012          * @param {String} [data.invokeID]
5013          *     The ID used to identify the request with the response. The invoke ID
5014          *     will be included in the data in the publish to the topic. It is the
5015          *     responsibility of the client to correlate the published data to the
5016          *     request made by using the invoke ID.
5017          * @private
5018          */
5019         _clientRequestHandler = function (topic, data) {
5020             var dataCopy;
5021 
5022             //Ensure a valid data object with "type" and "data" properties.
5023             if (typeof data === "object" &&
5024                     typeof data.type === "string" &&
5025                     typeof data.data === "object") {
5026                 switch (data.type) {
5027                 case _REQTYPES.CONNECTIONINFO:
5028                     //It is possible that Slave clients come up before the Master
5029                     //client. If that is the case, the Slaves will need to make a
5030                     //request for the Master to send the latest connection info to the
5031                     //connectionInfo topic.
5032                     dataCopy = _cloneConnInfoObj();
5033                     if (dataCopy) {
5034                         if (data.invokeID !== undefined) {
5035                             dataCopy.invokeID = data.invokeID;
5036                         }
5037                         _hub.publish(_topics.EVENTS_CONNECTION_INFO, dataCopy);
5038                     }
5039                     break;
5040                 case _REQTYPES.SUBSCRIBE:
5041                     if (typeof data.data.node === "string") {
5042                         _subscribeNode(data.data.node, data.invokeID);
5043                     }
5044                     break;
5045                 case _REQTYPES.UNSUBSCRIBE:
5046                     if (typeof data.data.node === "string") {
5047                         _unsubscribeNode(data.data.node, data.invokeID, data.isForceOp );
5048                     }
5049                     break;
5050                 case _REQTYPES.SUBSCRIPTIONSINFO:
5051                     _getAllSubscriptions(data.invokeID);
5052                     break;
5053                 case _REQTYPES.CONNECT:
5054                     // Deprecated: Disallow others (non-masters) from administering BOSH connections that are not theirs
5055                     _logger.log("MasterPublisher._clientRequestHandler(): F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
5056                     break;
5057                 default:
5058                     break;
5059                 }
5060             }
5061         };
5062 
5063         (function () {
5064             //Register to receive events and connection status from tunnel.
5065             _tunnel.registerEventHandler(_eventHandler);
5066             _tunnel.registerPresenceHandler(_presenceHandler);
5067             _tunnel.registerConnectionInfoHandler(_connInfoHandler);
5068 
5069             //Listen to a request channel to respond to any requests made by other
5070             //clients because the Master may have access to useful information.
5071             _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
5072         }());
5073 
5074         /**
5075          * @private
5076          * Handles client requests to establish a BOSH connection.
5077          * @param {String} id
5078          *     id of the xmpp user
5079          * @param {String} password
5080          *     password of the xmpp user
5081          * @param {String} xmppDomain
5082          *     xmppDomain of the xmpp user account
5083          */
5084         this.connect = function (id, password, xmppDomain) {
5085           _connect(id, password, xmppDomain);
5086         };
5087 
5088         /**
5089          * @private
5090          * Resets the list of explicit subscriptions
5091          */
5092         this.wipeout = function () {
5093             _cleanupPendingRequests();
5094             _nodesList = {};
5095        };
5096 
5097         //BEGIN TEST CODE//
5098        /**
5099         * Test code added to expose private functions that are used by unit test
5100         * framework. This section of code is removed during the build process
5101         * before packaging production code. The [begin|end]TestSection are used
5102         * by the build to identify the section to strip.
5103         * @ignore
5104         */
5105        this.beginTestSection = 0;
5106 
5107        /**
5108         * @ignore
5109         */
5110        this.getTestObject = function () {
5111            //Load mock dependencies.
5112            var _mock = new MockControl();
5113            _hub = _mock.createMock(gadgets.Hub);
5114            _tunnel = _mock.createMock();
5115 
5116            return {
5117                //Expose mock dependencies
5118                mock: _mock,
5119                hub: _hub,
5120                tunnel: _tunnel,
5121                setTunnel: function (tunnel) {
5122                    _tunnel = tunnel;
5123                },
5124                getTunnel: function () {
5125                    return _tunnel;
5126                },
5127 
5128                //Expose internal private functions
5129                reqtypes: _REQTYPES,
5130                eventHandler: _eventHandler,
5131                presenceHandler: _presenceHandler,
5132                
5133                subscribeNode: _subscribeNode,
5134                unsubscribeNode: _unsubscribeNode,
5135                
5136                getNodeList: function () {
5137                    return _nodesList;
5138                },
5139                setNodeList: function (nodelist) {
5140                    _nodesList = nodelist;
5141                },
5142                
5143                cloneConnInfoObj: _cloneConnInfoObj,
5144                connInfoHandler: _connInfoHandler,
5145                clientRequestHandler: _clientRequestHandler
5146 
5147            };
5148        };
5149 
5150 
5151        /**
5152         * @ignore
5153         */
5154        this.endTestSection = 0;
5155        //END TEST CODE//
5156 
5157     };
5158     
5159     window.finesse = window.finesse || {};
5160     window.finesse.clientservices = window.finesse.clientservices || {};
5161     window.finesse.clientservices.MasterPublisher = MasterPublisher;
5162 
5163     return MasterPublisher;
5164 });
5165 
5166 /** The following comment is to prevent jslint errors about
5167  * using variables before they are defined.
5168  */
5169 /*global publisher:true */
5170 
5171 /**
5172  * Exposes a set of API wrappers that will hide the dirty work of
5173  *     constructing Finesse API requests and consuming Finesse events.
5174  *
5175  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
5176  */
5177 
5178 
5179 /**
5180  * Allow clients to make Finesse API requests and consume Finesse events by
5181  * calling a set of exposed functions. The Services layer will do the dirty
5182  * work of establishing a shared BOSH connection (for designated Master
5183  * modules), consuming events for client subscriptions, and constructing API
5184  * requests.
5185  */
5186 /** @private */
5187 define('clientservices/ClientServices',[
5188     "clientservices/MasterTunnel",
5189     "clientservices/MasterPublisher",
5190     "clientservices/Topics",
5191     "utilities/Utilities"
5192 ],
5193 function (MasterTunnel, MasterPublisher, Topics, Utilities) {
5194 
5195      var ClientServices = (function () {/** @lends finesse.clientservices.ClientServices.prototype */
5196         var
5197 
5198         /**
5199          * Shortcut reference to the master tunnel
5200          * @private
5201          */
5202         _tunnel,
5203 
5204         _publisher,
5205 
5206         /**
5207          * Shortcut reference to the finesse.utilities.Utilities singleton
5208          * This will be set by init()
5209          * @private
5210          */
5211         _util,
5212 
5213         /**
5214          * Shortcut reference to the gadgets.io object.
5215          * This will be set by init()
5216          * @private
5217          */
5218         _io,
5219 
5220         /**
5221          * Shortcut reference to the gadget pubsub Hub instance.
5222          * This will be set by init()
5223          * @private
5224          */
5225         _hub,
5226 
5227         /**
5228          * Logger object set externally by setLogger, defaults to nothing.
5229          * @private
5230          */
5231         _logger = {},
5232 
5233         /**
5234          * Shortcut reference to the Topics class.
5235          * This will be set by init()
5236          * @private
5237          */
5238         _topics,
5239 
5240         /**
5241          * Config object needed to initialize this library
5242          * This must be set by init()
5243          * @private
5244          */
5245         _config,
5246 
5247         /**
5248          * @private
5249          * Whether or not this ClientService instance is a Master.
5250          */
5251         _isMaster = false,
5252 
5253         /**
5254          * @private
5255          * Whether the Client Services have been initiated yet.
5256          */
5257         _inited = false,
5258 
5259         /**
5260          * Stores the list of subscription IDs for all subscriptions so that it
5261          * could be retrieve for unsubscriptions.
5262          * @private
5263          */
5264         _subscriptionID = {},
5265 
5266         /**
5267          * The possible states of the JabberWerx BOSH connection.
5268          * @private
5269          */
5270         _STATUS = {
5271             CONNECTING: "connecting",
5272             CONNECTED: "connected",
5273             DISCONNECTED: "disconnected",
5274             DISCONNECTED_CONFLICT: "conflict",
5275             DISCONNECTED_UNAUTHORIZED: "unauthorized",
5276             DISCONNECTING: "disconnecting",
5277             RECONNECTING: "reconnecting",
5278             UNLOADING: "unloading",
5279             FAILING: "failing",
5280             RECOVERED: "recovered"
5281         },
5282 
5283         /**
5284          * Local reference for authMode enum object.
5285          * @private
5286          */
5287         _authModes,
5288 
5289         _failoverMode = false,
5290 
5291         /**
5292          * Handler function to be invoked when BOSH connection is connecting.
5293          * @private
5294          */
5295         _onConnectingHandler,
5296 
5297         /**
5298          * Handler function to be invoked when BOSH connection is connected
5299          * @private
5300          */
5301         _onConnectHandler,
5302 
5303         /**
5304          * Handler function to be invoked when BOSH connection is disconnecting.
5305          * @private
5306          */
5307         _onDisconnectingHandler,
5308 
5309         /**
5310          * Handler function to be invoked when the BOSH is disconnected.
5311          * @private
5312          */
5313         _onDisconnectHandler,
5314 
5315         /**
5316          * Handler function to be invoked when the BOSH is reconnecting.
5317          * @private
5318          */
5319         _onReconnectingHandler,
5320 
5321         /**
5322          * Handler function to be invoked when the BOSH is unloading.
5323          * @private
5324          */
5325         _onUnloadingHandler,
5326 
5327         /**
5328          * Contains a cache of the latest connection info containing the current
5329          * state of the BOSH connection and the resource ID.
5330          * @private
5331          */
5332         _connInfo,
5333 
5334         /**
5335          * Keeps track of all the objects that need to be refreshed when we recover
5336          * due to our resilient connection. Only objects that we subscribe to will
5337          * be added to this list.
5338          * @private
5339          */
5340         _refreshList = [],
5341 
5342         /**
5343          * Needs to be passed as authorization header inside makeRequest wrapper function
5344          */
5345         _authHeaderString,
5346 
5347         /**
5348          * @private
5349          * Centralized logger.log method for external logger
5350          * @param {String} msg
5351          * @param {Object} [Optional] Javascript error object 
5352          */
5353         _log = function (msg, e) {
5354             // If the external logger throws up, it stops here.
5355             try {
5356                 if (_logger.log) {
5357                     _logger.log("[ClientServices] " + msg, e);
5358                 }
5359             } catch (e) { }
5360         },
5361 
5362         /**
5363          * Go through each object in the _refreshList and call its refresh() function
5364          * @private
5365          */
5366         _refreshObjects = function () {
5367             var i;
5368 
5369             // wipe out the explicit subscription list before we refresh objects
5370             if (_publisher) {
5371                 _publisher.wipeout();
5372             }
5373 
5374             // refresh each item in the refresh list
5375             for (i = _refreshList.length - 1; i >= 0; i -= 1) {
5376                 try{
5377                     _log("Refreshing " + _refreshList[i].getRestUrl());
5378                     _refreshList[i].refresh(10);
5379                 }
5380                 catch(e){
5381                     _log("ClientServices._refreshObjects() unexpected error while refreshing object" + e);
5382                 }
5383             }
5384         },
5385 
5386         /**
5387          * Handler to process connection info publishes.
5388          * @param {Object} data
5389          *     The connection info data object.
5390          * @param {String} data.status
5391          *     The BOSH connection status.
5392          * @param {String} data.resourceID
5393          *     The resource ID for the connection.
5394          * @private
5395          */
5396         _connInfoHandler =  function (data) {
5397 
5398             //Invoke registered handler depending on status received. Due to the
5399             //request topic where clients can make request for the Master to publish
5400             //the connection info, there is a chance that duplicate connection info
5401             //events may be sent, so ensure that there has been a state change
5402             //before invoking the handlers.
5403             if (_connInfo === undefined || _connInfo.status !== data.status) {
5404                 _connInfo = data;
5405                 switch (data.status) {
5406                 case _STATUS.CONNECTING:
5407                     if (_isMaster && _onConnectingHandler) {
5408                         _onConnectingHandler();
5409                     }
5410                     break;
5411                 case _STATUS.CONNECTED:
5412                     if ((_isMaster || !_failoverMode) && _onConnectHandler) {
5413                         _onConnectHandler();
5414                     }
5415                     break;
5416                 case _STATUS.DISCONNECTED:
5417                     if (_isMaster && _onDisconnectHandler) {
5418                         _onDisconnectHandler();
5419                     }
5420                     break;
5421                 case _STATUS.DISCONNECTED_CONFLICT:
5422                     if (_isMaster && _onDisconnectHandler) {
5423                         _onDisconnectHandler("conflict");
5424                     }
5425                     break;
5426                 case _STATUS.DISCONNECTED_UNAUTHORIZED:
5427                     if (_isMaster && _onDisconnectHandler) {
5428                         _onDisconnectHandler("unauthorized");
5429                     }
5430                     break;
5431                 case _STATUS.DISCONNECTING:
5432                     if (_isMaster && _onDisconnectingHandler) {
5433                         _onDisconnectingHandler();
5434                     }
5435                     break;
5436                 case _STATUS.RECONNECTING:
5437                     if (_isMaster && _onReconnectingHandler) {
5438                         _onReconnectingHandler();
5439                     }
5440                     break;
5441                 case _STATUS.UNLOADING:
5442                     if (_isMaster && _onUnloadingHandler) {
5443                         _onUnloadingHandler();
5444                     }
5445                     break;
5446                 case _STATUS.FAILING:
5447                     if (!_isMaster) {
5448                         // Stop
5449                         _failoverMode = true;
5450                         if (_onDisconnectHandler) {
5451                             _onDisconnectHandler();
5452                         }
5453                     }
5454                     break;
5455                 case _STATUS.RECOVERED:
5456                     if (!_isMaster) {
5457                         _failoverMode = false;
5458                         if (_onConnectHandler) {
5459                             _onConnectHandler();
5460                         }
5461                     }
5462                     // Whenever we are recovered, we need to refresh any objects
5463                     // that are stored.
5464                     _refreshObjects();
5465                     break;
5466                 }
5467             }
5468         },
5469 
5470         /**
5471          * Ensure that ClientServices have been inited.
5472          * @private
5473          */
5474         _isInited = function () {
5475             if (!_inited) {
5476                 throw new Error("ClientServices needs to be inited.");
5477             }
5478         },
5479 
5480         /**
5481          * Have the client become the Master by initiating a tunnel to a shared
5482          * event BOSH connection. The Master is responsible for publishing all
5483          * events to the pubsub infrastructure.
5484          *
5485          * TODO: Currently we only check the global auth mode. This code has to
5486          * handle mixed mode - in this case the user specfic SSO mode has to be
5487          * exposed via an API.
5488          *
5489          * @private
5490          */
5491         _becomeMaster = function () {
5492             var creds , id;
5493             _tunnel = new MasterTunnel(_config.host, _config.scheme);
5494             _publisher = new MasterPublisher(_tunnel, _hub);
5495             if(_authModes.SSO === _config.systemAuthMode ) {
5496                 creds = _config.authToken;
5497             } else {
5498                 creds = _config.password;
5499             }
5500             _util = Utilities;
5501              id = _util.encodeNodeName(_config.id);
5502             _tunnel.init(id, creds, _config.xmppDomain, _config.pubsubDomain, _config.resource, _config.notificationConnectionType);
5503             _isMaster = true;
5504         },
5505 
5506         /**
5507          * Make a request to the request channel to have the Master publish the
5508          * connection info object.
5509          * @private
5510          */
5511         _makeConnectionInfoReq = function () {
5512             var data = {
5513                 type: "ConnectionInfoReq",
5514                 data: {},
5515                 invokeID: (new Date()).getTime()
5516             };
5517             _hub.publish(_topics.REQUESTS, data);
5518         },
5519 
5520         /**
5521          * Utility method to register a handler which is associated with a
5522          * particular connection status.
5523          * @param {String} status
5524          *     The connection status string.
5525          * @param {Function} handler
5526          *     The handler to associate with a particular connection status.
5527          * @throws {Error}
5528          *     If the handler provided is not a function.
5529          * @private
5530          */
5531         _registerHandler = function (status, handler) {
5532             if (typeof handler === "function") {
5533                 if (_connInfo && _connInfo.status === status) {
5534                     handler();
5535                 }
5536                 switch (status) {
5537                 case _STATUS.CONNECTING:
5538                     _onConnectingHandler = handler;
5539                     break;
5540                 case _STATUS.CONNECTED:
5541                     _onConnectHandler = handler;
5542                     break;
5543                 case _STATUS.DISCONNECTED:
5544                     _onDisconnectHandler = handler;
5545                     break;
5546                 case _STATUS.DISCONNECTING:
5547                     _onDisconnectingHandler = handler;
5548                     break;
5549                 case _STATUS.RECONNECTING:
5550                     _onReconnectingHandler = handler;
5551                     break;
5552                 case _STATUS.UNLOADING:
5553                     _onUnloadingHandler = handler;
5554                     break;
5555                 }
5556 
5557             } else {
5558                 throw new Error("Callback is not a function");
5559             }
5560         },
5561 
5562         /**
5563          * Callback function that is called when a refresh access token event message is posted to the Hub.
5564          *
5565          * Get access token from the data and update the finesse.gadget.Config object!
5566          *
5567          * @param {String} topic
5568          *      which topic the event came on (unused)
5569          * @param {Object} data
5570          *      the data published with the event
5571          * @private
5572          */
5573         _accessTokenRefreshHandler = function(topic , data){
5574             _log("Access token refreshed - topic :" + topic + ", authToken :" + data.authToken);
5575 
5576             if(data.authToken){
5577                _config.authToken = data.authToken;
5578                if(finesse.gadget && finesse.gadget.Config){
5579                    finesse.gadget.Config.authToken = data.authToken;
5580                }
5581             }
5582          },
5583 
5584         /**
5585          * @private
5586          * Retrieves systemAuthMode from parent Finesse Container. If parent is not available, mode will be retrieved from the systemInfo rest object
5587          * @throws {Error}
5588          *     If unable to retrieve systemAuthMode
5589          */
5590         _getSystemAuthMode = function(){
5591           var parentFinesse , sysInfo;
5592           // For gadgets hosted outside of finesse container , finesse parent object will not be available
5593             try{
5594                 parentFinesse = window.parent.finesse;
5595             } catch (e){
5596                 parentFinesse = undefined;
5597             }
5598 
5599             if( parentFinesse ){
5600                _config.systemAuthMode =  parentFinesse.container.Config.systemAuthMode;
5601             } else {
5602                sysInfo = new finesse.restservices.SystemInfo({
5603                    id: _config.id,
5604                    onLoad: function (systemInfo) {
5605                         _config.systemAuthMode = systemInfo.getSystemAuthMode();
5606                    },
5607                    onError: function (errRsp) {
5608                         throw new Error("Unable to retrieve systemAuthMode from config. Initialization failed......");
5609                    }
5610               });
5611 
5612             }
5613        };
5614 
5615         return {
5616 
5617             /**
5618              * @private
5619              * Adds an item to the list to be refreshed upon reconnect
5620              * @param {RestBase} object - rest object to be refreshed
5621              */
5622             addToRefreshList: function (object) {
5623                 _refreshList.push(object);
5624             },
5625             
5626             /**
5627              * Get the current state of the Bosh/Websocket connection
5628              * returns undefined when information not available.
5629              */
5630             getNotificationConnectionState: function () {
5631                 return _connInfo && _connInfo.status;
5632             },
5633 
5634             /**
5635              * @private
5636              * Removes the given item from the refresh list
5637              * @param  {RestBase} object - rest object to be removed
5638              */
5639             removeFromRefreshList: function (object) {
5640                 var i;
5641                 for (i = _refreshList.length - 1; i >= 0; i -= 1) {
5642                     if (_refreshList[i] === object) {
5643                         _refreshList.splice(i, 1);
5644                         break;
5645                     }
5646                 }
5647             },
5648 
5649             /**
5650              * @private
5651              * The location of the tunnel HTML URL.
5652              * @returns {String}
5653              *     The location of the tunnel HTML URL.
5654              */
5655             getTunnelURL: function () {
5656                 return _tunnel.getTunnelURL();
5657             },
5658 
5659             /**
5660              * @private
5661              * Indicates whether the tunnel frame is loaded.
5662              * @returns {Boolean}
5663              *     True if the tunnel frame is loaded, false otherwise.
5664              */
5665             isTunnelLoaded: function () {
5666                 return _tunnel.isTunnelLoaded();
5667             },
5668 
5669             /**
5670              * @private
5671              * Indicates whether the ClientServices instance is a Master.
5672              * @returns {Boolean}
5673              *     True if this instance of ClientServices is a Master, false otherwise.
5674              */
5675             isMaster: function () {
5676                 return _isMaster;
5677             },
5678 
5679             /**
5680              * @private
5681              * Get the resource ID. An ID is only available if the BOSH connection has
5682              * been able to connect successfully.
5683              * @returns {String}
5684              *     The resource ID string. Null if the BOSH connection was never
5685              *     successfully created and/or the resource ID has not been associated.
5686              */
5687             getResourceID: function () {
5688                 if (_connInfo !== undefined) {
5689                     return _connInfo.resourceID;
5690                 }
5691                 return null;
5692             },
5693 
5694             /*
5695             getHub: function () {
5696                 return _hub;
5697             },
5698         */
5699             /**
5700              * @private
5701              * Add a callback to be invoked when the BOSH connection is attempting
5702              * to connect. If the connection is already trying to connect, the
5703              * callback will be invoked immediately.
5704              * @param {Function} handler
5705              *      An empty param function to be invoked on connecting. Only one
5706              *      handler can be registered at a time. Handlers already registered
5707              *      will be overwritten.
5708              */
5709             registerOnConnectingHandler: function (handler) {
5710                 _registerHandler(_STATUS.CONNECTING, handler);
5711             },
5712 
5713             /**
5714              * @private
5715              * Removes the on connecting callback that was registered.
5716              */
5717             unregisterOnConnectingHandler: function () {
5718                 _onConnectingHandler = undefined;
5719             },
5720 
5721             /**
5722              * Add a callback to be invoked when all of the following conditions are met:
5723              * <ul>
5724              *   <li>When Finesse goes IN_SERVICE</li>
5725              *   <li>The BOSH connection is established</li>
5726              *   <li>The Finesse user presence becomes available</li>
5727              * </ul>
5728              * If all these conditions are met at the time this function is called, then
5729              * the handler will be invoked immediately.
5730              * @param {Function} handler
5731              *      An empty param function to be invoked on connect. Only one handler
5732              *      can be registered at a time. Handlers already registered will be
5733              *      overwritten.
5734              * @example
5735              *      finesse.clientservices.ClientServices.registerOnConnectHandler(gadget.myCallback);
5736              */
5737             registerOnConnectHandler: function (handler) {
5738                 _registerHandler(_STATUS.CONNECTED, handler);
5739             },
5740 
5741             /**
5742              * @private
5743              * Removes the on connect callback that was registered.
5744              */
5745             unregisterOnConnectHandler: function () {
5746                 _onConnectHandler = undefined;
5747             },
5748 
5749             /**
5750              * Add a callback to be invoked when any of the following occurs:
5751              * <ul>
5752              *   <li>Finesse is no longer IN_SERVICE</li>
5753              *   <li>The BOSH connection is lost</li>
5754              *   <li>The presence of the Finesse user is no longer available</li>
5755              * </ul>
5756              * If any of these conditions are met at the time this function is
5757              * called, the callback will be invoked immediately.
5758              * @param {Function} handler
5759              *      An empty param function to be invoked on disconnected. Only one
5760              *      handler can be registered at a time. Handlers already registered
5761              *      will be overwritten.
5762              * @example
5763              *      finesse.clientservices.ClientServices.registerOnDisconnectHandler(gadget.myCallback);
5764              */
5765             registerOnDisconnectHandler: function (handler) {
5766                 _registerHandler(_STATUS.DISCONNECTED, handler);
5767             },
5768 
5769             /**
5770              * @private
5771              * Removes the on disconnect callback that was registered.
5772              */
5773             unregisterOnDisconnectHandler: function () {
5774                 _onDisconnectHandler = undefined;
5775             },
5776 
5777             /**
5778              * @private
5779              * Add a callback to be invoked when the BOSH is currently disconnecting. If
5780              * the connection is already disconnecting, invoke the callback immediately.
5781              * @param {Function} handler
5782              *      An empty param function to be invoked on disconnected. Only one
5783              *      handler can be registered at a time. Handlers already registered
5784              *      will be overwritten.
5785              */
5786             registerOnDisconnectingHandler: function (handler) {
5787                 _registerHandler(_STATUS.DISCONNECTING, handler);
5788             },
5789 
5790             /**
5791              * @private
5792              * Removes the on disconnecting callback that was registered.
5793              */
5794             unregisterOnDisconnectingHandler: function () {
5795                 _onDisconnectingHandler = undefined;
5796             },
5797 
5798             /**
5799              * @private
5800              * Add a callback to be invoked when the BOSH connection is attempting
5801              * to connect. If the connection is already trying to connect, the
5802              * callback will be invoked immediately.
5803              * @param {Function} handler
5804              *      An empty param function to be invoked on connecting. Only one
5805              *      handler can be registered at a time. Handlers already registered
5806              *      will be overwritten.
5807              */
5808             registerOnReconnectingHandler: function (handler) {
5809                 _registerHandler(_STATUS.RECONNECTING, handler);
5810             },
5811 
5812             /**
5813              * @private
5814              * Removes the on reconnecting callback that was registered.
5815              */
5816             unregisterOnReconnectingHandler: function () {
5817                 _onReconnectingHandler = undefined;
5818             },
5819 
5820             /**
5821              * @private
5822              * Add a callback to be invoked when the BOSH connection is unloading
5823              *
5824              * @param {Function} handler
5825              *      An empty param function to be invoked on connecting. Only one
5826              *      handler can be registered at a time. Handlers already registered
5827              *      will be overwritten.
5828              */
5829             registerOnUnloadingHandler: function (handler) {
5830                 _registerHandler(_STATUS.UNLOADING, handler);
5831             },
5832 
5833             /**
5834              * @private
5835              * Removes the on unloading callback that was registered.
5836              */
5837             unregisterOnUnloadingHandler: function () {
5838                 _onUnloadingHandler = undefined;
5839             },
5840 
5841             /**
5842              * @private
5843              * Proxy method for gadgets.io.makeRequest. The will be identical to gadgets.io.makeRequest
5844              * ClientServices will mixin the BASIC Auth string, locale, and host, since the
5845              * configuration is encapsulated in here anyways.
5846              * This removes the dependency
5847              * @param {String} url
5848              *     The relative url to make the request to (the host from the passed in config will be
5849              *     appended). It is expected that any encoding to the URL is already done.
5850              * @param {Function} handler
5851              *     Callback handler for makeRequest to invoke when the response returns.
5852              *     Completely passed through to gadgets.io.makeRequest
5853              * @param {Object} params
5854              *     The params object that gadgets.io.makeRequest expects. Authorization and locale
5855              *     headers are mixed in.
5856              */
5857             makeRequest: function (url, handler, params) {
5858                 var requestedScheme, scheme = "http";
5859 
5860                 // ClientServices needs to be initialized with a config for restHost, auth, and locale
5861                 _isInited();
5862 
5863                 // Allow mixin of auth and locale headers
5864                 params = params || {};
5865 
5866                 // Override refresh interval to 0 instead of default 3600 as a way to workaround makeRequest
5867                 // using GET http method because then the params are added to the url as query params, which
5868                 // exposes the authorization string in the url. This is a placeholder until oauth comes in
5869                 params[gadgets.io.RequestParameters.REFRESH_INTERVAL] = params[gadgets.io.RequestParameters.REFRESH_INTERVAL] || 0;
5870 
5871                 params[gadgets.io.RequestParameters.HEADERS] = params[gadgets.io.RequestParameters.HEADERS] || {};
5872 
5873                 // Add Basic auth to request header
5874                 params[gadgets.io.RequestParameters.HEADERS].Authorization = _util.getAuthHeaderString(_config);
5875 
5876                 //Locale
5877                 params[gadgets.io.RequestParameters.HEADERS].locale = _config.locale;
5878 
5879                 //Allow clients to override the scheme:
5880                 //  - If not specified  => we use HTTP
5881                 //  - If null specified => we use _config.scheme
5882                 //  - Otherwise         => we use whatever they provide
5883                 requestedScheme = params.SCHEME;
5884                 if (!(requestedScheme === undefined || requestedScheme === "undefined")) {
5885                     if (requestedScheme === null) {
5886                        scheme = _config.scheme;
5887                     } else {
5888                        scheme = requestedScheme;
5889                     }
5890                 }
5891                 scheme = _config.restScheme || scheme;
5892 
5893                 _log("RequestedScheme: " + requestedScheme + "; Scheme: " + scheme);
5894                 gadgets.io.makeRequest(encodeURI(scheme + "://" + _config.restHost + ":" + _config.localhostPort) + url, handler, params);
5895             },
5896 
5897             /**
5898              * @private
5899              * Utility function to make a subscription to a particular topic. Only one
5900              * callback function is registered to a particular topic at any time.
5901              * @param {String} topic
5902              *     The full topic name. The topic name should follow the OpenAjax
5903              *     convention using dot notation (ex: finesse.api.User.1000).
5904              * @param {Function} callback
5905              *     The function that should be invoked with the data when an event
5906              *     is delivered to the specific topic.
5907              * @param {Function} contextId
5908              *     A unique id which gets appended to the topic for storing subscription details,
5909              *     when multiple subscriptions to the same topic is required.
5910              * @returns {Boolean}
5911              *     True if the subscription was made successfully and the callback was
5912              *     been registered. False if the subscription already exist, the
5913              *     callback was not overwritten.
5914              */
5915             subscribe: function (topic, callback, disableDuringFailover, contextId) {
5916                 _isInited();
5917                 
5918                 var topicId = topic + (contextId === undefined ? "" : contextId);
5919 
5920                 //Ensure that the same subscription isn't made twice.
5921                 if (!_subscriptionID[topicId]) {
5922                     //Store the subscription ID using the topic name as the key.
5923                     _subscriptionID[topicId] = _hub.subscribe(topic,
5924                         //Invoke the callback just with the data object.
5925                         function (topic, data) {
5926                             if (!disableDuringFailover || _isMaster || !_failoverMode) {
5927                                 // Halt all intermediate event processing while the master instance attempts to rebuild the connection. This only occurs:
5928                                 // - For RestBase objects (which pass the disableDuringFailover flag), so that intermediary events while recovering are hidden away until everything is all good
5929                                 //    - We shouldn't be halting anything else because we have infrastructure requests that use the hub pub sub
5930                                 // - Master instance will get all events regardless, because it is responsible for managing failover
5931                                 // - If we are not in a failover mode, everything goes
5932                                 // _refreshObjects will reconcile anything that was missed once we are back in action
5933                                 callback(data);
5934                             }
5935                         });
5936                     return true;
5937                 }
5938                 return false;
5939             },
5940 
5941             /**
5942              * @private
5943              * Unsubscribe from a particular topic.
5944              * @param {String} topic
5945              *     The full topic name.
5946              * @param {String} contextId
5947              *     A unique id which gets appended to the topic for storing subscription details,
5948              *     when multiple subscriptions to the same topic is required.
5949              */
5950             unsubscribe: function (topic, contextId) {
5951                 _isInited();
5952                 topic = topic + (contextId === undefined ? "" : contextId);
5953                 
5954                 //Unsubscribe from the topic using the subscription ID recorded when
5955                 //the subscription was made, then delete the ID from data structure.
5956                 if (_subscriptionID[topic]) {
5957                     _hub.unsubscribe(_subscriptionID[topic]);
5958                     delete _subscriptionID[topic];
5959                 }
5960             },
5961 
5962 
5963             /**
5964              * @private
5965              * Make a request to the request channel to have the Master subscribe
5966              * to a node.
5967              * @param {String} node
5968              *     The node to subscribe to.
5969              */
5970             subscribeNode: function (node, handler) {
5971                 if (handler && typeof handler !== "function") {
5972                     throw new Error("ClientServices.subscribeNode: handler is not a function");
5973                 }
5974 
5975                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
5976                 var data = {
5977                     type: "SubscribeNodeReq",
5978                     data: {node: node},
5979                     invokeID: _util.generateUUID()
5980                 },
5981                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
5982                 _this = this;
5983 
5984                 // We need to first subscribe to the response channel
5985                 this.subscribe(responseTopic, function (rsp) {
5986                     // Since this channel is only used for this singular request,
5987                     // we are not interested anymore.
5988                     // This is also critical to not leaking memory by having OpenAjax
5989                     // store a bunch of orphaned callback handlers that enclose on
5990                     // our entire ClientServices singleton
5991                     _this.unsubscribe(responseTopic);
5992                     if (handler) {
5993                         handler(data.invokeID, rsp);
5994                     }
5995                 });
5996                 // Then publish the request on the request channel
5997                 _hub.publish(_topics.REQUESTS, data);
5998             },
5999 
6000             /**
6001              * @private
6002              * Make a request to the request channel to have the Master unsubscribe
6003              * from a node.
6004              * @param {String} node
6005              *     The node to unsubscribe from.
6006              */
6007             unsubscribeNode: function (node, subid, handler) {
6008                 if (handler && typeof handler !== "function") {
6009                     throw new Error("ClientServices.unsubscribeNode: handler is not a function");
6010                 }
6011 
6012                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
6013                 var data = {
6014                     type: "UnsubscribeNodeReq",
6015                     data: {
6016                         node: node,
6017                         subid: subid
6018                     },
6019                     isForceOp: (typeof handler.isForceOp != 'undefined') ? handler.isForceOp: false,
6020                     invokeID: _util.generateUUID()
6021                 },
6022                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
6023                 _this = this;
6024 
6025                 // We need to first subscribe to the response channel
6026                 this.subscribe(responseTopic, function (rsp) {
6027                     // Since this channel is only used for this singular request,
6028                     // we are not interested anymore.
6029                     // This is also critical to not leaking memory by having OpenAjax
6030                     // store a bunch of orphaned callback handlers that enclose on
6031                     // our entire ClientServices singleton
6032                     _this.unsubscribe(responseTopic);
6033                     if (handler) {
6034                         handler(rsp);
6035                     }
6036                 });
6037                 // Then publish the request on the request channel
6038                 _hub.publish(_topics.REQUESTS, data);
6039             },
6040 
6041 
6042             /**
6043              * @private
6044              * Make a request to the request channel to get the list of all subscriptions for the logged in user.
6045              */
6046             getNodeSubscriptions: function (handler) {
6047                 if (handler && typeof handler !== "function") {
6048                     throw new Error("ClientServices.getNodeSubscriptions: handler is not a function");
6049                 }
6050 
6051                 // Construct the request to send to MasterPublisher through the OpenAjax Hub
6052                 var data = {
6053                     type: "SubscriptionsInfoReq",
6054                     data: {
6055                     },
6056                     invokeID: _util.generateUUID()
6057                 },
6058                 responseTopic = _topics.RESPONSES + "." + data.invokeID,
6059                 _this = this;
6060 
6061                 // We need to first subscribe to the response channel
6062                 this.subscribe(responseTopic, function (rsp) {
6063                     // Since this channel is only used for this singular request,
6064                     // we are not interested anymore.
6065                     // This is also critical to not leaking memory by having OpenAjax
6066                     // store a bunch of orphaned callback handlers that enclose on
6067                     // our entire ClientServices singleton
6068                     _this.unsubscribe(responseTopic);
6069                     if (handler) {
6070                         handler(JSON.parse(rsp));
6071                     }
6072                 });
6073                 // Then publish the request on the request channel
6074                 _hub.publish(_topics.REQUESTS, data);                
6075             },
6076 
6077             /**
6078              * @private
6079              * Make a request to the request channel to have the Master connect to the XMPP server via BOSH
6080              */
6081             makeConnectionReq : function () {
6082                 // Disallow others (non-masters) from administering BOSH connections that are not theirs
6083                 if (_isMaster && _publisher) {
6084                     _publisher.connect(_config.id, _config.password, _config.xmppDomain);
6085                 } else {
6086                     _log("F403: Access denied. Non-master ClientService instances do not have the clearance level to make this request: makeConnectionReq");
6087                 }
6088             },
6089 
6090             /**
6091              * @private
6092              * Set's the global logger for this Client Services instance.
6093              * @param {Object} logger
6094              *     Logger object with the following attributes defined:<ul>
6095              *         <li><b>log:</b> function (msg) to simply log a message
6096              *     </ul>
6097              */
6098             setLogger: function (logger) {
6099                 // We want to check the logger coming in so we don't have to check every time it is called.
6100                 if (logger && typeof logger === "object" && typeof logger.log === "function") {
6101                     _logger = logger;
6102                 } else {
6103                     // We are resetting it to an empty object so that _logger.log in .log is falsy.
6104                     _logger = {};
6105                 }
6106             },
6107 
6108             /**
6109              * @private
6110              * Centralized logger.log method for external logger
6111              * @param {String} msg
6112              *     Message to log
6113              */
6114             log: _log,
6115 
6116             /**
6117              * @class
6118              * Allow clients to make Finesse API requests and consume Finesse events by
6119              * calling a set of exposed functions. The Services layer will do the dirty
6120              * work of establishing a shared BOSH connection (for designated Master
6121              * modules), consuming events for client subscriptions, and constructing API
6122              * requests.
6123              *
6124              * @constructs
6125              */
6126             _fakeConstuctor: function () {
6127                 /* This is here so we can document init() as a method rather than as a constructor. */
6128             },
6129 
6130             /**
6131              * Initiates the Client Services with the specified config parameters.
6132              * Enabling the Client Services as Master will trigger the establishment
6133              * of a BOSH event connection.
6134              * @param {finesse.gadget.Config} config
6135              *     Configuration object containing properties used for making REST requests:<ul>
6136              *         <li><b>host:</b> The Finesse server IP/host as reachable from the browser
6137              *         <li><b>restHost:</b> The Finesse API IP/host as reachable from the gadget container
6138              *         <li><b>id:</b> The ID of the user. This is an optional param as long as the
6139              *         appropriate authorization string is provided, otherwise it is
6140              *         required.</li>
6141              *         <li><b>password:</b> The password belonging to the user. This is an optional param as
6142              *         long as the appropriate authorization string is provided,
6143              *         otherwise it is required.</li>
6144              *         <li><b>authorization:</b> The base64 encoded "id:password" authentication string. This
6145              *         param is provided to allow the ability to hide the password
6146              *         param. If provided, the id and the password extracted from this
6147              *         string will be used over the config.id and config.password.</li>
6148              *     </ul>
6149              * @throws {Error} If required constructor parameter is missing.
6150              * @example
6151              *      finesse.clientservices.ClientServices.init(finesse.gadget.Config);
6152              */
6153             init: function (config) {
6154                 if (!_inited) {
6155                     //Validate the properties within the config object if one is provided.
6156                     if (!(typeof config === "object" &&
6157                          typeof config.host === "string" && config.host.length > 0 && config.restHost &&
6158                          (typeof config.authorization === "string" ||
6159                                  (typeof config.id === "string")))) {
6160                         throw new Error("Config object contains invalid properties.");
6161                     }
6162 
6163                     // Initialize configuration
6164                     _config = config;
6165 
6166                     // Set shortcuts
6167                     _util = Utilities;
6168                     _authModes = _util.getAuthModes();
6169                     _topics = Topics;
6170 
6171                     //TODO: document when this is properly supported
6172                     // Allows hub and io dependencies to be passed in. Currently only used for unit tests.
6173                     _hub = config.hub || gadgets.Hub;
6174                     _io = config.io || gadgets.io;
6175 
6176                     //If the authorization string is provided, then use that to
6177                     //extract the ID and the password. Otherwise use the ID and
6178                     //password from the respective ID and password params.
6179                     if (_config.authorization) {
6180                         var creds = _util.getCredentials(_config.authorization);
6181                         _config.id = creds.id;
6182                         _config.password = creds.password;
6183                     }
6184                     else {
6185                         _config.authorization = _util.b64Encode(
6186                                 _config.id + ":" + _config.password);
6187                     }
6188 
6189                     //In case if gadgets create their own config instance , add systemAuthMode property inside config object
6190                     if(!_config.systemAuthMode || _config.systemAuthMode === ""){
6191                         _getSystemAuthMode();
6192                     }
6193 
6194                     if(_config.systemAuthMode === _authModes.SSO){
6195                         _accessTokenRefreshHandler(undefined , {authToken : _util.getToken()});
6196                         if(!_config.authToken){
6197                             throw new Error("ClientServices.init() - Access token is unavailable inside Config object.");
6198                         }
6199 
6200                         if (_hub){
6201                               _hub.subscribe(_topics.ACCESS_TOKEN_REFRESHED_EVENT, _accessTokenRefreshHandler);
6202                         }
6203                     }
6204 
6205                     _inited = true;
6206 
6207                     if (_hub) {
6208                         //Subscribe to receive connection information. Since it is possible that
6209                         //the client comes up after the Master comes up, the client will need
6210                         //to make a request to have the Master send the latest connection info.
6211                         //It would be possible that all clients get connection info again.
6212                         this.subscribe(_topics.EVENTS_CONNECTION_INFO, _connInfoHandler);
6213                         _makeConnectionInfoReq();
6214                     }
6215                 }
6216 
6217                 //Return the CS object for object chaining.
6218                 return this;
6219             },
6220 
6221             /**
6222              * @private
6223              * Initializes the BOSH component of this ClientServices instance. This establishes
6224              * the BOSH connection and will trigger the registered handlers as the connection
6225              * status changes respectively:<ul>
6226              *     <li>registerOnConnectingHandler</li>
6227              *     <li>registerOnConnectHandler</li>
6228              *     <li>registerOnDisconnectHandler</li>
6229              *     <li>registerOnDisconnectingHandler</li>
6230              *     <li>registerOnReconnectingHandler</li>
6231              *     <li>registerOnUnloadingHandler</li>
6232              * <ul>
6233              *
6234              * @param {Object} config
6235              *     An object containing the following (optional) handlers for the request:<ul>
6236              *         <li><b>xmppDomain:</b> {String} The domain of the XMPP server. Available from the SystemInfo object.
6237              *         This is used to construct the JID: user@domain.com</li>
6238              *         <li><b>pubsubDomain:</b> {String} The pub sub domain where the pub sub service is running.
6239              *         Available from the SystemInfo object.
6240              *         This is used for creating or removing subscriptions.</li>
6241              *         <li><b>resource:</b> {String} The resource to connect to the notification server with.</li>
6242              *     </ul>
6243              */
6244             initBosh: function (config) {
6245                 //Validate the properties within the config object if one is provided.
6246                 if (!(typeof config === "object" && typeof config.xmppDomain === "string" && typeof config.pubsubDomain === "string")) {
6247                     throw new Error("Config object contains invalid properties.");
6248                 }
6249 
6250                 // Mixin the required information for establishing the BOSH connection
6251                 _config.xmppDomain = config.xmppDomain;
6252                 _config.pubsubDomain = config.pubsubDomain;
6253                 _config.resource = config.resource;
6254 
6255                 //Initiate Master launch sequence
6256                 _becomeMaster();
6257             },
6258 
6259             /**
6260              * @private
6261              * Sets the failover mode to either FAILING or RECOVERED. This will only occur in the master instance and is meant to be
6262              * used by a stereotypical failover monitor type module to notify non-master instances (i.e. in gadgets)
6263              * @param {Object} failoverMode
6264              *     true if failing, false or something falsy when recovered
6265              */
6266             setFailoverMode: function (failoverMode) {
6267                 if (_isMaster) {
6268                     _hub.publish(_topics.EVENTS_CONNECTION_INFO, {
6269                         status: (failoverMode ? _STATUS.FAILING : _STATUS.RECOVERED)
6270                     });
6271                 }
6272             },
6273 
6274             /**
6275              * returns the destination host where the rest calls will be made proxied through shindig.
6276              */
6277             getRestHost: function () {
6278               return (_config && _config.restHost) || 'localhost';
6279             },
6280 
6281             /**
6282              * @private
6283              * Private accessor used to inject mocked private dependencies for unit testing
6284              */
6285             _getTestObj: function () {
6286                 return {
6287                     setPublisher: function (publisher) {
6288                         _publisher = publisher;
6289                     }
6290                 };
6291             }
6292         };
6293     }());
6294 
6295     window.finesse = window.finesse || {};
6296     window.finesse.clientservices = window.finesse.clientservices || {};
6297     window.finesse.clientservices.ClientServices = ClientServices;
6298 
6299     return ClientServices;
6300 
6301 });
6302 
6303 /**
6304  * The following comment prevents JSLint errors concerning undefined global variables.
6305  * It tells JSLint that these identifiers are defined elsewhere.
6306  */
6307 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
6308 
6309 /** The following comment is to prevent jslint errors about 
6310  * using variables before they are defined.
6311  */
6312 /*global Handlebars */
6313 
6314 /**
6315  * JavaScript class to implement common notification
6316  *               functionality.
6317  * 
6318  * @requires Class
6319  * @requires finesse.FinesseBase
6320  */
6321 /** @private */
6322 define('restservices/Notifier',[
6323     'FinesseBase',
6324     'clientservices/ClientServices'
6325 ],
6326 function (FinesseBase, ClientServices) {
6327     var Notifier = FinesseBase.extend({
6328 		/**
6329          * Initializes the notifier object.
6330          */
6331         init : function () {
6332             this._super();
6333             this._listenerCallback = [];
6334         },
6335 
6336         /**
6337          * Add a listener.
6338          * 
6339          * @param callback_function
6340          * @param scope
6341          *            is the callback function to add
6342          */
6343         addListener : function (callback_function, scope) {
6344             var len = this._listenerCallback.length, i, cb, add = true;
6345             for (i = 0; i < len; i += 1) {
6346                 cb = this._listenerCallback[i].callback;
6347                 if (cb === callback_function) {
6348                     // this callback already exists
6349                     add = false;
6350                     break;
6351                 }
6352             }
6353             if (add) {
6354                 this._listenerCallback.push({ "callback": this._isAFunction(callback_function), "scope": (scope || window) });
6355             }            
6356         },
6357 
6358         /**
6359          * Remove a listener.
6360          * 
6361          * @param callback_function
6362          *            is the callback function to remove
6363          * @return {Boolean} true if removed
6364          */
6365         removeListener : function (callback_function) {
6366 
6367             var result = false, len = this._listenerCallback.length, i, cb;
6368             for (i = len - 1; i >= 0; i -=1) {
6369                 cb = this._listenerCallback[i].callback;
6370                 if (cb === callback_function) {
6371                     this._listenerCallback[i] = undefined;
6372                     this._listenerCallback.splice(i, 1);
6373                     result = true;
6374                     break;
6375                 }
6376             }
6377             
6378             return result;
6379         },
6380 
6381         /**
6382 	 * Removes all listeners
6383 	 * @return {undefined}
6384 	 */
6385 	reset: function () {
6386 		this._listenerCallback = [];
6387 	},
6388 
6389 	/**
6390          * Notify all listeners.
6391          * 
6392          * @param obj
6393          *            is the object that has changed
6394          */
6395         notifyListeners : function (obj) {
6396             var len = this._listenerCallback.length, i, callbackFunction, scope;
6397 
6398             for (i = 0; i < len; i += 1) {
6399                 // Be sure that one bad callback does not prevent other listeners
6400                 // from receiving.
6401                 try {
6402                     callbackFunction = this._listenerCallback[i].callback;
6403                     scope = this._listenerCallback[i].scope;
6404                     if (typeof callbackFunction === 'function') {
6405                         callbackFunction.call(scope, obj);
6406                     }
6407                 } catch (err) {
6408                     ClientServices.log("Notifier.js#notifyListeners: Exception caught: ", err);
6409                 }
6410             }
6411         },
6412 
6413         /**
6414          * Gets a copy of the listeners.
6415          * @return changeListenerCopy (array of callbacks)
6416          */
6417         getListeners : function () {
6418             var changeListenerCopy = [], len = this._listenerCallback.length, i;
6419 
6420             for (i = 0; i < len; i += 1) {
6421                 changeListenerCopy.push(this._listenerCallback[i].callback);
6422             }
6423 
6424             return changeListenerCopy;
6425         },
6426         
6427         /**
6428          * Verifies that the handler is function.
6429          * @param handler to verify
6430          * @return the handler 
6431          * @throws Error if not a function
6432          */
6433         _isAFunction : function (handler) {
6434             if (handler === undefined || typeof handler === "function") {
6435                 return handler;
6436             } else {
6437                 throw new Error("handler must be a function");
6438             }
6439         }
6440 	});
6441 	
6442 	window.finesse = window.finesse || {};
6443 	window.finesse.restservices = window.finesse.restservices || {};
6444 	window.finesse.restservices.Notifier = Notifier;
6445 
6446 	/** @namespace JavaScript classes and methods that represent REST objects and collections. */
6447     finesse.restservices = finesse.restservices || {};
6448 	
6449     return Notifier;
6450 });
6451 
6452 /**
6453  * JavaScript base object that all REST objects should inherit
6454  * from because it encapsulates and provides the common functionality that
6455  * all REST objects need.
6456  *
6457  * @requires finesse.clientservices.ClientServices
6458  * @requires Class
6459  */
6460 
6461 /** @private */
6462 define('restservices/RestBase',[
6463     "FinesseBase",
6464     "utilities/Utilities",
6465     "restservices/Notifier",
6466     "clientservices/ClientServices",
6467     "clientservices/Topics"
6468 ],
6469 function (FinesseBase, Utilities, Notifier, ClientServices, Topics) {
6470     
6471     var RestBase = FinesseBase.extend(/** @lends finesse.restservices.RestBase.prototype */{
6472 
6473         doNotLog: false,        
6474 
6475         /**
6476          * Used by _processUpdate() and restRequest().
6477          * Maps requestIds to object-wrapped callbacks passed to restRequest(),
6478          * so that one of the callbacks can be fired when a corresponding event is
6479          * received inside _processUpdate().
6480          * @private
6481          */
6482         _pendingCallbacks: {},
6483         
6484         /**
6485          * Gets the REST class for the current object.  This object throws an
6486          * exception because subtype must implement.
6487          * @throws {Error} because subtype must implement
6488          * @private
6489          */
6490         getRestClass: function () {
6491             throw new Error("getRestClass(): Not implemented in subtype.");
6492         },
6493 
6494         /**
6495          * Gets the REST type for the current object.  This object throws an
6496          * exception because subtype must implement.
6497          * @throws {Error} because subtype must implement.
6498          * @private
6499          */
6500         getRestType: function () {
6501             throw new Error("getRestType(): Not implemented in subtype.");
6502         },
6503 
6504         /**
6505          * Gets the node path for the current object.
6506          * @private
6507          */
6508         getXMPPNodePath: function () {
6509             return this.getRestUrl();
6510         },
6511         
6512         /**
6513          * Boolean function that specifies whether the REST object supports
6514          * requests. True by default. Subclasses should override if false.
6515          * @private
6516          */
6517         supportsRequests: true,
6518 
6519         /**
6520          * Boolean function that specifies whether the REST object supports
6521          * subscriptions. True by default. Subclasses should override if false.
6522          * @private
6523          */
6524         supportsSubscriptions: true,
6525         
6526         /**
6527          * Boolean function that specifies whether the REST object should retain
6528          * a copy of the REST response. False by default. Subclasses should override if true.
6529          * @private
6530          */
6531         keepRestResponse: false,
6532 
6533         /**
6534          * Number that represents the REST Response status that is returned. Only
6535          * set if keepRestResponse is set to true.
6536          * @public
6537          */
6538         restResponseStatus: null,
6539 
6540         /**
6541          * Object to store additional headers to be sent with the REST Request, null by default.
6542          * @private
6543          */
6544         extraHeaders: null,
6545 
6546         /**
6547          * Boolean function that specifies whether the REST object explicitly
6548          * subscribes. False by default. Subclasses should override if true.
6549          * @private
6550          */
6551         explicitSubscription: false,
6552 
6553         /**
6554          * Boolean function that specifies whether subscribing should be
6555          * automatically done at construction. Defaults to true.
6556          * This be overridden at object construction, not by implementing subclasses
6557          * @private
6558          */
6559         autoSubscribe: true,
6560         
6561         /**
6562          *  A unique id which gets appended to the topic for storing subscription details,
6563          *  when multiple subscriptions to the same topic is required.
6564          * @private
6565          */
6566         contextId: '',
6567                 
6568         /**
6569          Default ajax request timout value
6570         */
6571         ajaxRequestTimeout : 5000,
6572         
6573         /**
6574          * Private reference to default logger
6575          * @private
6576          */
6577         _logger: {
6578             log: ClientServices.log,
6579             error: ClientServices.log
6580         },
6581 
6582         /**
6583          * @class
6584          * JavaScript representation of a REST object. Also exposes methods to operate
6585          * on the object against the server.  This object is typically extended into individual
6586          * REST Objects (like Dialog, User, etc...), and shouldn't be used directly.
6587          *
6588          * @constructor
6589          * @param {String} id
6590          *     The ID that uniquely identifies the REST object.
6591          * @param {Object} callbacks
6592          *     An object containing callbacks for instantiation and runtime
6593          *     Callback to invoke upon successful instantiation, passes in REST object.
6594          * @param {Function} callbacks.onLoad(this)
6595          *     Callback to invoke upon loading the data for the first time.
6596          * @param {Function} callbacks.onChange(this)
6597          *     Callback to invoke upon successful update object (PUT)
6598          * @param {Function} callbacks.onAdd(this)
6599          *     Callback to invoke upon successful update to add object (POST)
6600          * @param {Function} callbacks.onDelete(this)
6601          *     Callback to invoke upon successful update to delete object (DELETE)
6602          * @param {Function} callbacks.onError(rsp)
6603          *     Callback to invoke on update error (refresh or event)
6604          *     as passed by finesse.restservices.RestBase.restRequest()
6605          *     {
6606          *         status: {Number} The HTTP status code returned
6607          *         content: {String} Raw string of response
6608          *         object: {Object} Parsed object of response
6609          *         error: {Object} Wrapped exception that was caught
6610          *         error.errorType: {String} Type of error that was caught
6611          *         error.errorMessage: {String} Message associated with error
6612          *     }
6613          * @param {RestBase} [restObj]
6614          *     A RestBase parent object which this object has an association with.
6615          * @constructs
6616          */
6617         init: function (options, callbacks, restObj) {
6618             /**
6619               * Initialize the base class
6620               */
6621             var _this = this;
6622 
6623             this._super();
6624 
6625             if (typeof options === "object") {
6626                 this._id = options.id;
6627                 this._restObj = options.parentObj;
6628                 this.autoSubscribe = (options.autoSubscribe === false) ? false : true;
6629                 this.contextId = options.contextId ? options.contextId : this.contextId;
6630                 // Pass timeout value in options object if we want to override default ajax request timeout of 5 seconds while fetching the resource
6631                 this.ajaxRequestTimeout = options.timeout || this.ajaxRequestTimeout;
6632                 this.doNotSubscribe = options.doNotSubscribe;
6633                 this.doNotRefresh = this.doNotRefresh || options.doNotRefresh;
6634                 if (typeof options.supportsRequests != "undefined") {
6635 	              this.supportsRequests = options.supportsRequests;
6636 	            }
6637                 callbacks = {
6638                     onLoad: options.onLoad,
6639                     onChange: options.onChange,
6640                     onAdd: options.onAdd,
6641                     onDelete: options.onDelete,
6642                     onError: options.onError
6643                 };
6644             } else {
6645                 this._id = options;
6646                 this._restObj = restObj;
6647             }
6648             
6649             // Common stuff
6650             
6651             this._data = {};
6652             
6653             //Contains the full rest response to be processed by upper layers if needed
6654             this._restResponse = undefined;
6655 
6656             this._lastUpdate = {};
6657 
6658             this._util = Utilities;
6659 
6660             //Should be correctly initialized in either a window OR gadget context
6661             this._config = finesse.container.Config;
6662 
6663             // Setup all the notifiers - change, load and error.
6664             this._changeNotifier = new Notifier();
6665             this._loadNotifier = new Notifier();
6666             this._addNotifier = new Notifier();
6667             this._deleteNotifier = new Notifier();
6668             this._errorNotifier = new Notifier();
6669 
6670             this._loaded = false;
6671 
6672             // Protect against null dereferencing of options allowing its
6673             // (nonexistent) keys to be read as undefined
6674             callbacks = callbacks || {};
6675 
6676             this.addHandler('load', callbacks.onLoad);
6677             this.addHandler('change', callbacks.onChange);
6678             this.addHandler('add', callbacks.onAdd);
6679             this.addHandler('delete', callbacks.onDelete);
6680             this.addHandler('error', callbacks.onError);
6681 
6682             // Attempt to get the RestType then synchronize
6683             try {
6684                 this.getRestType();
6685 
6686                 // Only subscribe if this REST object supports subscriptions
6687                 // and autoSubscribe was not requested to be disabled as a construction option
6688                 if (this.supportsSubscriptions && this.autoSubscribe && !this.doNotSubscribe) {
6689                     this.subscribe({
6690                         success: function () {
6691                             //TODO: figure out how to use Function.call() or Function.apply() here...
6692                             //this is exactly the same as the below else case other than the scope of "this"
6693                             if (typeof options === "object" && options.data) {
6694                                 if (!_this._processObject(_this._normalize(options.data))) {
6695                                     // notify of error if we fail to construct
6696                                     _this._errorNotifier.notifyListeners(_this);
6697                                 }
6698                             } else {
6699                                 // Only subscribe if this REST object supports requests
6700                                 if (_this.supportsRequests) {
6701                                     _this._synchronize();
6702                                 }
6703                             }
6704                         },
6705                         error: function (err) {
6706                             _this._errorNotifier.notifyListeners(err);
6707                         }
6708                     });
6709                 } else {
6710                     if (typeof options === "object" && options.data) {
6711                         if (!this._processObject(this._normalize(options.data))) {
6712                             // notify of error if we fail to construct
6713                             this._errorNotifier.notifyListeners(this);
6714                         }
6715                     } else {
6716                         // Only subscribe if this REST object supports requests
6717                         if (this.supportsRequests) {
6718                             this._synchronize();
6719                         }
6720                     }
6721                 }
6722 
6723             } catch (err) {
6724                 this._logger.error('id=' + this._id + ': ' + err);
6725             }
6726         },
6727 
6728         /**
6729          * Determines if the object has a particular property.
6730          * @param obj is the object to examine
6731          * @param property is the property to check for
6732          * @returns {Boolean}
6733          */
6734         hasProperty: function (obj, prop) {
6735             return (obj !== null) && (obj.hasOwnProperty(prop));
6736         },
6737 
6738         /**
6739          * Gets a property from the object.
6740          * @param obj is the object to examine
6741          * @param property is the property to get
6742          * @returns {Property Value} or {Null} if not found
6743          */
6744         getProperty: function (obj, property) {
6745             var result = null;
6746 
6747             if (this.hasProperty(obj, property) === false) {
6748                 result = null;
6749             } else {
6750                 result = obj[property];
6751             }
6752             return result;
6753         },
6754 
6755         /**
6756          * Utility to extracts the ID from the specified REST URI. This is with the
6757          * assumption that the ID is always the last element in the URI after the
6758          * "/" delimiter.
6759          * @param {String} restUri
6760          *     The REST uri (i.e. /finesse/api/User/1000).
6761          * @private
6762          */
6763         _extractId: function (restObj) {
6764             var obj, restUri = "", strLoc;
6765             for (obj in restObj) {
6766                 if (restObj.hasOwnProperty(obj)) {
6767                     restUri = restObj[obj].uri;
6768                     break;
6769                 }
6770             }
6771             return Utilities.getId(restUri);
6772         },
6773 
6774         /**
6775          * Gets the data for this object.
6776          * @returns {Object} which is contained in data
6777          */
6778         getData: function () {
6779             return this._data;
6780         },
6781         
6782         /**
6783          * Gets the complete REST response to the request made
6784          * @returns {Object} which is contained in data
6785          * @private
6786          */
6787         getRestResponse: function () {
6788             return this._restResponse;
6789         },
6790 
6791          /**
6792          * Gets the REST response status to the request made
6793          * @returns {Integer} response status
6794          * @private
6795          */
6796         getRestResponseStatus: function () {
6797             return this.restResponseStatus;
6798         },
6799 
6800         /**
6801          * The REST URL in which this object can be referenced.
6802          * @return {String}
6803          *     The REST URI for this object.
6804          * @private
6805          */
6806         getRestUrl: function () {
6807             var
6808             restObj = this._restObj,
6809             restUrl = "";
6810 
6811             //Prepend the base REST object if one was provided.
6812             if (restObj instanceof RestBase) {
6813                 restUrl += restObj.getRestUrl();
6814             }
6815             //Otherwise prepend with the default webapp name.
6816             else {
6817                 restUrl += "/finesse/api";
6818             }
6819 
6820             //Append the REST type.
6821             restUrl += "/" + this.getRestType();
6822 
6823             //Append ID if it is not undefined, null, or empty.
6824             if (this._id) {
6825                 restUrl += "/" + this._id;
6826             }
6827             return restUrl;
6828         },
6829 
6830         /**
6831          * Getter for the id of this RestBase
6832          * @returns {String}
6833          *     The id of this RestBase
6834          */
6835         getId: function () {
6836             return this._id;
6837         },
6838 
6839         /**
6840          * Synchronize this object with the server using REST GET request.
6841          * @returns {Object}
6842          *     {
6843          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request
6844          *     }
6845          * @private
6846          */
6847         _synchronize: function (retries) {
6848             // Fetch this REST object
6849             if (typeof this._id === "string") {
6850                 var _this = this, isLoaded = this._loaded, _RETRY_INTERVAL = 10 * 1000;
6851 
6852                 return this._doGET(
6853                     {
6854                         success: function (rsp) {
6855                             if (!_this._processResponse(rsp)) {
6856                                 if (retries > 0) {
6857                                     setTimeout(function () {
6858                                         _this._synchronize(retries - 1);
6859                                     }, _RETRY_INTERVAL);
6860                                 } else {
6861                                     _this._errorNotifier.notifyListeners(_this);
6862                                 }
6863                             } else {
6864                                 // If this object was already "loaded" prior to
6865                                 // the _doGET request, then call the
6866                                 // changeNotifier
6867                                 if (isLoaded) {
6868                                     _this._changeNotifier.notifyListeners(_this);
6869                                 }
6870                             }
6871                         },
6872                         error: function (rsp) {
6873                             if (retries > 0) {
6874                                 setTimeout(function () {
6875                                     _this._synchronize(retries - 1);
6876                                 }, _RETRY_INTERVAL);
6877                                 
6878                             } else {
6879                                 _this._errorNotifier.notifyListeners(rsp);
6880                             }
6881                         }
6882                     }
6883                 );
6884 
6885             } else {
6886                 throw new Error("Can't construct a <" + this.getRestType() + "> due to invalid id type.");
6887             }
6888         },
6889 
6890         /**
6891          * Adds an handler to this object.
6892          * If notifierType is 'load' and the object has already loaded, the callback is invoked immediately
6893          * @param {String} notifierType
6894          *     The type of notifier to add to ('load', 'change', 'add', 'delete', 'error')
6895          * @param {Function} callback
6896          *     The function callback to invoke.
6897          * @example
6898          *   // Handler for additions to the Dialogs collection object.  
6899          *   // When Dialog (a RestBase object) is created, add a change handler.
6900          *   _handleDialogAdd = function(dialog) {
6901          *      dialog.addHandler('change', _handleDialogChange);
6902          *   }
6903          */
6904         addHandler: function (notifierType, callback, scope) {
6905             var notifier = null;
6906             try {
6907                 Utilities.validateHandler(callback);
6908 
6909                 notifier = this._getNotifierReference(notifierType);
6910 
6911                 notifier.addListener(callback, scope);
6912                 
6913                 // If load handler is added and object has
6914                 // already been loaded, invoke callback
6915                 // immediately
6916                 if (notifierType === 'load' && this._loaded) {
6917                     callback.call((scope || window), this);
6918                 }
6919             } catch (err) {
6920                 this._logger.error('id=' + this._id + ': ' + err);
6921             }
6922         },
6923 
6924         /**
6925          * Removes a handler from this object.
6926          * @param {String} notifierType
6927          *     The type of notifier to remove ('load', 'change', 'add', 'delete', 'error')
6928          * @param {Function} callback
6929          *     The function to remove.
6930          */
6931         removeHandler: function (notifierType, callback) {
6932             var notifier = null;
6933             try {
6934                 Utilities.validateHandler(callback);
6935 
6936                 notifier = this._getNotifierReference(notifierType);
6937 
6938                 if (typeof(callback) === "undefined")
6939                 {
6940                     // Remove all listeners for the type
6941                     notifier.reset();
6942                 }
6943                 else
6944                 {
6945                     // Remove the specified listener
6946                     finesse.utilities.Utilities.validateHandler(callback);
6947                     notifier.removeListener(callback);
6948                 }
6949             } catch (err) {
6950                 this._logger.error('id=' + this._id + ': ' + err);
6951             }
6952         },
6953 
6954         /**
6955          * Utility method gating any operations that require complete instantiation
6956          * @throws Error
6957          *     If this object was not fully instantiated yet
6958          * @returns {finesse.restservices.RestBase}
6959          *     This RestBase object to allow cascading
6960          */
6961         isLoaded: function () {
6962             if (!this._loaded) {
6963                 throw new Error("Cannot operate on object that is not fully instantiated, use onLoad/onLoadError handlers");
6964             }
6965             return this; // Allow cascading
6966         },
6967 
6968 
6969 
6970         /**
6971          * Force an update on this object. Since an asynchronous GET is performed,
6972          * it is necessary to have an onChange handler registered in order to be
6973          * notified when the response of this returns.
6974          * @param {Integer} retries
6975          *    The number or retry attempts to make.
6976          * @returns {Object}
6977          *     {
6978          *         abort: {function} Function that signifies the callback handler to NOT process the response of the asynchronous request
6979          *     }
6980          */
6981         refresh: function (retries) {
6982             var _this = this;
6983 
6984             if (this.explicitSubscription) {
6985                 this._subscribeNode({
6986                     success: function () {
6987                         //Disallow GETs if object doesn't support it.
6988                         if (!_this.supportsRequests) {
6989                             throw new Error("Object doesn't support request operations.");
6990                         }
6991 
6992                         _this._synchronize(retries);
6993 
6994                         return this; // Allow cascading
6995                     },
6996                     error: function (err) {
6997                         _this._errorNotifier.notifyListeners(err);
6998                     }
6999                 });
7000             } else {
7001                 //Disallow GETs if object doesn't support it.
7002                 if (!this.supportsRequests) {
7003                     throw new Error("Object doesn't support request operations.");
7004                 }
7005 
7006                 return this._synchronize(retries);
7007             }
7008         },
7009 
7010         /**
7011          * Utility method to validate against the known schema of this RestBase
7012          * @param {Object} obj
7013          *     The object to validate
7014          * @returns {Boolean}
7015          *     True if the object fits the schema of this object. This usually
7016          *     means all required keys or nested objects are present.
7017          *     False otherwise.
7018          * @private
7019          */
7020         _validate: function (obj) {
7021             var valid = (typeof obj === "object" && this.hasProperty(obj, this.getRestType()));
7022             if (!valid)
7023             {
7024                 this._logger.error(this.getRestType() + " failed validation! Does your JS REST class return the correct string from getRestType()?");
7025             }
7026             return valid;
7027         },
7028 
7029         /**
7030          * Utility method to fetch this RestBase from the server
7031          * @param {finesse.interfaces.RequestHandlers} handlers
7032          *     An object containing the handlers for the request
7033          * @returns {Object}
7034          *     {
7035          *         abort: {function} Function that signifies the callback handler to NOT process the response of the rest request
7036          *     }
7037          * @private
7038          */
7039         _doGET: function (handlers) {
7040             return this.restRequest(this.getRestUrl(), handlers);
7041         },
7042 
7043         /**
7044          * Common update event handler used by the pubsub callback closure.
7045          * Processes the update event then notifies listeners.
7046          * @param {Object} scope
7047          *     An object containing callbacks to handle the asynchronous get
7048          * @param {Object} update
7049          *     An object containing callbacks to handle the asynchronous get
7050          * @private
7051          */
7052         _updateEventHandler: function (scope, update) {
7053             if (scope._processUpdate(update)) {
7054                 switch (update.object.Update.event) {
7055                 case "POST":
7056                     scope._addNotifier.notifyListeners(scope);
7057                     break;
7058                 case "PUT":
7059                     scope._changeNotifier.notifyListeners(scope);
7060                     break;
7061                 case "DELETE":
7062                     scope._deleteNotifier.notifyListeners(scope);
7063                     break;
7064                 }
7065             }   
7066         },
7067 
7068         /**
7069          * Utility method to create a callback to be given to OpenAjax to invoke when a message
7070          * is published on the topic of our REST URL (also XEP-0060 node).
7071          * This needs to be its own defined method so that subclasses can have their own implementation.
7072          * @returns {Function} callback(update)
7073          *     The callback to be invoked when an update event is received. This callback will
7074          *     process the update and notify listeners.
7075          * @private
7076          */
7077         _createPubsubCallback: function () {
7078             var _this = this;
7079             return function (update) {
7080                 _this._updateEventHandler(_this, update);
7081             };
7082         },
7083 
7084         /**
7085          * Subscribe to pubsub infra using the REST URL as the topic name.
7086          * @param {finesse.interfaces.RequestHandlers} handlers
7087          *     An object containing the handlers for the request
7088          * @private
7089          */
7090         subscribe: function (callbacks) {
7091             // Only need to do a subscription to client pubsub. No need to trigger
7092             // a subscription on the Finesse server due to implicit subscribe (at
7093             // least for now).
7094             var _this = this,
7095             topic = Topics.getTopic(this.getXMPPNodePath()),
7096             handlers,
7097             successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true, this.contextId);
7098 
7099             callbacks = callbacks || {};
7100 
7101             handlers = {
7102                 /** @private */
7103                 success: function () {
7104                     // Add item to the refresh list in ClientServices to refresh if
7105                     // we recover due to our resilient connection. However, do
7106                     // not add if doNotRefresh flag is set.
7107                     if (!_this.doNotRefresh) {
7108                         ClientServices.addToRefreshList(_this);
7109                     }
7110 
7111                     if (typeof callbacks.success === "function") {
7112                         callbacks.success();
7113                     }
7114                 },
7115                 /** @private */
7116                 error: function (err) {
7117                     if (successful) {
7118                         ClientServices.unsubscribe(topic, this.contextId);
7119                     }
7120 
7121                     if (typeof callbacks.error === "function") {
7122                         callbacks.error(err);
7123                     }
7124                 }
7125             };
7126 
7127             // Request a node subscription only if this object requires explicit subscriptions
7128             if (this.explicitSubscription === true) {
7129                 this._subscribeNode(handlers);
7130             } else {
7131                 if (successful) {
7132                     this._subid = "OpenAjaxOnly";
7133                     handlers.success();
7134                 } else {
7135                     handlers.error();
7136                 }
7137             }
7138 
7139             return this;
7140         },
7141 
7142         /**
7143          * Unsubscribe to pubsub infra using the REST URL as the topic name.
7144          * @param {finesse.interfaces.RequestHandlers} handlers
7145          *     An object containing the handlers for the request
7146          * @private
7147          */
7148         unsubscribe: function (callbacks) {
7149             // Only need to do a subscription to client pubsub. No need to trigger
7150             // a subscription on the Finesse server due to implicit subscribe (at
7151             // least for now).
7152             var _this = this,
7153             topic = Topics.getTopic(this.getRestUrl()),
7154             handlers;
7155 
7156             // no longer keep track of object to refresh on reconnect
7157             ClientServices.removeFromRefreshList(_this);
7158 
7159             callbacks = callbacks || {};
7160 
7161             handlers = {
7162                 /** @private */
7163                 success: function () {
7164                     if (typeof callbacks.success === "function") {
7165                         callbacks.success();
7166                     }
7167                 },
7168                 /** @private */
7169                 error: function (err) {
7170                     if (typeof callbacks.error === "function") {
7171                         callbacks.error(err);
7172                     }
7173                 }
7174             };
7175 
7176             if (this._subid) {
7177                 ClientServices.unsubscribe(topic, this.contextId);
7178                 // Request a node unsubscribe only if this object requires explicit subscriptions
7179                 if (this.explicitSubscription === true) {
7180                     this._unsubscribeNode(handlers);
7181                 } else {
7182                     this._subid = undefined;
7183                     handlers.success();
7184                 }
7185             } else {
7186                 handlers.success();
7187             }
7188 
7189             return this;
7190         },
7191 
7192         /**
7193          * Private utility to perform node subscribe requests for explicit subscriptions
7194          * @param {finesse.interfaces.RequestHandlers} handlers
7195          *     An object containing the handlers for the request
7196          * @private
7197          */
7198         _subscribeNode: function (callbacks) {
7199             var _this = this;
7200 
7201             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
7202             callbacks = callbacks || {};
7203 
7204             ClientServices.subscribeNode(this.getXMPPNodePath(), function (subid, err) {
7205                 if (err) {
7206                     if (typeof callbacks.error === "function") {
7207                         callbacks.error(err);
7208                     }
7209                 } else {
7210                     // Store the subid on a successful subscribe
7211                     _this._subid = subid;
7212                     if (typeof callbacks.success === "function") {
7213                         callbacks.success();
7214                     }
7215                 }
7216             });
7217         },
7218 
7219         /**
7220          * Private utility to perform node unsubscribe requests for explicit subscriptions
7221          * @param {finesse.interfaces.RequestHandlers} handlers
7222          *     An object containing the handlers for the request
7223          * @private
7224          */
7225         _unsubscribeNode: function (callbacks) {
7226             var _this = this;
7227 
7228             // Protect against null dereferencing of callbacks allowing its (nonexistent) keys to be read as undefined
7229             callbacks = callbacks || {};
7230 
7231             ClientServices.unsubscribeNode(this.getXMPPNodePath(), this._subid, function (err) {
7232                 _this._subid = undefined;
7233                 if (err) {
7234                     if (typeof callbacks.error === "function") {
7235                         callbacks.error(err);
7236                     }
7237                 } else {
7238                     if (typeof callbacks.success === "function") {
7239                         callbacks.success();
7240                     }
7241                 }
7242             });
7243         },
7244 
7245         /**
7246          * Validate and store the object into the internal data store.
7247          * @param {Object} object
7248          *     The JavaScript object that should match of schema of this REST object.
7249          * @returns {Boolean}
7250          *     True if the object was validated and stored successfully.
7251          * @private
7252          */
7253         _processObject: function (object) {
7254             if (this._validate(object)) {
7255                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
7256 
7257                 // If loaded for the first time, call the load notifiers.
7258                 if (!this._loaded) {
7259                     this._loaded = true;
7260                     this._loadNotifier.notifyListeners(this);
7261                 }
7262 
7263                 return true;
7264             }
7265             return false;
7266         },
7267 
7268         /**
7269          * Normalize the object to mitigate the differences between the backend
7270          * and what this REST object should hold. For example, the backend sends
7271          * send an event with the root property name being lower case. In order to
7272          * match the GET, the property should be normalized to an upper case.
7273          * @param {Object} object
7274          *     The object which should be normalized.
7275          * @returns {Object}
7276          *     Return the normalized object.
7277          * @private
7278          */
7279         _normalize: function (object) {
7280             var
7281             restType = this.getRestType(),
7282             // Get the REST object name with first character being lower case.
7283             objRestType = restType.charAt(0).toLowerCase() + restType.slice(1);
7284 
7285             // Normalize payload to match REST object. The payload for an update
7286             // use a lower case object name as oppose to upper case. Only normalize
7287             // if necessary.
7288             if (!this.hasProperty(object, restType) && this.hasProperty(object, objRestType)) {
7289                 //Since the object is going to be modified, clone the object so that
7290                 //it doesn't affect others (due to OpenAjax publishing to other
7291                 //subscriber.
7292                 object = jQuery.extend(true, {}, object);
7293 
7294                 object[restType] = object[objRestType];
7295                 delete(object[objRestType]);
7296             }
7297             return object;
7298         },
7299 
7300         /**
7301          * Utility method to process the response of a successful get
7302          * @param {Object} rsp
7303          *     The response of a successful get
7304          * @returns {Boolean}
7305          *     True if the update was successfully processed (the response object
7306          *     passed the schema validation) and updated the internal data cache,
7307          *     false otherwise.
7308          * @private
7309          */
7310         _processResponse: function (rsp) {
7311             try {
7312                 if (this.keepRestResponse) {
7313                     this._restResponse = rsp.content;
7314                     this.restResponseStatus = rsp.status;
7315                 }
7316                 return this._processObject(rsp.object);
7317             }
7318             catch (err) {
7319                 this._logger.error(this.getRestType() + ': ' + err);
7320             }
7321             return false;
7322         },
7323 
7324         /**
7325          * Method that is called at the end of _processUpdate() which by default
7326          * will just delete the requestId-to-callbacks mapping but can be overridden.
7327          * @param  {String} requestId The requestId of the event
7328          */
7329         _postProcessUpdateStrategy: function (requestId) {
7330             //Clean up _pendingCallbacks now that we fired a callback corresponding to the received requestId.
7331             delete this._pendingCallbacks[requestId];
7332         },
7333 
7334         /**
7335          * Utility method to process the update notification.
7336          * @param {Object} update
7337          *     The payload of an update notification.
7338          * @returns {Boolean}
7339          *     True if the update was successfully processed (the update object
7340          *     passed the schema validation) and updated the internal data cache,
7341          *     false otherwise.
7342          * @private
7343          */
7344         _processUpdate: function (update) {
7345             try {
7346                 var updateObj, requestId, fakeResponse, receivedError;
7347 
7348                 // The backend will send the data object with a lower case. To be
7349                 // consistent with what should be represented in this object, the
7350                 // object name should be upper case. This will normalize the object.
7351                 updateObj = this._normalize(update.object.Update.data);
7352 
7353                 // Store the last event.
7354                 this._lastUpdate = update.object;
7355 
7356                 requestId = this._lastUpdate.Update ? this._lastUpdate.Update.requestId : undefined;
7357 
7358                 if (requestId && this._pendingCallbacks[requestId]) {
7359 
7360                     /*
7361                      * The passed success/error callbacks are expecting to be passed an AJAX response, so construct
7362                      * a simulated/"fake" AJAX response object from the information in the received event.
7363                      * The constructed object should conform to the contract for response objects specified
7364                      * in _createAjaxHandler().
7365                      */
7366                     fakeResponse = {};
7367 
7368                     //The contract says that rsp.content should contain the raw text of the response so we simulate that here.
7369                     //For some reason json2xml has trouble with the native update object, so we serialize a clone of it by
7370                     //doing a parse(stringify(update)).
7371                     fakeResponse.content = this._util.json2xml(gadgets.json.parse(gadgets.json.stringify(update)));
7372 
7373                     fakeResponse.object = {};
7374 
7375                     if (updateObj.apiErrors && updateObj.apiErrors.apiError) { //Error case
7376 
7377                         //TODO: The lowercase -> uppercase ApiErrors translation method below is undesirable, can it be improved?
7378                         receivedError = updateObj.apiErrors.apiError;
7379                         fakeResponse.object.ApiErrors = {};
7380                         fakeResponse.object.ApiErrors.ApiError = {};
7381                         fakeResponse.object.ApiErrors.ApiError.ErrorData = receivedError.errorData || undefined;
7382                         fakeResponse.object.ApiErrors.ApiError.ErrorMessage = receivedError.errorMessage || undefined;
7383                         fakeResponse.object.ApiErrors.ApiError.ErrorType = receivedError.errorType || undefined;
7384 
7385                         /*
7386                          * Since this is the error case, supply the error callback with a '400 BAD REQUEST' status code. We don't know what the real
7387                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
7388                          * This is just to conform to the contract for the error callback in _createAjaxHandler().
7389                          **/
7390                         fakeResponse.status = 400;
7391 
7392                     } else { //Success case
7393 
7394                         fakeResponse.object = this._lastUpdate;
7395 
7396                         /*
7397                          * Since this is the success case, supply the success callback with a '200 OK' status code. We don't know what the real
7398                          * status code should be since the event we're constructing fakeResponse from doesn't include a status code.
7399                          * This is just to conform to the contract for the success callback in _createAjaxHandler().
7400                          **/
7401                         fakeResponse.status = 200;
7402                     }
7403 
7404                     try {
7405 
7406                         if (fakeResponse.object.ApiErrors && this._pendingCallbacks[requestId].error) {
7407                             this._pendingCallbacks[requestId].error(fakeResponse);
7408                         } 
7409                         // HTTP 202 is handled as a success, besides, we cannot infer that a non-error is a success.
7410                         /*else if (this._pendingCallbacks[requestId].success) {
7411                             this._pendingCallbacks[requestId].success(fakeResponse);
7412                         }*/
7413 
7414                     } catch (callbackErr) {
7415 
7416                         this._logger.error(this.getRestType() + ": Caught error while firing callback: " + callbackErr);
7417 
7418                     }
7419 
7420                     this._postProcessUpdateStrategy(requestId);
7421 
7422                 } 
7423 
7424                 return this._processObject(updateObj);
7425             }
7426             catch (err) {
7427                 this._logger.error(this.getRestType() + ': ' + err);
7428             }
7429             return false;
7430         },
7431 
7432         /**
7433          * Utility method to create ajax response handler closures around the
7434          * provided callbacks. Callbacks should be passed through from .ajax().
7435          * makeRequest is responsible for garbage collecting these closures.
7436          * @param {finesse.interfaces.RequestHandlers} handlers
7437          *     An object containing the handlers for the request
7438          * @returns {Object}
7439          *     {
7440          *         abort: {function} Function that signifies the callback handler to NOT process the response when the response returns
7441          *         callback: {function} Callback handler to be invoked when the response returns
7442          *     }
7443          * @private
7444          */
7445         _createAjaxHandler: function (options) {
7446             //We should not need to check this again since it has already been done in .restRequest()
7447             //options = options || {};
7448 
7449             //Flag to indicate whether or not to process the response
7450             var abort = false,
7451 
7452             //Get a reference to the parent User object
7453             _this = this;
7454 
7455             return {
7456 
7457                 abort: function () {
7458                     abort = true;
7459                 },
7460 
7461                 callback: function (rsp) {
7462 
7463                     if (abort) {
7464                         // Do not process the response
7465                         return;
7466                     }
7467 
7468                     var requestId, error = false, rspObj;
7469 
7470                     if (options.success || options.error) {
7471                         rspObj = {
7472                             status: rsp.rc,
7473                             content: rsp.text,
7474                             isUnsent: rsp.isUnsent
7475                         };
7476 
7477                         if (!_this.doNotLog) {
7478                         	_this._logger.log(_this.getRestType() + ": requestId='" + options.uuid + "', Returned with status=" + rspObj.status + ", content='" + rspObj.content + "', isUnsent = " + rspObj.isUnsent);
7479                         }
7480 
7481                         //Some responses may not have a body.
7482                         if (rsp.text && rsp.text.length > 0) {
7483                             try {
7484                                 rspObj.object = _this._util.xml2js(rsp.text);
7485                             } catch (e) {
7486                                 error = true;
7487                                 rspObj.error = {
7488                                     errorType: "parseError",
7489                                     errorMessage: "Could not serialize XML: " + e
7490                                 };
7491                             }
7492                         } else {
7493                             rspObj.object = {};
7494                         }
7495 
7496                         if (!error && rspObj.status >= 200 && rspObj.status < 300) {
7497                             if (options.success) {
7498                                 options.success(rspObj);
7499                             }
7500                         } else {
7501                             if (options.error) {
7502                                 options.error(rspObj);
7503                             }
7504                         }
7505 
7506                         /*
7507                          * If a synchronous error happened after a non-GET request (usually a validation error), we
7508                          * need to clean up the request's entry in _pendingCallbacks since no corresponding event
7509                          * will arrive later. The corresponding requestId should be present in the response headers.
7510                          *
7511                          * It appears Shindig changes the header keys to lower case, hence 'requestid' instead of
7512                          * 'requestId' below.
7513                          **/
7514                         if (rspObj.status !== 202 && rsp.headers && rsp.headers.requestid) {
7515                             requestId = rsp.headers.requestid[0];
7516                             if (_this._pendingCallbacks[requestId]) {
7517                                 delete _this._pendingCallbacks[requestId];
7518                             }
7519                         }
7520                     }
7521                 }
7522             };
7523         },
7524 
7525         /**
7526          * Utility method to make an asynchronous request
7527          * @param {String} url
7528          *     The unencoded URL to which the request is sent (will be encoded)
7529          * @param {Object} options
7530          *     An object containing additional options for the request.
7531          * @param {Object} options.content
7532          *     An object to send in the content body of the request. Will be
7533          *     serialized into XML before sending.
7534          * @param {String} options.method
7535          *     The type of request. Defaults to "GET" when none is specified.
7536          * @param {Function} options.success(rsp)
7537          *     A callback function to be invoked for a successful request.
7538          *     {
7539          *         status: {Number} The HTTP status code returned
7540          *         content: {String} Raw string of response
7541          *         object: {Object} Parsed object of response
7542          *     }
7543          * @param {Function} options.error(rsp)
7544          *     A callback function to be invoked for an unsuccessful request.
7545          *     {
7546          *         status: {Number} The HTTP status code returned
7547          *         content: {String} Raw string of response
7548          *         object: {Object} Parsed object of response
7549          *         error: {Object} Wrapped exception that was caught
7550          *         error.errorType: {String} Type of error that was caught
7551          *         error.errorMessage: {String} Message associated with error
7552          *     }
7553          * @returns {Object}
7554          *     {
7555          *         abort: {function} Function that signifies the callback handler to NOT process the response of this asynchronous request
7556          *     }
7557          * @private
7558         */
7559 		restRequest: function(url,options) {
7560 
7561 			
7562 		    // Protect against null dereferencing of options
7563 		    // allowing its (nonexistent) keys to be read as
7564 		    // undefined
7565 		    options = options || {};
7566 		    options.success = this._util
7567 			    .validateHandler(options.success);
7568 		    options.error = this._util
7569 			    .validateHandler(options.error);
7570 	
7571 		    // true if this should be a GET request, false
7572 		    // otherwise
7573 		    if (!options.method || options.method === "GET") {
7574 				// Disable caching for GETs
7575 				if (url.indexOf("?") > -1) {
7576 				    url += "&";
7577 				} else {
7578 				    url += "?";
7579 				}
7580 				url += "nocache="
7581 					+ this._util.currentTimeMillis();
7582 		    } else {
7583 				/**
7584 				 * If not GET, generate a requestID and add it
7585 				 * to the headers, then wrap callbacks into an
7586 				 * object and store it in _pendingCallbacks. If
7587 				 * we receive a synchronous error response
7588 				 * instead of a 202 as expected, the AJAX
7589 				 * handler will clean up _pendingCallbacks.
7590 				 */
7591 				/*
7592 				 * TODO: Clean up _pendingCallbacks if an entry
7593 				 * persists after a certain amount of time has
7594 				 * passed. In the block below, can store the
7595 				 * current time (new Date().getTime()) alongside
7596 				 * the callbacks in the new _pendingCallbacks
7597 				 * entry. Then iterate through a copty of
7598 				 * _pendingCallbacks, deleting all entries
7599 				 * inside _pendingCallbacks that are older than
7600 				 * a certain threshold (2 minutes for example.)
7601 				 * This solves a potential memory leak issue if
7602 				 * we never receive an event for a given stored
7603 				 * requestId; we don't want to store unfired
7604 				 * callbacks forever.
7605 				 */
7606 				/** @private */
7607 				options.uuid = this._util.generateUUID();
7608 				// params[gadgets.io.RequestParameters.HEADERS].requestId
7609 				// = options.uuid;
7610 				// By default, Shindig strips nearly all of the
7611 				// response headers, but this parameter tells
7612 				// Shindig
7613 				// to send the headers through unmodified; we
7614 				// need to be able to read the 'requestId'
7615 				// header if we
7616 				// get a synchronous error as a result of a
7617 				// non-GET request. (See the bottom of
7618 				// _createAjaxHandler().)
7619 				// params[gadgets.io.RequestParameters.GET_FULL_HEADERS]
7620 				// = "true";
7621 				this._pendingCallbacks[options.uuid] = {};
7622 				this._pendingCallbacks[options.uuid].success = options.success;
7623 				this._pendingCallbacks[options.uuid].error = options.error;
7624 		    }
7625 		    
7626 		    
7627 		    /**
7628 		     * 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.
7629 		     */
7630 			var restHost = ClientServices.getRestHost().toLowerCase();
7631 			if(restHost === "localhost" || restHost === window.location.hostname.toLowerCase()) {
7632 				return this._restRequestThroughAjax(url,options);
7633 			} else {
7634 				return this._restRequestThroughShindig(url,options);
7635 			}
7636 		},	 
7637         
7638          _restRequestThroughAjax : function(url, options) {
7639 		    var encodedUrl, ajaxHandler, scheme = window.location.protocol, host = window.location.hostname,
7640 		    port = window.location.port, dataTypeAX, contentTypeAX, mtype, postdata = "", auth, rspObj,
7641 		    locale = this._config.locale, userName=this._config.id;
7642 
7643 		    encodedUrl = encodeURI(url)
7644 		    + (window.errorOnRestRequest ? "ERROR" : "");
7645 		    
7646 		    ajaxHandler = this._createAjaxHandler(options);
7647 		    // ClientServices.makeRequest(encodedUrl,
7648 		    // ajaxHandler.callback, params);
7649 		    mtype = options.method || 'GET';
7650 		    encodedUrl = scheme + "//" + host + ":" + port
7651 			    + encodedUrl;
7652 	
7653 		    if (typeof options.content === "object"
7654 			    && typeof options.content !== "undefined") {
7655 				// Except get, all the other operations accepts
7656 				// application/xml only.
7657 				// @Consumes in all the webservices except GET
7658 				// are having this.
7659 				postdata = this._util.js2xml(options.content);
7660 				if (postdata !== null && postdata !== "") {
7661 				    contentTypeAX = "application/xml";
7662 				} else {
7663 				    // in desktop, reason code GET request was
7664 				    // called with empty object content
7665 				    // if not able to parse any content to post
7666 				    // data, then it is GET only
7667 				    contentTypeAX = "";
7668 				    dataTypeAX = "text";
7669 				}
7670 		    } else {
7671 				// No content type for GET operation, by default
7672 				// it will take text/plain || application/xml.
7673 				// for dataType - GET will result plain text,
7674 				// which we are taking as xml.
7675 				// Queried list of @Produces from code base, all
7676 				// the GET operations accepts text/plain OR
7677 				// application/xml and produces application/xml
7678 				contentTypeAX = "";
7679 				dataTypeAX = "text";
7680 			    }
7681 			    auth = this._util.getAuthHeaderString(this._config);
7682 	
7683 			    if (!this.doNotLog) {
7684 				this._logger.log(this.getRestType()
7685 					+ ": requestId='" + options.uuid
7686 					+ "', Making REST request: method="
7687 					+ (options.method || "GET") + ", url='"
7688 					+ encodedUrl + "'");
7689 			    }
7690 			    
7691 			    $.ajax({
7692 					url : encodedUrl,
7693 					headers : this.extraHeaders || {},
7694 					beforeSend : function(xhr) {
7695 					    xhr.setRequestHeader(
7696 						    "Authorization", auth);
7697 					    xhr.setRequestHeader("locale",
7698 						    locale);
7699 					    xhr.setRequestHeader("f_username",
7700 					    		userName);
7701 					    if (options.uuid) {
7702 						xhr.setRequestHeader(
7703 							"requestId",
7704 							options.uuid);
7705 					    }
7706 					},
7707 					dataType : dataTypeAX, // xml or json?
7708 					contentType : contentTypeAX,
7709 					type : mtype,
7710 					data : postdata,
7711 					timeout : this.ajaxRequestTimeout,
7712 					success : function(response, status,
7713 						xhr) {
7714 					    var rspObj = {
7715 						rc : xhr.status,
7716 						text : response,
7717 						isUnsent : xhr.readyState==0,
7718 						headers : {
7719 						    requestid : xhr
7720 							    .getResponseHeader("requestId")
7721 						}
7722 					    };
7723 					    ajaxHandler.callback(rspObj);
7724 					},
7725 					error : function(jqXHR, request, error) {
7726 					    var resObj = {
7727 						rc : jqXHR.status,
7728 						text : jqXHR.responseText,
7729 						isUnsent : jqXHR.readyState==0,
7730 						headers : {
7731 						    requestid : jqXHR
7732 							    .getResponseHeader("requestId")
7733 						}
7734 					    };
7735 					    ajaxHandler.callback(resObj);
7736 					}
7737 				    });
7738 			    return {
7739 				abort : ajaxHandler.abort
7740 			    };
7741 			},
7742 
7743 
7744 	  _restRequestThroughShindig: function(url, options) {
7745           var params, encodedUrl, ajaxHandler;
7746 
7747           params = {};
7748           options = options || {};
7749 
7750           params[gadgets.io.RequestParameters.HEADERS] = this.extraHeaders || {};
7751           params[gadgets.io.RequestParameters.METHOD] = options.method;
7752 
7753 
7754           if (!options.method || options.method === "GET") {
7755 
7756           } else {
7757               params[gadgets.io.RequestParameters.HEADERS].requestId = options.uuid;           
7758               params[gadgets.io.RequestParameters.GET_FULL_HEADERS] = "true";
7759 
7760           }
7761 
7762           encodedUrl = encodeURI(url) + (window.errorOnRestRequest ? "ERROR" : "");
7763 
7764           if (!this.doNotLog) {
7765               this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', Making REST request: method=" + (options.method || "GET") + ", url='" + encodedUrl + "'");
7766           }
7767 
7768          
7769           if (typeof options.content === "object") {
7770             
7771               params[gadgets.io.RequestParameters.HEADERS]["Content-Type"] = "application/xml";
7772               params[gadgets.io.RequestParameters.POST_DATA] = this._util.js2xml(options.content);
7773               
7774               if (!this.doNotLog) {
7775                   this._logger.log(this.getRestType() + ": requestId='" + options.uuid + "', POST_DATA='" + params[gadgets.io.RequestParameters.POST_DATA] + "'");
7776               }
7777           }
7778 
7779           ajaxHandler = this._createAjaxHandler(options);
7780           ClientServices.makeRequest(encodedUrl, ajaxHandler.callback, params);
7781 
7782           return {
7783               abort: ajaxHandler.abort
7784           };
7785 	  },
7786 	  
7787     /**
7788 	 * Retrieves a reference to a particular notifierType.
7789 	 * 
7790 	 * @param notifierType
7791 	 *                is a string which indicates the notifier to retrieve
7792 	 *                ('load', 'change', 'add', 'delete', 'error')
7793 	 * @return {Notifier}
7794 	 * @private
7795 	 */
7796         _getNotifierReference: function (notifierType) {
7797             var notifierReference = null;
7798             if (notifierType === 'load') {
7799                 notifierReference = this._loadNotifier;
7800             } else if (notifierType === 'change') {
7801                 notifierReference = this._changeNotifier;
7802             } else if (notifierType === 'add') {
7803                 notifierReference = this._addNotifier;
7804             } else if (notifierType === 'delete') {
7805                 notifierReference = this._deleteNotifier;
7806             } else if (notifierType === 'error') {
7807                 notifierReference = this._errorNotifier;
7808             } else {
7809                 throw new Error("_getNotifierReference(): Trying to get unknown notifier(notifierType=" + notifierType + ")");
7810             }
7811 
7812             return notifierReference;
7813         }
7814     });
7815 
7816     window.finesse = window.finesse || {};
7817     window.finesse.restservices = window.finesse.restservices || {};
7818     window.finesse.restservices.RestBase = RestBase;
7819     
7820     return RestBase;
7821 });
7822 
7823 /** The following comment is to prevent jslint errors about 
7824  * using variables before they are defined.
7825  */
7826 /*global finesse*/
7827 
7828 /**
7829  * JavaScript base object that all REST collection objects should
7830  * inherit from because it encapsulates and provides the common functionality
7831  * that all REST objects need.
7832  *
7833  * @requires finesse.clientservices.ClientServices
7834  * @requires Class
7835  * @requires finesse.FinesseBase
7836  * @requires finesse.restservices.RestBase
7837  */
7838 
7839 /**
7840  * @class
7841  * JavaScript representation of a REST collection object.
7842  *
7843  * @constructor
7844  * @param {Function} callbacks.onCollectionAdd(this)
7845  *     Callback to invoke upon successful item addition to the collection.
7846  * @param {Function} callbacks.onCollectionDelete(this)
7847  *     Callback to invoke upon successful item deletion from the collection.
7848  * @borrows finesse.restservices.RestBase as finesse.restservices.RestCollectionBase
7849  */
7850 /** @private */
7851 define('restservices/RestCollectionBase',[
7852     'restservices/RestBase',
7853     'utilities/Utilities',
7854     'restservices/Notifier'
7855 ],
7856 function (RestBase, Utilities, Notifier) {
7857     var RestCollectionBase = RestBase.extend(/** @lends finesse.restservices.RestCollectionBase.prototype */{
7858 
7859         /**
7860          * Boolean function that specifies whether the collection handles subscribing
7861          * and propagation of events for the individual REST object items the
7862          * collection holds. False by default. Subclasses should override if true.
7863          * @private
7864          */
7865         supportsRestItemSubscriptions: false,
7866 
7867         /**
7868          * Gets the constructor the individual items that make of the collection.
7869          * For example, a Dialogs collection object will hold a list of Dialog items.
7870          * @throws Error because subtype must implement.
7871          * @private
7872          */
7873         getRestItemClass: function () {
7874             throw new Error("getRestItemClass(): Not implemented in subtype.");
7875         },
7876 
7877         /**
7878          * Gets the REST type of the individual items that make of the collection.
7879          * For example, a Dialogs collection object will hold a list of Dialog items.
7880          * @throws Error because subtype must implement.
7881          * @private
7882          */
7883         getRestItemType: function () {
7884             throw new Error("getRestItemType(): Not implemented in subtype.");
7885         },
7886 
7887         /**
7888          * The base REST URL in which items this object contains can be referenced.
7889          * @return {String}
7890          *     The REST URI for items this object contains.
7891          * @private
7892          */
7893         getRestItemBaseUrl: function () {
7894             var
7895             restUrl = "/finesse/api";
7896 
7897             //Append the REST type.
7898             restUrl += "/" + this.getRestItemType();
7899 
7900             return restUrl;
7901         },
7902 
7903          /*
7904          * Creates a new object from the given data
7905          * @param data - data object
7906          * @private
7907          */
7908         _objectCreator: function (data) {
7909             var objectId = this._extractId(data),
7910             newRestObj = this._collection[objectId],
7911             _this = this;
7912 
7913             //Prevent duplicate entries into collection.
7914             if (!newRestObj) {
7915                 //Create a new REST object using the subtype defined by the
7916                 //overridden method.
7917                 newRestObj = new (this.getRestItemClass())({
7918                     doNotSubscribe: this.handlesItemSubscription,
7919                     doNotRefresh: this.handlesItemRefresh,
7920                     id: objectId,
7921                     data: data,
7922                     onLoad: function (newObj) {
7923                         //Normalize and add  REST object to collection datastore.
7924                         _this._collection[objectId] = newObj;
7925                         _this._collectionAddNotifier.notifyListeners(newObj);
7926                         _this.length += 1;
7927                     }
7928                 });
7929             }
7930             else {
7931                 //If entry already exist in collection, process the new event,
7932                 //and notify all change listeners since an existing object has
7933                 //change. This could happen in the case when the Finesse server
7934                 //cycles, and sends a snapshot of the user's calls.
7935                 newRestObj._processObject(data);
7936                 newRestObj._changeNotifier.notifyListeners(newRestObj);
7937             }
7938         },
7939 
7940         /*
7941          * Deletes and object and notifies its handlers
7942          * @param data - data object
7943          * @private
7944          */
7945         _objectDeleter: function (data) {
7946             var objectId = this._extractId(data),
7947             object = this._collection[objectId];
7948             if (object) {
7949                 //Even though this is a delete, let's make sure the object we are passing has got good data
7950                 object._processObject(data);
7951                 //Notify listeners and delete from internal datastore.
7952                 this._collectionDeleteNotifier.notifyListeners(object);
7953                 delete this._collection[objectId];
7954                 this.length -= 1;
7955             }
7956         },
7957 
7958          /**
7959           * Creates an anonymous function for notifiying error listeners of a particular object
7960           * data.
7961           * @param obj - the objects whose error listeners to notify
7962           * @returns {Function}
7963           *     Callback for notifying of errors
7964           * @private
7965           */
7966         _createErrorNotifier: function (obj) {
7967             return function (err) {
7968                 obj._errorNotifier.notifyListeners(err);
7969             };
7970         },
7971 
7972          /**
7973           * Replaces the collection with a refreshed list using the passed in
7974           * data.
7975           * @param data - data object (usually this._data)
7976           * @private
7977           */
7978          _buildRefreshedCollection: function (data) {
7979             var i, dataObject, object, objectId, dataArray, newIds = [], foundFlag;
7980             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
7981                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
7982             } else {
7983                 dataArray = [];
7984             }
7985 
7986             // iterate through each item in the new data and add to or update collection
7987             for (i = 0; i < dataArray.length; i += 1) {
7988                 dataObject = {};
7989                 dataObject[this.getRestItemType()] = dataArray[i];
7990                 objectId = this._extractId(dataObject);
7991 
7992                 this._objectCreator(dataObject);
7993                 newIds.push(objectId);
7994 
7995                 // resubscribe if the object requires an explicit subscription
7996                 object = this._collection[objectId];
7997                 if (this.handlesItemRefresh && object.explicitSubscription) {
7998                     object._subscribeNode({
7999                         error: this._createErrorNotifier(object)
8000                     });
8001                 }
8002             }
8003 
8004             // now clean up items (if any) that were removed
8005             for (objectId in this._collection) {
8006                 if (this._collection.hasOwnProperty(objectId)) {
8007                     foundFlag = false;
8008                     for (i = newIds.length - 1; i >= 0; i -= 1) {
8009                         if (newIds[i] === objectId) {
8010                             foundFlag = true;
8011                             break;
8012                         }
8013                     }
8014                     // did not find in updated list, so delete it
8015                     if (!foundFlag) {
8016                         this._objectDeleter({'data': this._collection[objectId]._data});
8017                     }
8018                 }
8019             }
8020         },
8021 
8022          /**
8023           * The actual refresh operation, refactored out so we don't have to repeat code
8024           * @private
8025           */
8026         _RESTRefresh: function () {
8027             var _this = this;
8028             this._doGET({
8029                 success: function(rsp) {
8030                     if (_this._processResponse(rsp)) {
8031                         _this._buildRefreshedCollection(_this._data);
8032                     } else {
8033                         _this._errorNotifier.notifyListeners(_this);
8034                     }
8035                 },
8036                 error: function(rsp) {
8037                     _this._errorNotifier.notifyListeners(rsp);
8038                 }
8039             });            
8040         },
8041 
8042         /**
8043          * Force an update on this object. Since an asynchronous GET is performed,
8044          * it is necessary to have an onChange handler registered in order to be
8045          * notified when the response of this returns.
8046          * @returns {finesse.restservices.RestBaseCollection}
8047          *     This RestBaseCollection object to allow cascading
8048          */
8049          refresh: function() {
8050             var _this = this, isLoaded = this._loaded;
8051 
8052             // resubscribe if the collection requires an explicit subscription
8053             if (this.explicitSubscription) {
8054                 this._subscribeNode({
8055                     success: function () {
8056                         _this._RESTRefresh();
8057                     },
8058                     error: function (err) {
8059                         _this._errorNotifier.notifyListeners(err);
8060                     }
8061                 });
8062             } else {
8063                 this._RESTRefresh();
8064             }
8065 
8066             return this; // Allow cascading
8067          },
8068 
8069         /**
8070          * @private
8071          * The _addHandlerCb and _deleteHandlerCb require that data be passed in the
8072          * format of an array of {(Object Type): object} objects. For example, a
8073          * queues object would return [{Queue: queue1}, {Queue: queue2}, ...].
8074          * @param skipOuterObject If {true} is passed in for this param, then the "data"
8075          *                           property is returned instead of an object with the
8076          *                           data appended.
8077          * @return {Array}
8078          */
8079         extractCollectionData: function (skipOuterObject) {
8080             var restObjs,
8081             obj,
8082             result = [],
8083             _this = this;
8084             
8085             if (this._data)
8086             {
8087                 restObjs = this._data[this.getRestItemType()];
8088     
8089                 if (restObjs)
8090                 {
8091                     // check if there are multiple objects to pass
8092                     if (!$.isArray(restObjs))
8093                     {
8094                         restObjs = [restObjs];
8095                     }
8096     
8097                     // if so, create an object for each and add to result array
8098                     $.each(restObjs, function (id, object) {
8099                         if (skipOuterObject === true)
8100                         {
8101                             obj = object;
8102                         }
8103                         else
8104                         {
8105                             obj = {};
8106                             obj[_this.getRestItemType()] = object;
8107                         }
8108                         result.push(obj);
8109                     });
8110                 }
8111                 
8112             }
8113             
8114             return result;
8115         },
8116 
8117         /**
8118          * For Finesse, collections are handled uniquely on a POST and
8119          * doesn't necessary follow REST conventions. A POST on a collection
8120          * doesn't mean that the collection has been created, it means that an
8121          * item has been added to the collection. This function will generate
8122          * a closure which will handle this logic appropriately.
8123          * @param {Object} scope
8124          *     The scope of where the callback should be invoked.
8125          * @private
8126          */
8127         _addHandlerCb: function (scope) {
8128             return function (restItem) {
8129                 var data = restItem.extractCollectionData();               
8130 
8131                 $.each(data, function (id, object) {
8132                     scope._objectCreator(object);
8133                 });
8134             };
8135         },
8136 
8137         /**
8138          * For Finesse, collections are handled uniquely on a DELETE and
8139          * doesn't necessary follow REST conventions. A DELETE on a collection
8140          * doesn't mean that the collection has been deleted, it means that an
8141          * item has been deleted from the collection. This function will generate
8142          * a closure which will handle this logic appropriately.
8143          * @param {Object} scope
8144          *     The scope of where the callback should be invoked.
8145          * @private
8146          */
8147         _deleteHandlerCb: function (scope) {
8148             return function (restItem) {
8149                 var data = restItem.extractCollectionData();
8150 
8151                 $.each(data, function (id, obj) {
8152                     scope._objectDeleter(obj);
8153                 });
8154             };
8155         },
8156 
8157         /**
8158          * Utility method to process the update notification for Rest Items
8159          * that are children of the collection whose events are published to
8160          * the collection's node.
8161          * @param {Object} update
8162          *     The payload of an update notification.
8163          * @returns {Boolean}
8164          *     True if the update was successfully processed (the update object
8165          *     passed the schema validation) and updated the internal data cache,
8166          *     false otherwise.
8167          * @private
8168          */
8169         _processRestItemUpdate: function (update) {
8170             var object, objectId, updateObj = update.object.Update;
8171 
8172             //Extract the ID from the source if the Update was an error.
8173             if (updateObj.data.apiErrors) {
8174                 objectId = Utilities.getId(updateObj.source);
8175             }
8176             //Otherwise extract from the data object itself.
8177             else {
8178                 objectId = this._extractId(updateObj.data);
8179             }
8180 
8181             object = this._collection[objectId];
8182             if (object) {
8183                 if (object._processUpdate(update)) {
8184                     switch (updateObj.event) {
8185                     case "POST":
8186                         object._addNotifier.notifyListeners(object);
8187                         break;
8188                     case "PUT":
8189                         object._changeNotifier.notifyListeners(object);
8190                         break;
8191                     case "DELETE":
8192                         object._deleteNotifier.notifyListeners(object);
8193                         break;
8194                     }
8195                 }
8196             }
8197         },
8198 
8199         /**
8200          * SUBCLASS IMPLEMENTATION (override):
8201          * For collections, this callback has the additional responsibility of passing events
8202          * of collection item updates to the item objects themselves. The collection needs to
8203          * do this because item updates are published to the collection's node.
8204          * @returns {Function}
8205          *     The callback to be invoked when an update event is received
8206          * @private
8207          */
8208         _createPubsubCallback: function () {
8209             var _this = this;
8210             return function (update) {
8211                 //If the source of the update is our REST URL, this means the collection itself is modified
8212                 if (update.object.Update.source === _this.getRestUrl()) {
8213                     _this._updateEventHandler(_this, update);
8214                 } else {
8215                     //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
8216                     //rest item update of one of our children that was published on our node (OpenAjax topic)
8217                     _this._processRestItemUpdate(update);
8218                 }
8219             };
8220         },
8221 
8222         /**
8223          * @class
8224          * This is the base collection object. 
8225          *
8226          * @constructs
8227          * @augments finesse.restservices.RestBase
8228          * @see finesse.restservices.Contacts
8229          * @see finesse.restservices.Dialogs
8230          * @see finesse.restservices.PhoneBooks
8231          * @see finesse.restservices.Queues
8232          * @see finesse.restservices.WorkflowActions
8233          * @see finesse.restservices.Workflows
8234          * @see finesse.restservices.WrapUpReasons
8235          */
8236         _fakeConstuctor: function () {
8237             /* This is here to hide the real init constructor from the public docs */
8238         },
8239         
8240        /**
8241          * @private
8242          * @param {Object} options
8243          *     An object with the following properties:<ul>
8244          *         <li><b>id:</b> The id of the object being constructed</li>
8245          *         <li><b>onCollectionAdd(this): (optional)</b> when an object is added to this collection</li>
8246          *         <li><b>onCollectionDelete(this): (optional)</b> when an object is removed from this collection</li>
8247          *         <li><b>onLoad(this): (optional)</b> when the collection is successfully loaded from the server</li>
8248          *         <li><b>onChange(this): (optional)</b> when an update notification of the collection is received. 
8249          *         This does not include adding and deleting members of the collection</li>
8250          *         <li><b>onAdd(this): (optional)</b> when a notification that the collection is created is received</li>
8251          *         <li><b>onDelete(this): (optional)</b> when a notification that the collection is deleted is received</li>
8252          *         <li><b>onError(rsp): (optional)</b> if loading of the collection fails, invoked with the error response object:<ul>
8253          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8254          *             <li><b>content:</b> {String} Raw string of response</li>
8255          *             <li><b>object:</b> {Object} Parsed object of response</li>
8256          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8257          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8258          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8259          *             </ul></li>
8260          *         </ul></li>
8261          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8262          **/
8263         init: function (options) {
8264 
8265             options = options || {};
8266             options.id = "";
8267 
8268             //Make internal datastore collection to hold a list of objects.
8269             this._collection = {};
8270             this.length = 0;
8271 
8272             //Collections will have additional callbacks that will be invoked when
8273             //an item has been added/deleted.
8274             this._collectionAddNotifier = new Notifier();
8275             this._collectionDeleteNotifier = new Notifier();
8276 
8277             //Initialize the base class.
8278             this._super(options);
8279 
8280             this.addHandler('collectionAdd', options.onCollectionAdd);
8281             this.addHandler('collectionDelete', options.onCollectionDelete);
8282 
8283             //For Finesse, collections are handled uniquely on a POST/DELETE and
8284             //doesn't necessary follow REST conventions. A POST on a collection
8285             //doesn't mean that the collection has been created, it means that an
8286             //item has been added to the collection. A DELETE means that an item has
8287             //been removed from the collection. Due to this, we are attaching
8288             //special callbacks to the add/delete that will handle this logic.
8289             this.addHandler("add", this._addHandlerCb(this));
8290             this.addHandler("delete", this._deleteHandlerCb(this));
8291         },
8292 
8293         /**
8294          * Returns the collection.
8295          * @returns {Object}
8296          *     The collection as an object
8297          */
8298         getCollection: function () {
8299             //TODO: is this safe? or should we instead return protected functions such as .each(function)?
8300             return this._collection;
8301         },
8302 
8303         /**
8304          * Utility method to build the internal collection data structure (object) based on provided data
8305          * @param {Object} data
8306          *     The data to build the internal collection from
8307          * @private
8308          */
8309         _buildCollection: function (data) {
8310             var i, object, objectId, dataArray;
8311             if (data && this.getProperty(data, this.getRestItemType()) !== null) {
8312                 dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
8313                 for (i = 0; i < dataArray.length; i += 1) {
8314 
8315                     object = {};
8316                     object[this.getRestItemType()] = dataArray[i];
8317                     objectId = this._extractId(object);
8318                     this._collection[objectId] = new (this.getRestItemClass())({
8319                         doNotSubscribe: this.handlesItemSubscription,
8320                         doNotRefresh: this.handlesItemRefresh,
8321                         id: objectId,
8322                         data: object
8323                     });
8324                     this.length += 1;
8325                 }
8326             }
8327         },
8328 
8329         /**
8330          * Called to know whether to include an item in the _collection and _data. Return false to keep it, true to filter out (discard) it.
8331          * Override this in subclasses if you need only object with certain attribute values.
8332          * @param  {Object} item Item to test.
8333          * @return {Boolean} False to keep, true to filter out (discard);
8334          */
8335         _filterOutItem: function (item) {
8336             return false;
8337         },
8338     
8339         /**
8340          * Validate and store the object into the internal data store.
8341          * SUBCLASS IMPLEMENTATION (override):
8342          * Performs collection specific logic to _buildCollection internally based on provided data
8343          * @param {Object} object
8344          *     The JavaScript object that should match of schema of this REST object.
8345          * @returns {Boolean}
8346          *     True if the object was validated and stored successfully.
8347          * @private
8348          */
8349         _processObject: function (object) {
8350             var i,
8351                 restItemType = this.getRestItemType(),
8352                 items;
8353             if (this._validate(object)) {
8354                 this._data = this.getProperty(object, this.getRestType()); // Should clone the object here?
8355     
8356                 // If a subclass has overriden _filterOutItem then we'll need to run through the items and remove them
8357                 if (this._data)
8358                 {
8359                     items = this._data[restItemType];
8360     
8361                     if (typeof(items) !== "undefined")
8362                     {
8363                         if (typeof(items.length) === "undefined")
8364                         {
8365                             // Single object
8366                             if (this._filterOutItem(items))
8367                             {
8368                                 this._data[restItemType] = items = [];
8369                             }
8370                             
8371                         }
8372                         else
8373                         {
8374                             // filter out objects
8375                             for (i = items.length - 1; i !== -1; i = i - 1)
8376                             {
8377                                 if (this._filterOutItem(items[i]))
8378                                 {
8379                                     items.splice(i, 1);
8380                                 }
8381                             }
8382                         }
8383                     }
8384                 }
8385     
8386                 // If loaded for the first time, call the load notifiers.
8387                 if (!this._loaded) {
8388                     this._buildCollection(this._data);
8389                     this._loaded = true;
8390                     this._loadNotifier.notifyListeners(this);
8391                 }
8392                 
8393                 return true;
8394                 
8395             }
8396             return false;
8397         },
8398 
8399         /**
8400          * Retrieves a reference to a particular notifierType.
8401          * @param {String} notifierType
8402          *      Specifies the notifier to retrieve (load, change, error, add, delete)
8403          * @return {Notifier} The notifier object.
8404          */
8405         _getNotifierReference: function (notifierType) {
8406             var notifierReference;
8407 
8408             try {
8409                 //Use the base method to get references for load/change/error.
8410                 notifierReference = this._super(notifierType);
8411             } catch (err) {
8412                 //Check for add/delete
8413                 if (notifierType === "collectionAdd") {
8414                     notifierReference = this._collectionAddNotifier;
8415                 } else if (notifierType === "collectionDelete") {
8416                     notifierReference = this._collectionDeleteNotifier;
8417                 } else {
8418                     //Rethrow exception from base class.
8419                     throw err;
8420                 }
8421             }
8422             return notifierReference;
8423         }
8424     });
8425     
8426     window.finesse = window.finesse || {};
8427     window.finesse.restservices = window.finesse.restservices || {};
8428     window.finesse.restservices.RestCollectionBase = RestCollectionBase;
8429     
8430     return RestCollectionBase;
8431 });
8432 
8433 /**
8434  * JavaScript representation of the Finesse Dialog object.
8435  *
8436  * @requires finesse.clientservices.ClientServices
8437  * @requires Class
8438  * @requires finesse.FinesseBase
8439  * @requires finesse.restservices.RestBase
8440  */
8441 
8442 /** @private */
8443 define('restservices/DialogBase',[
8444         'restservices/RestBase',
8445         'utilities/Utilities'
8446     ],
8447     function (RestBase, Utilities) {
8448         var DialogBase = RestBase.extend(/** @lends finesse.restservices.DialogBase.prototype */{
8449 
8450             /**
8451              * @class
8452              * A DialogBase is an attempted connection between or among multiple participants,
8453              * for example, a regular phone call, a chat, or an email.
8454              *
8455              * This object is typically extended into individual
8456              * REST Objects (like Dialog, MediaDialog, etc...), and shouldn't be used directly.
8457              *
8458              * @augments finesse.restservices.RestBase
8459              * @constructs
8460              */
8461             _fakeConstuctor: function () {
8462                 /* This is here to hide the real init constructor from the public docs */
8463             },
8464 
8465             /**
8466              * @private
8467              *
8468              * @param {Object} options
8469              *     An object with the following properties:<ul>
8470              *         <li><b>id:</b> The id of the object being constructed</li>
8471              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8472              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8473              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8474              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8475              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8476              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8477              *             <li><b>content:</b> {String} Raw string of response</li>
8478              *             <li><b>object:</b> {Object} Parsed object of response</li>
8479              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8480              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8481              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8482              *             </ul></li>
8483              *         </ul></li>
8484              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8485              **/
8486             init: function (options) {
8487                 this._super(options);
8488             },
8489 
8490             /**
8491              * @private
8492              * Gets the REST class for the current object - this is the Dialog class.
8493              * @returns {Object} The Dialog class.
8494              */
8495             getRestClass: function () {
8496                 throw new Error("getRestClass(): Not implemented in subtype.");
8497             },
8498 
8499             /**
8500              * @private
8501              * The constant for agent device.
8502              */
8503             _agentDeviceType: "AGENT_DEVICE",
8504 
8505             /**
8506              * @private
8507              * Gets the REST type for the current object - this is a "Dialog".
8508              * @returns {String} The Dialog string.
8509              */
8510             getRestType: function () {
8511                 return "Dialog";
8512             },
8513 
8514             /**
8515              * @private
8516              * Override default to indicate that this object doesn't support making
8517              * requests.
8518              */
8519             supportsRequests: false,
8520 
8521             /**
8522              * @private
8523              * Override default to indicate that this object doesn't support subscriptions.
8524              */
8525             supportsSubscriptions: false,
8526 
8527 
8528             /**
8529              * Getter for the media type.
8530              * @returns {String} The media type.
8531              */
8532             getMediaType: function () {
8533                 this.isLoaded();
8534                 return this.getData().mediaType;
8535             },
8536 
8537             /**
8538              * @private
8539              * Getter for the uri.
8540              * @returns {String} The uri.
8541              */
8542             getDialogUri: function () {
8543                 this.isLoaded();
8544                 return this.getData().uri;
8545             },
8546 
8547             /**
8548              * Getter for the callType.
8549              * @deprecated Use getMediaProperties().callType instead.
8550              * @returns {String} The callType.
8551              */
8552             getCallType: function () {
8553                 this.isLoaded();
8554                 return this.getData().mediaProperties.callType;
8555             },
8556 
8557 
8558             /**
8559              * Getter for the Dialog state.
8560              * @returns {String} The Dialog state.
8561              */
8562             getState: function () {
8563                 this.isLoaded();
8564                 return this.getData().state;
8565             },
8566 
8567             /**
8568              * Retrieves a list of participants within the Dialog object.
8569              * @returns {Object} Array list of participants.
8570              * Participant entity properties are as follows:<ul>
8571              *     <li>state - The state of the Participant. 
8572              *     <li>stateCause - The state cause of the Participant.
8573              *     <li>mediaAddress - The media address of the Participant.
8574              *     <li>startTime - The start Time of the Participant.
8575              *     <li>stateChangeTime - The time when participant state has changed.
8576              *     <li>actions - These are the actions that a Participant can perform</ul>
8577              */
8578             getParticipants: function () {
8579                 this.isLoaded();
8580                 var participants = this.getData().participants.Participant;
8581                 //Due to the nature of the XML->JSO converter library, a single
8582                 //element in the XML array will be considered to an object instead of
8583                 //a real array. This will handle those cases to ensure that an array is
8584                 //always returned.
8585 
8586                 return Utilities.getArray(participants);
8587             },
8588 
8589             /**
8590              * This method retrieves the participant timer counters
8591              *
8592              * @param {String} participantExt Extension of participant.
8593              * @returns {Object} Array of Participants which contains properties :<ul>
8594              *     <li>state - The state of the Participant. 
8595              *     <li>startTime - The start Time of the Participant.
8596              *     <li>stateChangeTime - The time when participant state has changed.</ul>
8597              * 
8598              */
8599             getParticipantTimerCounters : function (participantExt) {
8600                 var part, participantTimerCounters = {}, idx, participants;
8601 
8602                 participants = this.getParticipants();
8603 
8604 
8605                 //Loop through all the participants and find the right participant (based on participantExt)
8606                 for(idx=0;idx<participants.length;idx=idx+1)
8607                 {
8608                     part = participants[idx];
8609 
8610                     if (part.mediaAddress === participantExt)
8611                     {
8612                         participantTimerCounters.startTime= part.startTime;
8613                         participantTimerCounters.stateChangeTime= part.stateChangeTime;
8614                         participantTimerCounters.state= part.state;
8615                         break;
8616                     }
8617                 }
8618 
8619                 return participantTimerCounters;
8620             },
8621 
8622 
8623             /**
8624              * Retrieves a list of media properties from the dialog object.
8625              * @returns {Object} Map of call variables; names mapped to values.
8626              * Variables may include the following:<ul>
8627              * <li>dialedNumber: The number dialed.
8628              * <li>callType: The type of call. Call types include:<ul>
8629              *     <li>ACD_IN
8630              *     <li>PREROUTE_ACD_IN
8631              *     <li>PREROUTE_DIRECT_AGENT
8632              *     <li>TRANSFER
8633              *     <li>OTHER_IN
8634              *     <li>OUT
8635              *     <li>AGENT_INSIDE
8636              *     <li>CONSULT
8637              *     <li>CONFERENCE
8638              *     <li>SUPERVISOR_MONITOR
8639              *     <li>OUTBOUND
8640              *     <li>OUTBOUND_PREVIEW</ul>
8641              * <li>DNIS: The DNIS provided. For routed calls, this is the route point.
8642              * <li>wrapUpReason: A description of the call.
8643              * <li>queueNumber: Number of the agent Skill Group the call is attributed to.
8644              * <li>queueName: Name of the agent Skill Group the call is attributed to.
8645              * <li>callKeyCallId: unique number of the call routed on a particular day.
8646              * <li>callKeyPrefix: represents the day when the call is routed.
8647              * <li>callKeySequenceNum: represents the sequence number of call.
8648              * <li>Call Variables, by name.  The name indicates whether it is a call variable or ECC variable.
8649              * Call variable names start with callVariable#, where # is 1-10. ECC variable names (both scalar and array) are prepended with "user".
8650              * ECC variable arrays include an index enclosed within square brackets located at the end of the ECC array name.
8651              * <li>The following call variables provide additional details about an Outbound Option call:<ul>
8652              *     <li>BACampaign
8653              *     <li>BAAccountNumber
8654              *     <li>BAResponse
8655              *     <li>BAStatus<ul>
8656              *         <li>PREDICTIVE_OUTBOUND: A predictive outbound call.
8657              *         <li>PROGRESSIVE_OUTBOUND: A progressive outbound call.
8658              *         <li>PREVIEW_OUTBOUND_RESERVATION: Agent is reserved for a preview outbound call.
8659              *         <li>PREVIEW_OUTBOUND: Agent is on a preview outbound call.</ul>
8660              *     <li>BADialedListID
8661              *     <li>BATimeZone
8662              *     <li>BABuddyName</ul></ul>
8663              *
8664              */
8665             getMediaProperties: function () {
8666                 var mpData, currentMediaPropertiesMap, thisMediaPropertiesJQuery;
8667 
8668                 this.isLoaded();
8669 
8670                 // We have to convert to jQuery object to do a proper compare
8671                 thisMediaPropertiesJQuery = jQuery(this.getData().mediaProperties);
8672 
8673                 if ((this._lastMediaPropertiesJQuery !== undefined)
8674                     && (this._lastMediaPropertiesMap !== undefined)
8675                     && (this._lastMediaPropertiesJQuery.is(thisMediaPropertiesJQuery))) {
8676 
8677                     return this._lastMediaPropertiesMap;
8678                 }
8679 
8680                 currentMediaPropertiesMap = {};
8681 
8682                 mpData = this.getData().mediaProperties;
8683 
8684                 if (mpData) {
8685                     if (mpData.callvariables && mpData.callvariables.CallVariable) {
8686                         if (mpData.callvariables.CallVariable.length === undefined) {
8687                             mpData.callvariables.CallVariable = [mpData.callvariables.CallVariable];
8688                         }
8689                         jQuery.each(mpData.callvariables.CallVariable, function (i, callVariable) {
8690                             currentMediaPropertiesMap[callVariable.name] = callVariable.value;
8691                         });
8692                     }
8693 
8694                     jQuery.each(mpData, function (key, value) {
8695                         if (key !== 'callvariables') {
8696                             currentMediaPropertiesMap[key] = value;
8697                         }
8698                     });
8699                 }
8700 
8701                 this._lastMediaPropertiesMap = currentMediaPropertiesMap;
8702                 this._lastMediaPropertiesJQuery = thisMediaPropertiesJQuery;
8703 
8704                 return this._lastMediaPropertiesMap;
8705             },
8706 
8707 
8708 
8709             /**
8710              * @private
8711              * Invoke a request to the server given a content body and handlers.
8712              *
8713              * @param {Object} contentBody
8714              *     A JS object containing the body of the action request.
8715              * @param {finesse.interfaces.RequestHandlers} handlers
8716              *     An object containing the handlers for the request
8717              */
8718             _makeRequest: function (contentBody, handlers) {
8719                 // Protect against null dereferencing of options allowing its
8720                 // (nonexistent) keys to be read as undefined
8721                 handlers = handlers || {};
8722 
8723                 this.restRequest(this.getRestUrl(), {
8724                     method: 'PUT',
8725                     success: handlers.success,
8726                     error: handlers.error,
8727                     content: contentBody
8728                 });
8729             }
8730 
8731         });
8732 
8733         window.finesse = window.finesse || {};
8734         window.finesse.restservices = window.finesse.restservices || {};
8735         window.finesse.restservices.DialogBase = DialogBase;
8736 
8737 
8738         return DialogBase;
8739     });
8740 
8741 /**
8742  * JavaScript representation of the Finesse Dialog object.
8743  *
8744  * @requires finesse.clientservices.ClientServices
8745  * @requires Class
8746  * @requires finesse.FinesseBase
8747  * @requires finesse.restservices.RestBase
8748  */
8749 
8750 /** @private */
8751 define('restservices/Dialog',[
8752     'restservices/DialogBase',
8753     'utilities/Utilities'
8754 ],
8755 function (DialogBase, Utilities) {
8756     var Dialog = DialogBase.extend(/** @lends finesse.restservices.Dialog.prototype */{
8757 
8758         /**
8759          * @class
8760          * A Dialog is an attempted connection between or among multiple participants,
8761          * for example, a regular phone call, a conference, or a silent monitor session.
8762          * 
8763          * @augments finesse.restservices.DialogBase
8764          * @constructs
8765          */
8766         _fakeConstuctor: function () {
8767             /* This is here to hide the real init constructor from the public docs */
8768         },
8769         
8770         /**
8771          * @private
8772          *
8773          * @param {Object} options
8774          *     An object with the following properties:<ul>
8775          *         <li><b>id:</b> The id of the object being constructed</li>
8776          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
8777          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
8778          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
8779          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
8780          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
8781          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
8782          *             <li><b>content:</b> {String} Raw string of response</li>
8783          *             <li><b>object:</b> {Object} Parsed object of response</li>
8784          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
8785          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
8786          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
8787          *             </ul></li>
8788          *         </ul></li>
8789          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
8790          **/
8791         init: function (options) {
8792             this._super(options);
8793         },
8794 
8795         /**
8796          * @private
8797          * Gets the REST class for the current object - this is the Dialog class.
8798          * @returns {Object} The Dialog class.
8799          */
8800         getRestClass: function () {
8801             return Dialog;
8802         },
8803 
8804         /**
8805          * The requestId reaper timeout in ms
8806          */
8807         REQUESTID_REAPER_TIMEOUT: 5000,
8808 
8809         /**
8810          * Getter for the from address.
8811          * @returns {String} The from address.
8812          */
8813         getFromAddress: function () {
8814             this.isLoaded();
8815             return this.getData().fromAddress;
8816         },
8817 
8818         /**
8819          * Getter for the to address.
8820          * @returns {String} The to address.
8821          */
8822         getToAddress: function () {
8823             this.isLoaded();
8824             return this.getData().toAddress;
8825         },
8826         
8827         /**
8828          * Getter for the callback number without prefix.
8829          * This is required to schedule a callback if there is any dialer prefix added for direct_preview outbound calls
8830          * @returns {String} The callback number.undefined if callbackNumber is not available
8831          */
8832         getCallbackNumber: function () {
8833             this.isLoaded();
8834             return this.getData().callbackNumber;
8835         },
8836         
8837         /**
8838          * Getter for the secondaryId of a dialog.
8839          * A CONSULT call has two call legs (primary leg and a consult leg). 
8840          * As the CONSULT call is completed (either with TRANSFER or CONFERENCE), call legs would be merged. 
8841          * The surviving call's Dialog will contain the dropped call's Dialog Id in secondaryId field.
8842          * For CCE deployments, DIRECT_TRANSFER also have the secondaryId populated as mentioned above.  
8843          * @returns {String} The id of the secondary dialog.
8844          * @since   11.6(1)-ES1 onwards
8845          */
8846         getSecondaryId: function () {
8847             this.isLoaded();
8848             return this.getData().secondaryId;
8849         },
8850         
8851        /**
8852          * gets the participant timer counters 
8853          *
8854          * @param {String} participantExt Extension of participant.
8855          * @returns {Object} Array of Participants which contains properties :<ul>
8856          *     <li>state - The state of the Participant. 
8857          *     <li>startTime - The start Time of the Participant.
8858          *     <li>stateChangeTime - The time when participant state has changed.</ul>
8859          */
8860         getParticipantTimerCounters : function (participantExt) {
8861           var part, participantTimerCounters = {}, idx, participants;
8862           
8863           participants = this.getParticipants();
8864 
8865 
8866           //Loop through all the participants and find the right participant (based on participantExt)
8867           for(idx=0;idx<participants.length;idx=idx+1)
8868           {
8869             part = participants[idx];
8870             
8871             if (part.mediaAddress === participantExt)
8872             {
8873                 participantTimerCounters.startTime= part.startTime;
8874                 participantTimerCounters.stateChangeTime= part.stateChangeTime;
8875                 participantTimerCounters.state= part.state;
8876                 break;
8877             }
8878           }
8879           
8880           return participantTimerCounters;
8881         },
8882         
8883         /**
8884          * Determines the droppable participants.  A droppable participant is a participant that is an agent extension.   
8885          * (It is not a CTI Route Point, IVR Port, or the caller)
8886          * 
8887          * @param {String} filterExtension used to remove a single extension from the list
8888          * @returns {Object} Array of Participants that can be dropped.
8889          * Participant entity properties are as follows:<ul>
8890          *     <li>state - The state of the Participant. 
8891          *     <li>stateCause - The state cause of the Participant.
8892          *     <li>mediaAddress - The media address of the Participant.
8893          *     <li>startTime - The start Time of the Participant.
8894          *     <li>stateChangeTime - The time when participant state has changed.
8895          *     <li>actions - These are the actions that a Participant can perform</ul>
8896          */
8897         getDroppableParticipants: function (filterExtension) {
8898           this.isLoaded();
8899           var droppableParticipants = [], participants, index, idx, filterExtensionToRemove = "", callStateOk, part;
8900 
8901           participants = this.getParticipants();
8902 
8903           if (filterExtension)
8904           {
8905             filterExtensionToRemove = filterExtension;
8906           }
8907 
8908           //Loop through all the participants to remove non-agents & remove filterExtension
8909           //We could have removed filterExtension using splice, but we have to iterate through
8910           //the list anyway.
8911           for(idx=0;idx<participants.length;idx=idx+1)
8912           {
8913             part = participants[idx];
8914 
8915             //Skip the filterExtension
8916             if (part.mediaAddress !== filterExtensionToRemove)
8917             {
8918                 callStateOk = this._isParticipantStateDroppable(part);
8919 
8920                 //Remove non-agents & make sure callstate 
8921                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
8922                 {
8923                   droppableParticipants.push(part);
8924                 }
8925             }
8926         }
8927 
8928         return Utilities.getArray(droppableParticipants);
8929         },
8930 
8931         _isParticipantStateDroppable : function (part)
8932         {
8933           var isParticipantStateDroppable = false;
8934           if (part.state === Dialog.ParticipantStates.ACTIVE || part.state === Dialog.ParticipantStates.ACCEPTED || part.state === Dialog.ParticipantStates.HELD)
8935           {
8936             isParticipantStateDroppable = true;
8937           }
8938           
8939           return isParticipantStateDroppable;
8940         },
8941         
8942         /**
8943          * Is the participant droppable
8944          *
8945          * @param {String} participantExt Extension of participant.
8946          * @returns {Boolean} True is droppable.
8947          */
8948         isParticipantDroppable : function (participantExt) {
8949           var droppableParticipants = null, isDroppable = false, idx, part, callStateOk;
8950           
8951           droppableParticipants = this.getDroppableParticipants();
8952           
8953           if (droppableParticipants) 
8954           {
8955             for(idx=0;idx<droppableParticipants.length;idx=idx+1)
8956             {
8957               part = droppableParticipants[idx];
8958              
8959               if (part.mediaAddress === participantExt)
8960               {
8961                 callStateOk = this._isParticipantStateDroppable(part);
8962 
8963                 //Remove non-agents & make sure callstate 
8964                 if (callStateOk === true && part.mediaAddressType === this._agentDeviceType)
8965                 {
8966                   isDroppable = true;
8967                   break;
8968                 }
8969               }
8970             }
8971           }
8972           
8973           return isDroppable;
8974         },
8975 
8976         /**
8977          * Retrieves information about the currently scheduled callback, if any.
8978          * @returns {Object} If no callback has been set, will return undefined. If 
8979          * a callback has been set, it will return a map with one or more of the 
8980          * following entries, depending on what values have been set. 
8981          *    callbackTime   - the callback time, if it has been set.
8982          *    callbackNumber - the callback number, if it has been set.
8983          */
8984         getCallbackInfo: function() {
8985             this.isLoaded();
8986             return this.getData().scheduledCallbackInfo;
8987         },
8988 
8989         /**
8990          * Invoke a consult call out to a destination.
8991          *
8992          * @param {String} mediaAddress
8993          *     The media address of the user performing the consult call.
8994          * @param {String} toAddress
8995          *     The destination address of the consult call.
8996          * @param {finesse.interfaces.RequestHandlers} handlers
8997          *     An object containing the handlers for the request
8998          */
8999         makeConsultCall: function (mediaAddress, toAddress, handlers) {
9000             this.isLoaded();
9001             var contentBody = {};
9002             contentBody[this.getRestType()] = {
9003                 "targetMediaAddress": mediaAddress,
9004                 "toAddress": toAddress,
9005                 "requestedAction": Dialog.Actions.CONSULT_CALL
9006             };
9007             this._makeRequest(contentBody, handlers);
9008             return this; // Allow cascading
9009         },
9010         
9011         /**
9012          * Invoke a single step transfer request.
9013          *
9014          * @param {String} mediaAddress
9015          *     The media address of the user performing the single step transfer.
9016          * @param {String} toAddress
9017          *     The destination address of the single step transfer.
9018          * @param {finesse.interfaces.RequestHandlers} handlers
9019          *     An object containing the handlers for the request
9020          */
9021         initiateDirectTransfer: function (mediaAddress, toAddress, handlers) {
9022             this.isLoaded();
9023             var contentBody = {};
9024             contentBody[this.getRestType()] = {
9025                 "targetMediaAddress": mediaAddress,
9026                 "toAddress": toAddress,
9027                 "requestedAction": Dialog.Actions.TRANSFER_SST
9028             };
9029             this._makeRequest(contentBody, handlers);
9030             return this; // Allow cascading
9031         },
9032 
9033         /**
9034          * Update this dialog's wrap-up reason.
9035          *
9036          * @param {String} wrapUpReason
9037          *     The new wrap-up reason for this dialog
9038          * @param {finesse.interfaces.RequestHandlers} handlers
9039          *     An object containing the handlers for the request
9040          */
9041         updateWrapUpReason: function (wrapUpItems, options)
9042         {
9043 			this.isLoaded();
9044 			var mediaProperties = {};
9045 			if (window.finesse.container.Config.deploymentType === 'UCCX') {
9046 				mediaProperties = {
9047 					"wrapUpItems": {wrapUpItem: wrapUpItems}
9048 				} ;
9049 			} else {
9050 				mediaProperties = {
9051 					"wrapUpReason": wrapUpItems
9052 				 };
9053 			}
9054 
9055             options = options || {};
9056             options.content = {};
9057             options.content[this.getRestType()] =
9058             {
9059                 "mediaProperties": mediaProperties,
9060                 "requestedAction": Dialog.Actions.UPDATE_CALL_DATA
9061             };
9062             options.method = "PUT";
9063             this.restRequest(this.getRestUrl(), options);
9064 
9065             return this;
9066         },
9067 
9068         /**
9069          * Invoke a request to server based on the action given.
9070          * @param {String} mediaAddress
9071          *     The media address of the user performing the action.
9072          * @param {finesse.restservices.Dialog.Actions} action
9073          *     The action string indicating the action to invoke on dialog.
9074          * @param {finesse.interfaces.RequestHandlers} handlers
9075          *     An object containing the handlers for the request
9076          */
9077         requestAction: function (mediaAddress, action, handlers) {
9078             this.isLoaded();
9079             var contentBody = {};
9080             contentBody[this.getRestType()] = {
9081                 "targetMediaAddress": mediaAddress,
9082                 "requestedAction": action
9083             };
9084             this._makeRequest(contentBody, handlers);
9085             return this; // Allow cascading
9086         },
9087         
9088         /**
9089          * Wrapper around "requestAction" to request PARTICIPANT_DROP action.
9090          *
9091          * @param targetMediaAddress is the address to drop
9092          * @param {finesse.interfaces.RequestHandlers} handlers
9093          *     An object containing the handlers for the request
9094          */
9095         dropParticipant: function (targetMediaAddress, handlers) {
9096             this.requestAction(targetMediaAddress, Dialog.Actions.PARTICIPANT_DROP, handlers);
9097         },
9098         
9099         /**
9100          * Invoke a request to server to send DTMF digit tones.
9101          * @param {String} mediaAddress
9102          * @param {finesse.interfaces.RequestHandlers} handlers
9103          *     An object containing the handlers for the request
9104          * @param {String} digit
9105          *     The digit which causes invocation of an action on the dialog.
9106          */
9107         sendDTMFRequest: function (mediaAddress, handlers, digit) {
9108             this.isLoaded();
9109             var contentBody = {};
9110             contentBody[this.getRestType()] = {
9111                 "targetMediaAddress": mediaAddress,
9112                 "requestedAction": "SEND_DTMF",
9113                 "actionParams": {
9114                     "ActionParam": {
9115                         "name": "dtmfString",
9116                         "value": digit
9117                     }
9118                 }
9119             };
9120             this._makeRequest(contentBody, handlers);
9121             return this; // Allow cascading
9122         },
9123 
9124         /**
9125          * Invoke a request to server to set the time for a callback.
9126          * @param {String} mediaAddress
9127          * @param {String} callbackTime 
9128          *     The requested time for the callback, in YYYY-MM-DDTHH:MM format
9129          *     (ex: 2013-12-24T23:59)
9130          * @param {finesse.interfaces.RequestHandlers} handlers
9131          *     An object containing the handlers for the request
9132          */
9133         updateCallbackTime: function (mediaAddress, callbackTime, handlers) {
9134             this.isLoaded();
9135             var contentBody = {};
9136             contentBody[this.getRestType()] = {
9137                 "targetMediaAddress": mediaAddress,
9138                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
9139                 "actionParams": {
9140                     "ActionParam": {
9141                         "name": "callbackTime",
9142                         "value": callbackTime
9143                     }
9144                 }
9145             };
9146             this._makeRequest(contentBody, handlers);
9147             return this; // Allow cascading
9148         },
9149 
9150         /**
9151          * Invoke a request to server to set the number for a callback.
9152          * @param {String} mediaAddress
9153          * @param {String} callbackNumber
9154          *     The requested number to call for the callback
9155          * @param {finesse.interfaces.RequestHandlers} handlers
9156          *     An object containing the handlers for the request
9157          */
9158         updateCallbackNumber: function (mediaAddress, callbackNumber, handlers) {
9159             this.isLoaded();
9160             var contentBody = {};
9161             contentBody[this.getRestType()] = {
9162                 "targetMediaAddress": mediaAddress,
9163                 "requestedAction": Dialog.Actions.UPDATE_SCHEDULED_CALLBACK,
9164                 "actionParams": {
9165                     "ActionParam": {
9166                         "name": "callbackNumber",
9167                         "value": callbackNumber
9168                     }
9169                 }
9170             };
9171             this._makeRequest(contentBody, handlers);
9172             return this; // Allow cascading
9173         },
9174 
9175         /**
9176          * Invoke a request to server to cancel a callback.
9177          * @param {String} mediaAddress
9178          * @param {finesse.interfaces.RequestHandlers} handlers
9179          *     An object containing the handlers for the request
9180          */
9181         cancelCallback: function (mediaAddress, handlers) {
9182             this.isLoaded();
9183             var contentBody = {};
9184             contentBody[this.getRestType()] = {
9185                 "targetMediaAddress": mediaAddress,
9186                 "requestedAction": Dialog.Actions.CANCEL_SCHEDULED_CALLBACK
9187             };
9188             this._makeRequest(contentBody, handlers);
9189             return this; // Allow cascading
9190         },
9191 
9192         /**
9193          * Invoke a request to server to reclassify the call type.
9194          * @param {String} mediaAddress
9195          *     The media address of the user performing the consult call.
9196          * @param {String} classification
9197          *     The classification to assign to the call. Valid values are "VOICE", "FAX",
9198          *     "ANS_MACHINE", "INVALID", "BUSY" (CCX only), and "DO_NOT_CALL".
9199          * @param {finesse.interfaces.RequestHandlers} handlers
9200          *     An object containing the handlers for the request
9201          */
9202         reclassifyCall: function (mediaAddress, classification, handlers) {
9203             this.isLoaded();
9204             var contentBody = {};
9205             contentBody[this.getRestType()] = {
9206                 "targetMediaAddress": mediaAddress,
9207                 "requestedAction": Dialog.Actions.RECLASSIFY,
9208                 "actionParams": {
9209                     "ActionParam": {
9210                         "name": "outboundClassification",
9211                         "value": classification
9212                     }
9213                 }
9214             };
9215             this._makeRequest(contentBody, handlers);
9216             return this; // Allow cascading
9217         },
9218 
9219         /**
9220          * Utility method to create a closure containing the requestId and the Dialogs object so 
9221          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
9222          * @param  {String} requestId The requestId of the event
9223          * @return {Function}           The function to be executed by setTimeout
9224          */
9225         _createRequestIdReaper: function (requestId) {
9226             var that = this;
9227             return function () {
9228                 that._logger.log("Dialog: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
9229                 delete that._pendingCallbacks[requestId];
9230             };
9231         },
9232 
9233         /**
9234          * Overriding implementation of the one in RestBase.js
9235          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
9236          * @param  {String} requestId The requestId of the event
9237          */
9238         _postProcessUpdateStrategy: function (requestId) {
9239             this._logger.log("Dialog: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
9240             var callbacksObj = this._pendingCallbacks[requestId];
9241             if (callbacksObj && !callbacksObj.used) {
9242                 this._logger.log("Dialog: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
9243                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
9244                 callbacksObj.used = true;
9245             }            
9246         }
9247 
9248     });
9249 
9250     Dialog.Actions = /** @lends finesse.restservices.Dialog.Actions.prototype */ {
9251             /**
9252              * Drops the Participant from the Dialog.
9253              */
9254             DROP: "DROP",
9255             /**
9256              * Answers a Dialog.
9257              */
9258             ANSWER: "ANSWER",
9259             /**
9260              * Holds the Dialog.
9261              */
9262             HOLD: "HOLD",
9263             /**
9264              * Barges into a Call Dialog.
9265              */
9266             BARGE_CALL: "BARGE_CALL",
9267             /**
9268              * Allow as Supervisor to Drop a Participant from the Dialog.
9269              */
9270             PARTICIPANT_DROP: "PARTICIPANT_DROP",
9271             /**
9272              * Makes a new Call Dialog.
9273              */
9274             MAKE_CALL: "MAKE_CALL",
9275             /**
9276              * Retrieves a Dialog that is on Hold.
9277              */
9278             RETRIEVE: "RETRIEVE",
9279             /**
9280              * Sets the time or number for a callback. Can be
9281              * either a new callback, or updating an existing one.
9282              */
9283             UPDATE_SCHEDULED_CALLBACK: "UPDATE_SCHEDULED_CALLBACK",
9284             /**
9285              * Cancels a callback.
9286              */
9287             CANCEL_SCHEDULED_CALLBACK: "CANCEL_SCHEDULED_CALLBACK",
9288             /**
9289              * Initiates a Consult Call.
9290              */
9291             CONSULT_CALL: "CONSULT_CALL",
9292             /**
9293              * Initiates a Transfer of a Dialog.
9294              */
9295             TRANSFER: "TRANSFER",
9296             /**
9297              * Initiates a Single-Step Transfer of a Dialog.
9298              */
9299             TRANSFER_SST: "TRANSFER_SST",
9300             /**
9301              * Initiates a Conference of a Dialog.
9302              */
9303             CONFERENCE: "CONFERENCE",
9304             /**
9305              * Changes classification for a call
9306              */
9307             RECLASSIFY: "RECLASSIFY", 
9308             /**
9309              * Updates data on a Call Dialog.
9310              */
9311             UPDATE_CALL_DATA: "UPDATE_CALL_DATA",
9312             /**
9313              * Initiates a Recording on a Call Dialog.
9314              */
9315             START_RECORDING : "START_RECORDING",
9316             /**
9317              * Sends DTMF (dialed digits) to a Call Dialog.
9318              */
9319             DTMF : "SEND_DTMF",            
9320             /**
9321              * Accepts a Dialog that is being Previewed.
9322              */
9323             ACCEPT: "ACCEPT",
9324             /**
9325              * Rejects a Dialog.
9326              */
9327             REJECT: "REJECT",
9328             /**
9329              * Closes a Dialog.
9330              */
9331             CLOSE : "CLOSE",
9332             /**
9333              * @class Set of action constants for a Dialog.  These should be used for
9334              * {@link finesse.restservices.Dialog#requestAction}.
9335              * @constructs
9336              */
9337             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
9338         };
9339 
9340     Dialog.States = /** @lends finesse.restservices.Dialog.States.prototype */ {
9341        /**
9342          * Indicates that the call is ringing at a device.
9343          */
9344         ALERTING: "ALERTING",
9345         /**
9346          * Indicates that the phone is off the hook at a device.
9347          */
9348         INITIATING: "INITIATING",
9349         /**
9350          * Indicates that the dialog has a least one active participant.
9351          */
9352         ACTIVE: "ACTIVE",
9353         /**
9354          * Indicates that the dialog has no active participants.
9355          */
9356         DROPPED: "DROPPED",
9357         /**
9358          * Indicates that the phone is dialing at the device.
9359          */
9360         INITIATED: "INITIATED",
9361         /**
9362          * Indicates that the dialog has failed.
9363          * @see Dialog.ReasonStates
9364          */
9365         FAILED: "FAILED",
9366         /**
9367          * Indicates that the user has accepted an OUTBOUND_PREVIEW dialog.
9368          */
9369         ACCEPTED: "ACCEPTED",
9370         /**
9371          * @class Possible Dialog State constants.
9372          * The State flow of a typical in-bound Dialog is as follows: INITIATING, INITIATED, ALERTING, ACTIVE, DROPPED.
9373          * @constructs
9374          */
9375         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
9376     };
9377 
9378     Dialog.ParticipantStates = /** @lends finesse.restservices.Dialog.ParticipantStates.prototype */ {
9379         /**
9380           * Indicates that an incoming call is ringing on the device.
9381           */
9382          ALERTING: "ALERTING",
9383          /**
9384           * Indicates that an outgoing call, not yet active, exists on the device.
9385           */
9386          INITIATING: "INITIATING",
9387          /**
9388           * Indicates that the participant is active on the call.
9389           */
9390          ACTIVE: "ACTIVE",
9391          /**
9392           * Indicates that the participant has dropped from the call.
9393           */
9394          DROPPED: "DROPPED",
9395          /**
9396           * Indicates that the participant has held their connection to the call.
9397           */
9398          HELD: "HELD",
9399          /**
9400           * Indicates that the phone is dialing at a device.
9401           */
9402          INITIATED: "INITIATED",
9403          /**
9404           * Indicates that the call failed.
9405           * @see Dialog.ReasonStates
9406           */
9407          FAILED: "FAILED",
9408          /**
9409           * Indicates that the participant is not in active state on the call, but is wrapping up after the participant has dropped from the call.
9410           */
9411          WRAP_UP: "WRAP_UP",
9412          /**
9413           * Indicates that the participant has accepted the dialog.  This state is applicable to OUTBOUND_PREVIEW dialogs.
9414           */
9415          ACCEPTED: "ACCEPTED",
9416          /**
9417           * @class Possible Dialog Participant State constants.
9418           * @constructs
9419           */
9420          _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
9421      };
9422 
9423     Dialog.ReasonStates = /** @lends finesse.restservices.Dialog.ReasonStates.prototype */ {
9424        /**
9425         * Dialog was Busy.  This will typically be for a Failed Dialog.
9426         */
9427         BUSY: "BUSY",
9428         /**
9429          * Dialog reached a Bad Destination.  This will typically be for a Failed Dialog.
9430          */
9431         BAD_DESTINATION: "BAD_DESTINATION",
9432         /**
9433          * All Other Reasons.  This will typically be for a Failed Dialog.
9434          */
9435         OTHER: "OTHER",
9436         /**
9437          * The Device Resource for the Dialog was not available.
9438          */
9439         DEVICE_RESOURCE_NOT_AVAILABLE : "DEVICE_RESOURCE_NOT_AVAILABLE",
9440         /**
9441          * @class Possible dialog state reasons code constants.
9442              * @constructs
9443              */
9444             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
9445     };
9446 
9447     window.finesse = window.finesse || {};
9448     window.finesse.restservices = window.finesse.restservices || {};
9449     window.finesse.restservices.Dialog = Dialog;
9450     
9451     
9452     return Dialog;
9453 });
9454 
9455 /**
9456  * JavaScript representation of the Finesse Dialogs collection
9457  * object which contains a list of Dialog objects.
9458  *
9459  * @requires finesse.clientservices.ClientServices
9460  * @requires Class
9461  * @requires finesse.FinesseBase
9462  * @requires finesse.restservices.RestBase
9463  * @requires finesse.restservices.Dialog
9464  */
9465 /** @private */
9466 define('restservices/Dialogs',[
9467     'restservices/RestCollectionBase',
9468     'restservices/Dialog'
9469 ],
9470 function (RestCollectionBase, Dialog) {
9471     var Dialogs = RestCollectionBase.extend(/** @lends finesse.restservices.Dialogs.prototype */{
9472 
9473         /**
9474          * @class
9475          * JavaScript representation of a Dialogs collection object. Also exposes
9476          * methods to operate on the object against the server.
9477          * @augments finesse.restservices.RestCollectionBase
9478          * @constructs
9479          * @see finesse.restservices.Dialog
9480          * @example
9481          *  _dialogs = _user.getDialogs( {
9482          *      onCollectionAdd : _handleDialogAdd,
9483          *      onCollectionDelete : _handleDialogDelete,
9484          *      onLoad : _handleDialogsLoaded
9485          *  });
9486          *  
9487          * _dialogCollection = _dialogs.getCollection();
9488          * for (var dialogId in _dialogCollection) {
9489          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
9490          *         _dialog = _dialogCollection[dialogId];
9491          *         etc...
9492          *     }
9493          * }
9494          */
9495         _fakeConstuctor: function () {
9496             /* This is here to hide the real init constructor from the public docs */
9497         },
9498         
9499         /**
9500          * @private
9501          * @param {Object} options
9502          *     An object with the following properties:<ul>
9503          *         <li><b>id:</b> The id of the object being constructed</li>
9504          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9505          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9506          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9507          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9508          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9509          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9510          *             <li><b>content:</b> {String} Raw string of response</li>
9511          *             <li><b>object:</b> {Object} Parsed object of response</li>
9512          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9513          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9514          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9515          *             </ul></li>
9516          *         </ul></li>
9517          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9518          **/
9519         init: function (options) {
9520             this._super(options);
9521         },
9522 
9523         /**
9524          * @private
9525          * Gets the REST class for the current object - this is the Dialogs class.
9526          */
9527         getRestClass: function () {
9528             return Dialogs;
9529         },
9530 
9531         /**
9532          * @private
9533          * Gets the REST class for the objects that make up the collection. - this
9534          * is the Dialog class.
9535          */
9536         getRestItemClass: function () {
9537             return Dialog;
9538         },
9539 
9540         /**
9541          * @private
9542          * Gets the REST type for the current object - this is a "Dialogs".
9543          */
9544         getRestType: function () {
9545             return "Dialogs";
9546         },
9547 
9548         /**
9549          * @private
9550          * Gets the REST type for the objects that make up the collection - this is "Dialogs".
9551          */
9552         getRestItemType: function () {
9553             return "Dialog";
9554         },
9555 
9556         /**
9557          * @private
9558          * Override default to indicates that the collection doesn't support making
9559          * requests.
9560          */
9561         supportsRequests: true,
9562 
9563         /**
9564          * @private
9565          * Override default to indicates that the collection subscribes to its objects.
9566          */
9567         supportsRestItemSubscriptions: true,
9568 
9569         /**
9570          * The requestId reaper timeout in ms
9571          */
9572         REQUESTID_REAPER_TIMEOUT: 5000,
9573 
9574         /**
9575          * @private
9576          * Create a new Dialog in this collection
9577          *
9578          * @param {String} toAddress
9579          *     The to address of the new Dialog
9580          * @param {String} fromAddress
9581          *     The from address of the new Dialog
9582          * @param {finesse.interfaces.RequestHandlers} handlers
9583          *     An object containing the (optional) handlers for the request.
9584          * @return {finesse.restservices.Dialogs}
9585          *     This Dialogs object, to allow cascading.
9586          */
9587         createNewCallDialog: function (toAddress, fromAddress, handlers)
9588         {
9589             var contentBody = {};
9590             contentBody[this.getRestItemType()] = {
9591                 "requestedAction": "MAKE_CALL",
9592                 "toAddress": toAddress,
9593                 "fromAddress": fromAddress
9594             };
9595 
9596             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9597             handlers = handlers || {};
9598 
9599             this.restRequest(this.getRestUrl(), {
9600                 method: 'POST',
9601                 success: handlers.success,
9602                 error: handlers.error,
9603                 content: contentBody
9604             });
9605             return this; // Allow cascading
9606         },
9607 
9608         /**
9609          * @private
9610          * Create a new Dialog in this collection as a result of a requested action
9611          *
9612          * @param {String} toAddress
9613          *     The to address of the new Dialog
9614          * @param {String} fromAddress
9615          *     The from address of the new Dialog
9616          * @param {finesse.restservices.Dialog.Actions} actionType
9617          *     The associated action to request for creating this new dialog
9618          * @param {finesse.interfaces.RequestHandlers} handlers
9619          *     An object containing the (optional) handlers for the request.
9620          * @return {finesse.restservices.Dialogs}
9621          *     This Dialogs object, to allow cascading.
9622          */
9623         createNewSuperviseCallDialog: function (toAddress, fromAddress, actionType, handlers)
9624         {
9625             var contentBody = {};
9626             this._isLoaded = true;
9627 
9628             contentBody[this.getRestItemType()] = {
9629                 "requestedAction": actionType,
9630                 "toAddress": toAddress,
9631                 "fromAddress": fromAddress
9632             };
9633 
9634             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
9635             handlers = handlers || {};
9636 
9637             this.restRequest(this.getRestUrl(), {
9638                 method: 'POST',
9639                 success: handlers.success,
9640                 error: handlers.error,
9641                 content: contentBody
9642             });
9643             return this; // Allow cascading
9644         },
9645         
9646         /**
9647          * @private
9648          * Create a new Dialog in this collection as a result of a requested action
9649          * @param {String} fromAddress
9650          *     The from address of the new Dialog
9651          * @param {String} toAddress
9652          *     The to address of the new Dialog
9653          * @param {finesse.restservices.Dialog.Actions} actionType
9654          *     The associated action to request for creating this new dialog
9655          * @param {String} dialogUri
9656          *     The associated uri of SUPERVISOR_MONITOR call
9657          * @param {finesse.interfaces.RequestHandlers} handlers
9658          *     An object containing the (optional) handlers for the request.
9659          * @return {finesse.restservices.Dialogs}
9660          *     This Dialogs object, to allow cascading.
9661          */
9662         createNewBargeCall: function (fromAddress, toAddress, actionType, dialogURI, handlers) {
9663             this.isLoaded();
9664          
9665             var contentBody = {};
9666             contentBody[this.getRestItemType()] = {
9667                 "fromAddress": fromAddress,
9668                 "toAddress": toAddress,
9669                 "requestedAction": actionType,
9670                 "associatedDialogUri": dialogURI
9671                 
9672             };
9673             // (nonexistent) keys to be read as undefined
9674             handlers = handlers || {};  
9675             this.restRequest(this.getRestUrl(), {
9676                 method: 'POST',
9677                 success: handlers.success,
9678                 error: handlers.error,
9679                 content: contentBody
9680             });
9681             return this; // Allow cascading
9682         },
9683 
9684         /**
9685          * Utility method to get the number of dialogs in this collection.
9686          * 'excludeSilentMonitor' flag is provided as an option to exclude calls with type
9687          * 'SUPERVISOR_MONITOR' from the count.
9688          * @param  {Boolean} excludeSilentMonitor If true, calls with type of 'SUPERVISOR_MONITOR' will be excluded from the count.
9689          * @return {Number} The number of dialogs in this collection.
9690          */
9691         getDialogCount: function (excludeSilentMonitor) {
9692             this.isLoaded();
9693 
9694             var dialogId, count = 0;
9695             if (excludeSilentMonitor) {
9696                 for (dialogId in this._collection) {
9697                     if (this._collection.hasOwnProperty(dialogId)) {
9698                         if (this._collection[dialogId].getCallType() !== 'SUPERVISOR_MONITOR') {
9699                             count += 1;
9700                         }
9701                     }
9702                 }
9703 
9704                 return count;
9705             } else {
9706                 return this.length;
9707             }        
9708         },
9709 
9710         /**
9711          * Utility method to create a closure containing the requestId and the Dialogs object so 
9712          * that the _pendingCallbacks map can be manipulated when the timer task is executed.
9713          * @param  {String} requestId The requestId of the event
9714          * @return {Function}           The function to be executed by setTimeout
9715          */
9716         _createRequestIdReaper: function (requestId) {
9717             var that = this;
9718             return function () {
9719                 that._logger.log("Dialogs: clearing the requestId-to-callbacks mapping for requestId=" + requestId);
9720                 delete that._pendingCallbacks[requestId];
9721             };
9722         },
9723 
9724         /**
9725          * Overriding implementation of the one in RestBase.js
9726          * This determines the strategy that Dialogs will take after processing an event that contains a requestId.
9727          * @param  {String} requestId The requestId of the event
9728          */
9729         _postProcessUpdateStrategy: function (requestId) {
9730             this._logger.log("Dialogs: determining whether to set timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
9731             var callbacksObj = this._pendingCallbacks[requestId];
9732             if (callbacksObj && !callbacksObj.used) {
9733                 this._logger.log("Dialogs: setting timeout for clearing requestId-to-callbacks mapping requestId=" + requestId);
9734                 setTimeout(this._createRequestIdReaper(requestId), this.REQUESTID_REAPER_TIMEOUT);
9735                 callbacksObj.used = true;
9736             }            
9737         }
9738 
9739     });
9740     
9741     window.finesse = window.finesse || {};
9742     window.finesse.restservices = window.finesse.restservices || {};
9743     window.finesse.restservices.Dialogs = Dialogs;
9744     
9745     return Dialogs;
9746 });
9747 
9748 /**
9749  * JavaScript representation of the Finesse ClientLog object
9750  *
9751  * @requires finesse.clientservices.ClientServices
9752  * @requires Class
9753  * @requires finesse.FinesseBase
9754  * @requires finesse.restservices.RestBase
9755  */
9756 
9757 /** The following comment is to prevent jslint errors about 
9758  * using variables before they are defined.
9759  */
9760 /** @private */
9761 /*global finesse*/
9762 
9763 define('restservices/ClientLog',["restservices/RestBase"], function (RestBase) {
9764     
9765     var ClientLog = RestBase.extend(/** @lends finesse.restservices.ClientLog.prototype */{    
9766         /**
9767          * @private
9768          * Returns whether this object supports transport logs
9769          */
9770         doNotLog : true,
9771         
9772         explicitSubscription : true,
9773         
9774         /**
9775          * @class
9776          * @private
9777          * JavaScript representation of a ClientLog object. Also exposes methods to operate
9778          * on the object against the server.
9779          *
9780          * @param {Object} options
9781          *     An object with the following properties:<ul>
9782          *         <li><b>id:</b> The id of the object being constructed</li>
9783          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
9784          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
9785          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
9786          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
9787          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
9788          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9789          *             <li><b>content:</b> {String} Raw string of response</li>
9790          *             <li><b>object:</b> {Object} Parsed object of response</li>
9791          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9792          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9793          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9794          *             </ul></li>
9795          *         </ul></li>
9796          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
9797          * @constructs
9798          * @augments finesse.restservices.RestBase
9799          **/
9800         init: function (options) {
9801             this._super({
9802                 id: "", 
9803                 data: {clientLog : null},
9804                 onAdd: options.onAdd,
9805                 onChange: options.onChange,
9806                 onLoad: options.onLoad,
9807                 onError: options.onError,
9808                 parentObj: options.parentObj
9809                 });
9810         },
9811 
9812         /**
9813          * @private
9814          * Gets the REST class for the current object - this is the ClientLog object.
9815          */
9816         getRestClass: function () {
9817             return ClientLog;
9818         },
9819 
9820         /**
9821          * @private
9822          * Gets the REST type for the current object - this is a "ClientLog".
9823          */
9824         getRestType: function ()
9825         {
9826             return "ClientLog";
9827         },
9828         
9829         /**
9830          * @private
9831          * Gets the node path for the current object
9832          * @returns {String} The node path
9833          */
9834         getXMPPNodePath: function () {
9835             return this.getRestUrl();
9836         },
9837 
9838         /**
9839          * @private
9840          * Utility method to fetch this object from the server, however we
9841          * override it for ClientLog to not do anything because GET is not supported
9842          * for ClientLog object.
9843          */
9844         _doGET: function (handlers) {
9845             return;
9846         },
9847            
9848         /**
9849          * @private
9850          * Invoke a request to the server given a content body and handlers.
9851          *
9852          * @param {Object} contentBody
9853          *     A JS object containing the body of the action request.
9854          * @param {Object} handlers
9855          *     An object containing the following (optional) handlers for the request:<ul>
9856          *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
9857          *         response object as its only parameter:<ul>
9858          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9859          *             <li><b>content:</b> {String} Raw string of response</li>
9860          *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
9861          *         <li>A error callback function for an unsuccessful request to be invoked with the
9862          *         error response object as its only parameter:<ul>
9863          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
9864          *             <li><b>content:</b> {String} Raw string of response</li>
9865          *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
9866          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
9867          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
9868          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
9869          *             </ul></li>
9870          *         </ul>
9871          */
9872         sendLogs: function (contentBody, handlers) {
9873             // Protect against null dereferencing of options allowing its
9874             // (nonexistent) keys to be read as undefined
9875             handlers = handlers || {};
9876 
9877             this.restRequest(this.getRestUrl(), {
9878                 method: 'POST',
9879                 //success: handlers.success,
9880                 error: handlers.error,
9881                 content: contentBody
9882             });
9883         }
9884     });
9885     
9886     window.finesse = window.finesse || {};
9887     window.finesse.restservices = window.finesse.restservices || {};
9888     window.finesse.restservices.ClientLog = ClientLog;
9889     
9890     return ClientLog;
9891 });
9892 
9893 /**
9894  * JavaScript representation of the Finesse Queue object
9895  * @requires finesse.clientservices.ClientServices
9896  * @requires Class
9897  * @requires finesse.FinesseBase
9898  * @requires finesse.restservices.RestBase
9899  */
9900 
9901 /** @private */
9902 define('restservices/Queue',[
9903     'restservices/RestBase',
9904     'utilities/Utilities'
9905 ],
9906 function (RestBase, Utilities) {
9907     var Queue = RestBase.extend(/** @lends finesse.restservices.Queue.prototype */{
9908 
9909         /**
9910          * @class
9911          * A Queue is a list of Contacts available to a User for quick dial.
9912          * 
9913          * @augments finesse.restservices.RestBase
9914          * @constructs
9915          */
9916         _fakeConstuctor: function () {
9917             /* This is here to hide the real init constructor from the public docs */
9918         },
9919         
9920 		/**
9921 		 * @private
9922 		 * JavaScript representation of a Queue object. Also exposes methods to operate
9923 		 * on the object against the server.
9924 		 *
9925 		 * @constructor
9926 		 * @param {String} id
9927 		 *     Not required...
9928 		 * @param {Object} callbacks
9929 		 *     An object containing callbacks for instantiation and runtime
9930 		 * @param {Function} callbacks.onLoad(this)
9931 		 *     Callback to invoke upon successful instantiation
9932 		 * @param {Function} callbacks.onLoadError(rsp)
9933 		 *     Callback to invoke on instantiation REST request error
9934 		 *     as passed by finesse.clientservices.ClientServices.ajax()
9935 		 *     {
9936 		 *         status: {Number} The HTTP status code returned
9937 		 *         content: {String} Raw string of response
9938 		 *         object: {Object} Parsed object of response
9939 		 *         error: {Object} Wrapped exception that was caught
9940 		 *         error.errorType: {String} Type of error that was caught
9941 		 *         error.errorMessage: {String} Message associated with error
9942 		 *     }
9943 		 * @param {Function} callbacks.onChange(this)
9944 		 *     Callback to invoke upon successful update
9945 		 * @param {Function} callbacks.onError(rsp)
9946 		 *     Callback to invoke on update error (refresh or event)
9947 		 *     as passed by finesse.clientservices.ClientServices.ajax()
9948 		 *     {
9949 		 *         status: {Number} The HTTP status code returned
9950 		 *         content: {String} Raw string of response
9951 		 *         object: {Object} Parsed object of response
9952 		 *         error: {Object} Wrapped exception that was caught
9953 		 *         error.errorType: {String} Type of error that was caught
9954 		 *         error.errorMessage: {String} Message associated with error
9955 		 *     }
9956 		 *  
9957 		 */
9958         init: function (id, callbacks, restObj) {
9959             this._super(id, callbacks, restObj);
9960         },
9961 
9962         /**
9963          * @private
9964          * Gets the REST class for the current object - this is the Queue object.
9965          */
9966         getRestClass: function () {
9967             return Queue;
9968         },
9969 
9970         /**
9971          * @private
9972          * Gets the REST type for the current object - this is a "Queue".
9973          */
9974         getRestType: function () {
9975             return "Queue";
9976         },
9977 
9978         /**
9979          * @private
9980          * Returns whether this object supports subscriptions
9981          */
9982         supportsSubscriptions: function () {
9983             return true;
9984         },
9985         
9986         /**
9987          * @private
9988          * Specifies whether this object's subscriptions need to be explicitly requested
9989          */
9990         explicitSubscription: true,
9991         
9992         /**
9993          * @private
9994          * Gets the node path for the current object - this is the team Users node
9995          * @returns {String} The node path
9996          */
9997         getXMPPNodePath: function () {
9998             return this.getRestUrl();
9999         },
10000         
10001         /**
10002          * Getter for the queue id
10003          * @returns {String}
10004          *     The id of the Queue
10005          */
10006         getId: function () {
10007             this.isLoaded();
10008             return this._id;
10009         },
10010         
10011         /**
10012          * Getter for the queue name
10013          * @returns {String}
10014          *      The name of the Queue
10015          */
10016         getName: function () {
10017             this.isLoaded();
10018             return this.getData().name;
10019         },
10020         
10021         /**
10022          * Getter for the queue statistics.
10023          * Supported statistics include:<br>
10024          *  - agentsBusyOther<br>
10025          *  - agentsLoggedOn<br>
10026          *  - agentsNotReady<br>
10027          *  - agentsReady<br>
10028          *  - agentsTalkingInbound<br>
10029          *  - agentsTalkingInternal<br>
10030          *  - agentsTalkingOutbound<br>
10031          *  - agentsWrapUpNotReady<br>
10032          *  - agentsWrapUpReady<br>
10033          *  - callsInQueue<br>
10034          *  - startTimeOfLongestCallInQueue<br>
10035          *  <br>
10036          *  These statistics can be accessed via dot notation:<br>
10037          *  i.e.: getStatistics().callsInQueue
10038          * @returns {Object}
10039          *      The Object with different statistics as properties.
10040          */
10041         getStatistics: function () {
10042             this.isLoaded();
10043             return this.getData().statistics;       
10044         },
10045 
10046         /**
10047          * Parses a uriString to retrieve the id portion
10048          * @param {String} uriString
10049          * @return {String} id
10050          */
10051         _parseIdFromUriString : function (uriString) {
10052             return Utilities.getId(uriString);
10053         }
10054 
10055     });
10056 	
10057 	window.finesse = window.finesse || {};
10058     window.finesse.restservices = window.finesse.restservices || {};
10059     window.finesse.restservices.Queue = Queue;
10060     
10061     return Queue;
10062 });
10063 
10064 /**
10065  * JavaScript representation of the Finesse Queues collection
10066  * object which contains a list of Queue objects.
10067  * @requires finesse.clientservices.ClientServices
10068  * @requires Class
10069  * @requires finesse.FinesseBase
10070  * @requires finesse.restservices.RestBase
10071  * @requires finesse.restservices.RestCollectionBase
10072  */
10073 
10074 /**
10075  * @class
10076  * JavaScript representation of a Queues collection object.
10077  *
10078  * @constructor
10079  * @borrows finesse.restservices.RestCollectionBase as finesse.restservices.Queues
10080  */
10081 
10082 /** @private */
10083 define('restservices/Queues',[
10084     'restservices/RestCollectionBase',
10085     'restservices/Queue'
10086 ],
10087 function (RestCollectionBase, Queue) {
10088     var Queues = RestCollectionBase.extend(/** @lends finesse.restservices.Queues.prototype */{
10089 
10090         /**
10091          * @class
10092          * JavaScript representation of a Queues collection object. 
10093          * @augments finesse.restservices.RestCollectionBase
10094          * @constructs
10095          * @see finesse.restservices.Queue
10096          * @example
10097          *  _queues = _user.getQueues( {
10098          *      onCollectionAdd : _handleQueueAdd,
10099          *      onCollectionDelete : _handleQueueDelete,
10100          *      onLoad : _handleQueuesLoaded
10101          *  });
10102          *  
10103          * _queueCollection = _queues.getCollection();
10104          * for (var queueId in _queueCollection) {
10105          *     if (_queueCollection.hasOwnProperty(queueId)) {
10106          *         _queue = _queueCollection[queueId];
10107          *         etc...
10108          *     }
10109          * }
10110          */
10111         _fakeConstuctor: function () {
10112             /* This is here to hide the real init constructor from the public docs */
10113         },
10114 	    
10115          /**
10116          * @private
10117          * JavaScript representation of a Queues object. Also exposes
10118          * methods to operate on the object against the server.
10119          *
10120          * @param {Object} options
10121          *     An object with the following properties:<ul>
10122          *         <li><b>id:</b> The id of the object being constructed</li>
10123          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10124          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10125          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10126          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10127          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10128          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10129          *             <li><b>content:</b> {String} Raw string of response</li>
10130          *             <li><b>object:</b> {Object} Parsed object of response</li>
10131          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10132          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10133          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10134          *             </ul></li>
10135          *         </ul></li>
10136          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10137          **/
10138         init: function (options) {
10139             this._super(options);           
10140         },
10141 
10142         /**
10143          * @private
10144          * Gets xmpp node path.
10145          */
10146         getXMPPNodePath: function () {
10147             return this.getRestUrl();
10148         },
10149 
10150         /**
10151          * @private
10152          * Gets the REST class for the current object - this is the Queues class.
10153          */
10154         getRestClass: function () {
10155             return Queues;
10156         },
10157 
10158         /**
10159          * @private
10160          * Gets the REST class for the objects that make up the collection. - this
10161          * is the Queue class.
10162          */
10163         getRestItemClass: function () {
10164             return Queue;
10165         },
10166 
10167         /**
10168          * @private
10169          * Gets the REST type for the current object - this is a "Queues".
10170          */
10171         getRestType: function () {
10172             return "Queues";
10173         },
10174         
10175         /**
10176          * @private
10177          * Gets the REST type for the objects that make up the collection - this is "Queue".
10178          */
10179         getRestItemType: function () {
10180             return "Queue";
10181         },
10182 
10183         explicitSubscription: true,
10184         
10185         handlesItemRefresh: true
10186     });
10187     
10188     window.finesse = window.finesse || {};
10189     window.finesse.restservices = window.finesse.restservices || {};
10190     window.finesse.restservices.Queues = Queues;
10191     
10192     return Queues;
10193 });
10194 
10195 /**
10196  * JavaScript representation of the Finesse WrapUpReason object.
10197  *
10198  * @requires finesse.clientservices.ClientServices
10199  * @requires Class
10200  * @requires finesse.FinesseBase
10201  * @requires finesse.restservices.RestBase
10202  */
10203 
10204 /** @private */
10205 define('restservices/WrapUpReason',['restservices/RestBase'], function (RestBase) {
10206 
10207     var WrapUpReason = RestBase.extend(/** @lends finesse.restservices.WrapUpReason.prototype */{
10208 
10209         /**
10210          * @class
10211          * A WrapUpReason is a code and description identifying a particular reason that a
10212          * User is in WORK (WrapUp) mode.
10213          * 
10214          * @augments finesse.restservices.RestBase
10215          * @see finesse.restservices.User
10216          * @see finesse.restservices.User.States#WORK
10217          * @constructs
10218          */
10219         _fakeConstuctor: function () {
10220             /* This is here to hide the real init constructor from the public docs */
10221         },
10222         
10223         /** 
10224          * @private
10225          * JavaScript representation of a WrapUpReason object. Also exposes
10226          * methods to operate on the object against the server.
10227          *
10228          * @param {Object} options
10229          *     An object with the following properties:<ul>
10230          *         <li><b>id:</b> The id of the object being constructed</li>
10231          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10232          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10233          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10234          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10235          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10236          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10237          *             <li><b>content:</b> {String} Raw string of response</li>
10238          *             <li><b>object:</b> {Object} Parsed object of response</li>
10239          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10240          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10241          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10242          *             </ul></li>
10243          *         </ul></li>
10244          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10245          **/
10246         init: function (options) {
10247             this._super(options);
10248         },
10249 
10250         /**
10251          * @private
10252          * Gets the REST class for the current object - this is the WrapUpReason class.
10253          * @returns {Object} The WrapUpReason class.
10254          */
10255         getRestClass: function () {
10256             return WrapUpReason;
10257         },
10258 
10259         /**
10260          * @private
10261          * Gets the REST type for the current object - this is a "WrapUpReason".
10262          * @returns {String} The WrapUpReason string.
10263          */
10264         getRestType: function () {
10265             return "WrapUpReason";
10266         },
10267 
10268         /**
10269          * @private
10270          * Gets the REST type for the current object - this is a "WrapUpReasons".
10271          * @returns {String} The WrapUpReasons string.
10272          */
10273         getParentRestType: function () {
10274             return "WrapUpReasons";
10275         },
10276 
10277         /**
10278          * @private
10279          * Override default to indicate that this object doesn't support making
10280          * requests.
10281          */
10282         supportsRequests: false,
10283 
10284         /**
10285          * @private
10286          * Override default to indicate that this object doesn't support subscriptions.
10287          */
10288         supportsSubscriptions: false,
10289 
10290         /**
10291          * Getter for the label.
10292          * @returns {String} The label.
10293          */
10294         getLabel: function () {
10295             this.isLoaded();
10296             return this.getData().label;
10297         },
10298 
10299         /**
10300          * @private
10301          * Getter for the forAll flag.
10302          * @returns {Boolean} True if global.
10303          */
10304         getForAll: function () {
10305             this.isLoaded();
10306             return this.getData().forAll;
10307         },
10308 
10309         /**
10310          * @private
10311          * Getter for the Uri value.
10312          * @returns {String} The Uri.
10313          */
10314         getUri: function () {
10315             this.isLoaded();
10316             return this.getData().uri;
10317         }
10318     });
10319 
10320     window.finesse = window.finesse || {};
10321     window.finesse.restservices = window.finesse.restservices || {};
10322     window.finesse.restservices.WrapUpReason = WrapUpReason;
10323         
10324     return WrapUpReason;
10325 });
10326 
10327 /**
10328 * JavaScript representation of the Finesse WrapUpReasons collection
10329 * object which contains a list of WrapUpReason objects.
10330  *
10331  * @requires finesse.clientservices.ClientServices
10332  * @requires Class
10333  * @requires finesse.FinesseBase
10334  * @requires finesse.restservices.RestBase
10335  * @requires finesse.restservices.Dialog
10336  * @requires finesse.restservices.RestCollectionBase
10337  */
10338 
10339 /** @private */
10340 define('restservices/WrapUpReasons',[
10341     'restservices/RestCollectionBase',
10342     'restservices/WrapUpReason'
10343 ],
10344 function (RestCollectionBase, WrapUpReason) {
10345 
10346     var WrapUpReasons = RestCollectionBase.extend(/** @lends finesse.restservices.WrapUpReasons.prototype */{
10347         
10348         /**
10349          * @class
10350          * JavaScript representation of a WrapUpReasons collection object. 
10351          * @augments finesse.restservices.RestCollectionBase
10352          * @constructs
10353          * @see finesse.restservices.WrapUpReason
10354          * @example
10355          *  _wrapUpReasons = _user.getWrapUpReasons ( {
10356          *      onCollectionAdd : _handleWrapUpReasonAdd,
10357          *      onCollectionDelete : _handleWrapUpReasonDelete,
10358          *      onLoad : _handleWrapUpReasonsLoaded
10359          *  });
10360          *  
10361          * _wrapUpReasonCollection = _wrapUpReasons.getCollection();
10362          * for (var wrapUpReasonId in _wrapUpReasonCollection) {
10363          *     if (_wrapUpReasonCollection.hasOwnProperty(wrapUpReasonId)) {
10364          *         _wrapUpReason = _wrapUpReasonCollection[wrapUpReasonId];
10365          *         etc...
10366          *     }
10367          * }
10368         */
10369         _fakeConstuctor: function () {
10370             /* This is here to hide the real init constructor from the public docs */
10371         },
10372         
10373         /** 
10374          * @private
10375          * JavaScript representation of a WrapUpReasons collection object. Also exposes
10376          * methods to operate on the object against the server.
10377          *
10378          * @param {Object} options
10379          *     An object with the following properties:<ul>
10380          *         <li><b>id:</b> The id of the object being constructed</li>
10381          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10382          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10383          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10384          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10385          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<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>
10389          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10390          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10391          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10392          *             </ul></li>
10393          *         </ul></li>
10394          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10395          **/
10396         init: function (options) {
10397             this._super(options);           
10398         },
10399 
10400         /**
10401          * @private
10402          * Gets the REST class for the current object - this is the WrapUpReasons class.
10403          */
10404         getRestClass: function () {
10405             return WrapUpReasons;
10406         },
10407 
10408         /**
10409          * @private
10410          * Gets the REST class for the objects that make up the collection. - this
10411          * is the WrapUpReason class.
10412          */
10413         getRestItemClass: function () {
10414             return WrapUpReason;
10415         },
10416 
10417         /**
10418          * @private
10419          * Gets the REST type for the current object - this is a "WrapUpReasons".
10420          */
10421         getRestType: function () {
10422             return "WrapUpReasons";
10423         },
10424         
10425         /**
10426          * @private
10427          * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
10428          */
10429         getRestItemType: function () {
10430             return "WrapUpReason";
10431         },
10432 
10433         /**
10434          * @private
10435          * Override default to indicates that the collection supports making
10436          * requests.
10437          */
10438         supportsRequests: true,
10439 
10440         /**
10441          * @private
10442          * Override default to indicate that this object doesn't support subscriptions.
10443          */
10444         supportsRestItemSubscriptions: false,
10445 
10446         /**
10447          * @private
10448          * Retrieve the Wrap-Up Reason Codes. This call will re-query the server and refresh the collection.
10449          *
10450          * @returns {finesse.restservices.WrapUpReasons}
10451          *     This ReadyReasonCodes object to allow cascading.
10452          */
10453         get: function () {
10454             // set loaded to false so it will rebuild the collection after the get
10455             this._loaded = false;
10456             // reset collection
10457             this._collection = {};
10458             // perform get
10459             this._synchronize();
10460             return this;
10461         }
10462         
10463     });
10464  
10465     window.finesse = window.finesse || {};
10466     window.finesse.restservices = window.finesse.restservices || {};
10467     window.finesse.restservices.WrapUpReasons = WrapUpReasons;
10468        
10469     return WrapUpReasons;
10470 });
10471 
10472 /**
10473  * JavaScript representation of the Finesse Contact object.
10474  * @requires finesse.clientservices.ClientServices
10475  * @requires Class
10476  * @requires finesse.FinesseBase
10477  * @requires finesse.restservices.RestBase
10478  */
10479 /** @private */
10480 define('restservices/Contact',['restservices/RestBase'], function (RestBase) {
10481 
10482     var Contact = RestBase.extend(/** @lends finesse.restservices.Contact.prototype */{
10483 
10484         /**
10485          * @class
10486          * A Contact is a single entry in a PhoneBook, consisting of a First and Last Name,
10487          * a Phone Number, and a Description.
10488          * 
10489          * @augments finesse.restservices.RestBase
10490          * @see finesse.restservices.PhoneBook
10491          * @constructs
10492          */
10493         _fakeConstuctor: function () {
10494             /* This is here to hide the real init constructor from the public docs */
10495         },
10496         
10497         /**
10498          * @private
10499          * @param {Object} options
10500          *     An object with the following properties:<ul>
10501          *         <li><b>id:</b> The id of the object being constructed</li>
10502          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10503          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10504          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10505          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10506          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10507          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10508          *             <li><b>content:</b> {String} Raw string of response</li>
10509          *             <li><b>object:</b> {Object} Parsed object of response</li>
10510          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10511          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10512          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10513          *             </ul></li>
10514          *         </ul></li>
10515          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10516          **/
10517         init: function (options) {
10518             this._super(options);
10519         },
10520 
10521         /**
10522          * @private
10523          * Gets the REST class for the current object - this is the Contact class.
10524          * @returns {Object} The Contact class.
10525          */
10526         getRestClass: function () {
10527             return Contact;
10528         },
10529 
10530         /**
10531          * @private
10532          * Gets the REST type for the current object - this is a "Contact".
10533          * @returns {String} The Contact string.
10534          */
10535         getRestType: function () {
10536             return "Contact";
10537         },
10538 
10539         /**
10540          * @private
10541          * Override default to indicate that this object doesn't support making
10542          * requests.
10543          */
10544         supportsRequests: false,
10545 
10546         /**
10547          * @private
10548          * Override default to indicate that this object doesn't support subscriptions.
10549          */
10550         supportsSubscriptions: false,
10551 
10552         /**
10553          * Getter for the firstName.
10554          * @returns {String} The firstName.
10555          */
10556         getFirstName: function () {
10557             this.isLoaded();
10558             return this.getData().firstName;
10559         },
10560 
10561         /**
10562          * Getter for the lastName.
10563          * @returns {String} The lastName.
10564          */
10565         getLastName: function () {
10566             this.isLoaded();
10567             return this.getData().lastName;
10568         },
10569 
10570         /**
10571          * Getter for the phoneNumber.
10572          * @returns {String} The phoneNumber.
10573          */
10574         getPhoneNumber: function () {
10575             this.isLoaded();
10576             return this.getData().phoneNumber;
10577         },
10578 
10579         /**
10580          * Getter for the description.
10581          * @returns {String} The description.
10582          */
10583         getDescription: function () {
10584             this.isLoaded();
10585             return this.getData().description;
10586         },
10587 
10588         /** @private */
10589         createPutSuccessHandler: function(contact, contentBody, successHandler){
10590             return function (rsp) {
10591                 // Update internal structure based on response. Here we
10592                 // inject the contentBody from the PUT request into the
10593                 // rsp.object element to mimic a GET as a way to take
10594                 // advantage of the existing _processResponse method.
10595                 rsp.object = contentBody;
10596                 contact._processResponse(rsp);
10597 
10598                 //Remove the injected Contact object before cascading response
10599                 rsp.object = {};
10600                 
10601                 //cascade response back to consumer's response handler
10602                 successHandler(rsp);
10603             };
10604         },
10605 
10606         /** @private */
10607         createPostSuccessHandler: function (contact, contentBody, successHandler) {
10608             return function (rsp) {
10609                 rsp.object = contentBody;
10610                 contact._processResponse(rsp);
10611 
10612                 //Remove the injected Contact object before cascading response
10613                 rsp.object = {};
10614 
10615                 //cascade response back to consumer's response handler
10616                 successHandler(rsp);
10617             };
10618         },
10619 
10620         /**
10621          * Add
10622          * @private
10623          */
10624         add: function (newValues, handlers) {
10625             // this.isLoaded();
10626             var contentBody = {};
10627 
10628             contentBody[this.getRestType()] = {
10629                 "firstName": newValues.firstName,
10630                 "lastName": newValues.lastName,
10631                 "phoneNumber": newValues.phoneNumber,
10632                 "description": newValues.description
10633             };
10634 
10635             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10636             handlers = handlers || {};
10637 
10638             this.restRequest(this.getRestUrl(), {
10639                 method: 'POST',
10640                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
10641                 error: handlers.error,
10642                 content: contentBody
10643             });
10644 
10645             return this; // Allow cascading
10646         },
10647 
10648         /**
10649          * Update
10650          * @private
10651          */
10652         update: function (newValues, handlers) {
10653             this.isLoaded();
10654             var contentBody = {};
10655 
10656             contentBody[this.getRestType()] = {
10657                 "uri": this.getId(),
10658                 "firstName": newValues.firstName,
10659                 "lastName": newValues.lastName,
10660                 "phoneNumber": newValues.phoneNumber,
10661                 "description": newValues.description
10662             };
10663 
10664             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10665             handlers = handlers || {};
10666 
10667             this.restRequest(this.getRestUrl(), {
10668                 method: 'PUT',
10669                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
10670                 error: handlers.error,
10671                 content: contentBody
10672             });
10673 
10674             return this; // Allow cascading
10675         },
10676 
10677 
10678         /**
10679          * Delete
10680          * @private
10681          */
10682         "delete": function ( handlers) {
10683             this.isLoaded();
10684 
10685             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
10686             handlers = handlers || {};
10687 
10688             this.restRequest(this.getRestUrl(), {
10689                 method: 'DELETE',
10690                 success: this.createPutSuccessHandler(this, {}, handlers.success),
10691                 error: handlers.error,
10692                 content: undefined
10693             });
10694 
10695             return this; // Allow cascading
10696         }
10697     });
10698 
10699     window.finesse = window.finesse || {};
10700     window.finesse.restservices = window.finesse.restservices || {};
10701     window.finesse.restservices.Contact = Contact;
10702     
10703     return Contact;
10704 });
10705 
10706 /**
10707 * JavaScript representation of the Finesse Contacts collection
10708 * object which contains a list of Contact objects.
10709  *
10710  * @requires finesse.clientservices.ClientServices
10711  * @requires Class
10712  * @requires finesse.FinesseBase
10713  * @requires finesse.restservices.RestBase
10714  * @requires finesse.restservices.Dialog
10715  * @requires finesse.restservices.RestCollectionBase
10716  */
10717 /** @private */
10718 define('restservices/Contacts',[
10719     'restservices/RestCollectionBase',
10720     'restservices/Contact'
10721 ],
10722 function (RestCollectionBase, Contact) {
10723     var Contacts = RestCollectionBase.extend(/** @lends finesse.restservices.Contacts.prototype */{
10724         
10725         /**
10726          * @class
10727          * JavaScript representation of a Contacts collection object. Also exposes
10728          * methods to operate on the object against the server.
10729          * @augments finesse.restservices.RestCollectionBase
10730          * @constructs
10731          * @see finesse.restservices.Contact
10732          * @see finesse.restservices.PhoneBook
10733          * @example
10734          *  _contacts = _phonebook.getContacts( {
10735          *      onCollectionAdd : _handleContactAdd,
10736          *      onCollectionDelete : _handleContactDelete,
10737          *      onLoad : _handleContactsLoaded
10738          *  });
10739          *  
10740          * _contactCollection = _contacts.getCollection();
10741          * for (var contactId in _contactCollection) {
10742          *     if (_contactCollection.hasOwnProperty(contactId)) {
10743          *         _contact = _contactCollection[contactId];
10744          *         etc...
10745          *     }
10746          * }
10747          */
10748         _fakeConstuctor: function () {
10749             /* This is here to hide the real init constructor from the public docs */
10750         },
10751         
10752         /** 
10753          * @private
10754          * JavaScript representation of a Contacts collection object. Also exposes
10755          * methods to operate on the object against the server.
10756          *
10757          * @param {Object} options
10758          *     An object with the following properties:<ul>
10759          *         <li><b>id:</b> The id of the object being constructed</li>
10760          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10761          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10762          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10763          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10764          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10765          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10766          *             <li><b>content:</b> {String} Raw string of response</li>
10767          *             <li><b>object:</b> {Object} Parsed object of response</li>
10768          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10769          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10770          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10771          *             </ul></li>
10772          *         </ul></li>
10773          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10774          **/
10775         init: function (options) {
10776             this._super(options);           
10777         },
10778 
10779         /**
10780          * @private
10781          * Gets the REST class for the current object - this is the Contacts class.
10782          */
10783         getRestClass: function () {
10784             return Contacts;
10785         },
10786 
10787         /**
10788          * @private
10789          * Gets the REST class for the objects that make up the collection. - this
10790          * is the Contact class.
10791          */
10792         getRestItemClass: function () {
10793             return Contact;
10794         },
10795 
10796         /**
10797          * @private
10798          * Gets the REST type for the current object - this is a "Contacts".
10799          */
10800         getRestType: function () {
10801             return "Contacts";
10802         },
10803         
10804         /**
10805          * @private
10806          * Gets the REST type for the objects that make up the collection - this is "Contacts".
10807          */
10808         getRestItemType: function () {
10809             return "Contact";
10810         },
10811 
10812         /**
10813          * @private
10814          * Override default to indicates that the collection supports making
10815          * requests.
10816          */
10817         supportsRequests: true,
10818 
10819         /**
10820          * @private
10821          * Override default to indicates that the collection subscribes to its objects.
10822          */
10823         supportsRestItemSubscriptions: false,
10824         
10825         /**
10826          * @private
10827          * Retrieve the Contacts.  This call will re-query the server and refresh the collection.
10828          *
10829          * @returns {finesse.restservices.Contacts}
10830          *     This Contacts object, to allow cascading.
10831          */
10832         get: function () {
10833             // set loaded to false so it will rebuild the collection after the get
10834             this._loaded = false;
10835             // reset collection
10836             this._collection = {};
10837             // perform get
10838             this._synchronize();
10839             return this;
10840         }
10841         
10842     });
10843     
10844     window.finesse = window.finesse || {};
10845     window.finesse.restservices = window.finesse.restservices || {};
10846     window.finesse.restservices.Contacts = Contacts;
10847     
10848     
10849     return Contacts;
10850 });
10851 
10852 /**
10853  * JavaScript representation of the Finesse PhoneBook object.
10854  *
10855  * @requires finesse.clientservices.ClientServices
10856  * @requires Class
10857  * @requires finesse.FinesseBase
10858  * @requires finesse.restservices.RestBase
10859  */
10860 
10861 /** @private */
10862 define('restservices/PhoneBook',[
10863     'restservices/RestBase',
10864     'restservices/Contacts'
10865 ],
10866 function (RestBase, Contacts) {
10867     var PhoneBook = RestBase.extend(/** @lends finesse.restservices.PhoneBook.prototype */{
10868 
10869         _contacts: null,
10870 
10871         /**
10872          * @class
10873          * A PhoneBook is a list of Contacts available to a User for quick dial.
10874          * 
10875          * @augments finesse.restservices.RestBase
10876          * @see finesse.restservices.Contacts
10877          * @constructs
10878          */
10879         _fakeConstuctor: function () {
10880             /* This is here to hide the real init constructor from the public docs */
10881         },
10882         
10883         /** 
10884          * @private
10885          * JavaScript representation of a PhoneBook object. Also exposes
10886          * methods to operate on the object against the server.
10887          *
10888          * @param {Object} options
10889          *     An object with the following properties:<ul>
10890          *         <li><b>id:</b> The id of the object being constructed</li>
10891          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
10892          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
10893          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
10894          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
10895          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
10896          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
10897          *             <li><b>content:</b> {String} Raw string of response</li>
10898          *             <li><b>object:</b> {Object} Parsed object of response</li>
10899          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
10900          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
10901          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
10902          *             </ul></li>
10903          *         </ul></li>
10904          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
10905          **/
10906         init: function (options) {
10907             this._super(options);
10908         },
10909 
10910         /**
10911          * @private
10912          * Gets the REST class for the current object - this is the PhoneBook class.
10913          * @returns {Object} The PhoneBook class.
10914          */
10915         getRestClass: function () {
10916             return PhoneBook;
10917         },
10918 
10919         /**
10920          * @private
10921          * Gets the REST type for the current object - this is a "PhoneBook".
10922          * @returns {String} The PhoneBook string.
10923          */
10924         getRestType: function () {
10925             return "PhoneBook";
10926         },
10927 
10928         /**
10929          * @private
10930          * Override default to indicate that this object doesn't support making
10931          * requests.
10932          */
10933         supportsRequests: false,
10934 
10935         /**
10936          * @private
10937          * Override default to indicate that this object doesn't support subscriptions.
10938          */
10939         supportsSubscriptions: false,
10940 
10941         /**
10942          * Getter for the name of the Phone Book.
10943          * @returns {String} The name.
10944          */
10945         getName: function () {
10946             this.isLoaded();
10947             return this.getData().name;
10948         },
10949 
10950         /**
10951          * Getter for the type flag.
10952          * @returns {String} The type.
10953          */
10954         getType: function () {
10955             this.isLoaded();
10956             return this.getData().type;
10957         },
10958 
10959         /**
10960          * @private
10961          * Getter for the Uri value.
10962          * @returns {String} The Uri.
10963          */
10964         getUri: function () {
10965             this.isLoaded();
10966             return this.getData().uri;
10967         },
10968 
10969         /**
10970          * Getter for a Contacts collection object that is associated with PhoneBook.
10971          * @param {finesse.interfaces.RequestHandlers} handlers
10972          *     An object containing the handlers for the request
10973          * @returns {finesse.restservices.Contacts}
10974          *     A Contacts collection object.
10975          */
10976         getContacts: function (callbacks) {
10977             var options = callbacks || {};
10978             options.parentObj = this;
10979             this.isLoaded();
10980 
10981             if (this._contacts === null) {
10982                 this._contacts = new Contacts(options);
10983             }
10984 
10985             return this._contacts;
10986         },
10987 
10988         /**
10989          * Getter for <contacts> node within PhoneBook - sometimes it's just a URI, sometimes it is a Contacts collection
10990          * @returns {String} uri to contacts
10991          *          or {finesse.restservices.Contacts} collection
10992          */
10993         getEmbeddedContacts: function(){
10994             this.isLoaded();
10995             return this.getData().contacts;
10996         },
10997 
10998         /** @private */
10999         createPutSuccessHandler: function(phonebook, contentBody, successHandler){
11000             return function (rsp) {
11001                 // Update internal structure based on response. Here we
11002                 // inject the contentBody from the PUT request into the
11003                 // rsp.object element to mimic a GET as a way to take
11004                 // advantage of the existing _processResponse method.
11005                 rsp.object = contentBody;
11006                 phonebook._processResponse(rsp);
11007 
11008                 //Remove the injected PhoneBook object before cascading response
11009                 rsp.object = {};
11010                 
11011                 //cascade response back to consumer's response handler
11012                 successHandler(rsp);
11013             };
11014         },
11015 
11016         /** @private */
11017         createPostSuccessHandler: function (phonebook, contentBody, successHandler) {
11018             return function (rsp) {
11019                 rsp.object = contentBody;
11020                 phonebook._processResponse(rsp);
11021 
11022                 //Remove the injected PhoneBook object before cascading response
11023                 rsp.object = {};
11024 
11025                 //cascade response back to consumer's response handler
11026                 successHandler(rsp);
11027             };
11028         },
11029 
11030         /**
11031          * @private
11032          * Add a PhoneBook.
11033          * @param {Object} newValues
11034          * @param {String} newValues.name Name of PhoneBook
11035          * @param {String} newValues.type Type of PhoneBook
11036          * @param {finesse.interfaces.RequestHandlers} handlers
11037          *     An object containing the handlers for the request
11038          * @returns {finesse.restservices.PhoneBook}
11039          *     This PhoneBook object, to allow cascading
11040          */
11041         add: function (newValues, handlers) {
11042             // this.isLoaded();
11043             var contentBody = {};
11044 
11045             contentBody[this.getRestType()] = {
11046                 "name": newValues.name,
11047                 "type": newValues.type
11048             };
11049 
11050             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11051             handlers = handlers || {};
11052 
11053             this.restRequest(this.getRestUrl(), {
11054                 method: 'POST',
11055                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
11056                 error: handlers.error,
11057                 content: contentBody
11058             });
11059 
11060             return this; // Allow cascading
11061         },
11062 
11063         /**
11064          * @private
11065          * Update a PhoneBook.
11066          * @param {Object} newValues
11067          * @param {String} newValues.name Name of PhoneBook
11068          * @param {String} newValues.type Type of PhoneBook
11069          * @param {finesse.interfaces.RequestHandlers} handlers
11070          *     An object containing the handlers for the request
11071          * @returns {finesse.restservices.PhoneBook}
11072          *     This PhoneBook object, to allow cascading
11073          */
11074         update: function (newValues, handlers) {
11075             this.isLoaded();
11076             var contentBody = {};
11077 
11078             contentBody[this.getRestType()] = {
11079                 "uri": this.getId(),
11080                 "name": newValues.name,
11081                 "type": newValues.type
11082             };
11083 
11084             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11085             handlers = handlers || {};
11086 
11087             this.restRequest(this.getRestUrl(), {
11088                 method: 'PUT',
11089                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
11090                 error: handlers.error,
11091                 content: contentBody
11092             });
11093 
11094             return this; // Allow cascading
11095         },
11096 
11097 
11098         /**
11099          * Delete a PhoneBook.
11100          * @param {finesse.interfaces.RequestHandlers} handlers
11101          *     An object containing the handlers for the request
11102          * @returns {finesse.restservices.PhoneBook}
11103          *     This PhoneBook object, to allow cascading
11104          */
11105         "delete": function ( handlers) {
11106             this.isLoaded();
11107 
11108             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11109             handlers = handlers || {};
11110 
11111             this.restRequest(this.getRestUrl(), {
11112                 method: 'DELETE',
11113                 success: this.createPutSuccessHandler(this, {}, handlers.success),
11114                 error: handlers.error,
11115                 content: undefined
11116             });
11117 
11118             return this; // Allow cascading
11119         }
11120 
11121 
11122 
11123     });
11124     
11125     window.finesse = window.finesse || {};
11126     window.finesse.restservices = window.finesse.restservices || {};
11127     window.finesse.restservices.PhoneBook = PhoneBook;
11128     
11129     return PhoneBook;
11130 });
11131 
11132 /**
11133 * JavaScript representation of the Finesse PhoneBooks collection
11134 * object which contains a list of PhoneBook objects.
11135  *
11136  * @requires finesse.clientservices.ClientServices
11137  * @requires Class
11138  * @requires finesse.FinesseBase
11139  * @requires finesse.restservices.RestBase
11140  * @requires finesse.restservices.Dialog
11141  * @requires finesse.restservices.RestCollectionBase
11142  */
11143 /** @private */
11144 define('restservices/PhoneBooks',[
11145     'restservices/RestCollectionBase',
11146     'restservices/PhoneBook'
11147 ],
11148 function (RestCollectionBase, PhoneBook) {
11149     var PhoneBooks = RestCollectionBase.extend(/** @lends finesse.restservices.PhoneBooks.prototype */{
11150         
11151         /**
11152          * @class
11153          * JavaScript representation of a PhoneBooks collection object. 
11154          * @augments finesse.restservices.RestCollectionBase
11155          * @constructs
11156          * @see finesse.restservices.PhoneBook
11157          * @see finesse.restservices.Contacts
11158          * @see finesse.restservices.Contact
11159          * @example
11160          *  _phoneBooks = _user.getPhoneBooks( {
11161          *      onCollectionAdd : _handlePhoneBookAdd,
11162          *      onCollectionDelete : _handlePhoneBookDelete,
11163          *      onLoad : _handlePhoneBooksLoaded
11164          *  });
11165          *  
11166          * _phoneBookCollection = _phoneBooks.getCollection();
11167          * for (var phoneBookId in _phoneBookCollection) {
11168          *     if (_phoneBookCollection.hasOwnProperty(phoneBookId)) {
11169          *         _phoneBook = _phoneBookCollection[phoneBookId];
11170          *         etc...
11171          *     }
11172          * }
11173         */
11174         _fakeConstuctor: function () {
11175             /* This is here to hide the real init constructor from the public docs */
11176         },
11177         
11178        /**
11179          * @private
11180          *
11181          * @param {Object} options
11182          *     An object with the following properties:<ul>
11183          *         <li><b>id:</b> The id of the object being constructed</li>
11184          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11185          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11186          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11187          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11188          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11189          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11190          *             <li><b>content:</b> {String} Raw string of response</li>
11191          *             <li><b>object:</b> {Object} Parsed object of response</li>
11192          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11193          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11194          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11195          *             </ul></li>
11196          *         </ul></li>
11197          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11198          **/
11199         init: function (options) {
11200             // Keep the REST response for PhoneBooks to check for 206 Partial Content.
11201             this.keepRestResponse = true;
11202             // Add in the Range header which is required for PhoneBooks API.
11203             this.extraHeaders = { "Range": "objects=1-1500" };
11204             this._super(options);
11205         },
11206 
11207         /**
11208          * @private
11209          * Gets the REST class for the current object - this is the PhoneBooks class.
11210          * @returns {Object} The PhoneBooks class.
11211          */
11212         getRestClass: function () {
11213             return PhoneBooks;
11214         },
11215 
11216         /**
11217          * @private
11218          * Gets the REST class for the objects that make up the collection. - this is the PhoneBook class.
11219          * @returns {Object} The PhoneBook class
11220          */
11221         getRestItemClass: function () {
11222             return PhoneBook;
11223         },
11224 
11225         /**
11226          * @private
11227          * Gets the REST type for the current object - this is a "PhoneBooks".
11228          * @returns {String} The PhoneBooks string.
11229          */
11230         getRestType: function () {
11231             return "PhoneBooks";
11232         },
11233         
11234         /**
11235          * @private
11236          * Gets the REST type for the objects that make up the collection - this is "PhoneBooks".
11237          * @returns {String} The PhoneBook string.
11238          */
11239         getRestItemType: function () {
11240             return "PhoneBook";
11241         },
11242 
11243         /**
11244          * @private
11245          * Override default to indicates that the collection supports making
11246          * requests.
11247          */
11248         supportsRequests: true,
11249 
11250         /**
11251          * @private
11252          * Override default to indicates that the collection subscribes to its objects.
11253          */
11254         supportsRestItemSubscriptions: false,
11255         
11256         /**
11257          * @private
11258          * Retrieve the PhoneBooks.  This call will re-query the server and refresh the collection.
11259          *
11260          * @returns {finesse.restservices.PhoneBooks}
11261          *     This PhoneBooks object, to allow cascading.
11262          */
11263         get: function () {
11264             // set loaded to false so it will rebuild the collection after the get
11265             this._loaded = false;
11266             // reset collection
11267             this._collection = {};
11268             // perform get
11269             this._synchronize();
11270             return this;
11271         }
11272         
11273     });
11274     
11275     window.finesse = window.finesse || {};
11276     window.finesse.restservices = window.finesse.restservices || {};
11277     window.finesse.restservices.PhoneBooks = PhoneBooks;
11278     
11279     return PhoneBooks;
11280 });
11281 
11282 /**
11283  * JavaScript representation of the Finesse WorkflowAction object.
11284  *
11285  * @requires finesse.clientservices.ClientServices
11286  * @requires Class
11287  * @requires finesse.FinesseBase
11288  * @requires finesse.restservices.RestBase
11289  */
11290 
11291 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
11292 /*global define,finesse */
11293 
11294 /** @private */
11295 define('restservices/WorkflowAction',['restservices/RestBase'], function (RestBase) {
11296 
11297     var WorkflowAction = RestBase.extend({
11298 
11299         _contacts: null,
11300 
11301         actionTypes: [
11302             {
11303                 name: 'BROWSER_POP',
11304                 params: [
11305                     {
11306                         name: 'windowName',
11307                         type: 'text'
11308                     },
11309                     {
11310                         name: 'path',
11311                         type: 'systemVariableSingleLineEditor'
11312                     }
11313                 ]
11314             },
11315             {
11316                 name: 'HTTP_REQUEST',
11317                 params: [
11318                     {
11319                         name: 'method',
11320                         type: 'dropdown',
11321                         values: ['POST', 'PUT']
11322                     },
11323                     {
11324                         name: 'location',
11325                         type: 'dropdown',
11326                         values: ['FINESSE', 'OTHER']
11327                     },
11328                     {
11329                         name: 'contentType',
11330                         type: 'text'
11331                     },
11332                     {
11333                         name: 'path',
11334                         type: 'systemVariableSingleLineEditor'
11335                     },
11336                     {
11337                         name: 'body',
11338                         type: 'systemVariableMultiLineEditor'
11339                     }
11340                 ]
11341             }            
11342             // more action type definitions here
11343         ],
11344 
11345         /**
11346          * @class
11347          * A WorkflowAction is an action (e.g. Browser Pop, Rest Request) defined in a
11348          * Workflow and triggered by a system event (Call Received, Call Ended, etc.).
11349          * 
11350          * @augments finesse.restservices.RestBase
11351          * @see finesse.restservices.Workflow
11352          * @constructs
11353          */
11354         _fakeConstuctor: function () {
11355             /* This is here to hide the real init constructor from the public docs */
11356         },
11357         
11358         /**
11359          * @private
11360          * JavaScript representation of a WorkflowAction object. Also exposes
11361          * methods to operate on the object against the server.
11362          *
11363          * @param {Object} options
11364          *     An object with the following properties:<ul>
11365          *         <li><b>id:</b> The id of the object being constructed</li>
11366          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11367          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11368          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11369          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11370          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11371          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11372          *             <li><b>content:</b> {String} Raw string of response</li>
11373          *             <li><b>object:</b> {Object} Parsed object of response</li>
11374          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11375          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11376          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11377          *             </ul></li>
11378          *         </ul></li>
11379          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11380          **/
11381         init: function (options) {
11382             this._super(options);
11383         },
11384 
11385         /**
11386          * @private
11387          * Gets the REST class for the current object - this is the WorkflowAction class.
11388          * @returns {Object} The WorkflowAction class.
11389          */
11390         getRestClass: function () {
11391             return finesse.restservices.WorkflowAction;
11392         },
11393 
11394         /**
11395          * @private
11396          * Gets the REST type for the current object - this is a "WorkflowAction".
11397          * @returns {String} The WorkflowAction string.
11398          */
11399         getRestType: function () {
11400             return "WorkflowAction";
11401         },
11402 
11403         /**
11404          * @private
11405          * Override default to indicate that this object doesn't support making
11406          * requests.
11407          */
11408         supportsRequests: false,
11409 
11410         /**
11411          * @private
11412          * Override default to indicate that this object doesn't support subscriptions.
11413          */
11414         supportsSubscriptions: false,
11415 
11416         /**
11417          * Getter for the name.
11418          * @returns {String} The name.
11419          */
11420         getName: function () {
11421             this.isLoaded();
11422             return this.getData().name;
11423         },
11424 
11425         /**
11426          * Getter for the type flag.
11427          * @returns {String} The type.
11428          */
11429         getType: function () {
11430             this.isLoaded();
11431             return this.getData().type;
11432         },
11433 
11434         /**
11435          * @private
11436          * Getter for the Uri value.
11437          * @returns {String} The Uri.
11438          */
11439         getUri: function () {
11440             this.isLoaded();
11441             return this.getData().uri;
11442         },
11443 
11444         /**
11445          * @private
11446          * Getter for the handledBy value.
11447          * @returns {String} handledBy.
11448          */
11449         getHandledBy: function () {
11450             this.isLoaded();
11451             return this.getData().handledBy;
11452         },
11453 
11454         /**
11455          * Getter for the parameters.
11456          * @returns {Object} key = param name, value = param value
11457          */
11458         getParams: function () {
11459             var map = {},
11460                 params = this.getData().params.Param,
11461                 i,
11462                 param;
11463 
11464             for(i=0; i<params.length; i+=1){
11465                 param = params[i];
11466                 map[param.name] = param.value || "";
11467             }
11468 
11469             return map;
11470         },
11471 
11472         /**
11473          * Getter for the ActionVariables
11474          * @returns {Object} key = action variable name, value = Object{name, type, node, testValue}
11475          */
11476         getActionVariables: function() {
11477             var map = {},
11478                 actionVariablesParent = this.getData().actionVariables,
11479                 actionVariables,
11480                 i,
11481                 actionVariable;
11482 
11483             if (actionVariablesParent === null ||  typeof(actionVariablesParent) === "undefined" || actionVariablesParent.length === 0){
11484                 return map;
11485             }
11486             actionVariables = actionVariablesParent.ActionVariable;
11487 
11488             if(actionVariables.length > 0){
11489                 for(i=0; i<actionVariables.length; i+=1){
11490                     actionVariable = actionVariables[i];
11491                     // escape nulls to empty string
11492                     actionVariable.name = actionVariable.name || "";
11493                     actionVariable.type = actionVariable.type || "";
11494                     actionVariable.node = actionVariable.node || "";
11495                     actionVariable.testValue = actionVariable.testValue || "";
11496                     map[actionVariable.name] = actionVariable;
11497                 }
11498             } else {
11499                 map[actionVariables.name] = actionVariables;
11500             }
11501 
11502             return map;
11503         },
11504 
11505         /** @private */
11506         createPutSuccessHandler: function(action, contentBody, successHandler){
11507             return function (rsp) {
11508                 // Update internal structure based on response. Here we
11509                 // inject the contentBody from the PUT request into the
11510                 // rsp.object element to mimic a GET as a way to take
11511                 // advantage of the existing _processResponse method.
11512                 rsp.object = contentBody;
11513                 action._processResponse(rsp);
11514 
11515                 //Remove the injected WorkflowAction object before cascading response
11516                 rsp.object = {};
11517                 
11518                 //cascade response back to consumer's response handler
11519                 successHandler(rsp);
11520             };
11521         },
11522 
11523         /** @private */
11524         createPostSuccessHandler: function (action, contentBody, successHandler) {
11525             return function (rsp) {
11526                 rsp.object = contentBody;
11527                 action._processResponse(rsp);
11528 
11529                 //Remove the injected WorkflowAction object before cascading response
11530                 rsp.object = {};
11531 
11532                 //cascade response back to consumer's response handler
11533                 successHandler(rsp);
11534             };
11535         },
11536 
11537         /**
11538          * @private
11539          * Build params array out of all the values coming into add or update methods
11540          * paramMap is a map of params.. we need to translate it into an array of Param objects
11541          * where path and windowName are params for the BROWSER_POP type
11542          */
11543         buildParamsForRest: function(paramMap){
11544             var params = {"Param": []},
11545                 i;
11546             for(i in paramMap){
11547                 if(paramMap.hasOwnProperty(i)){
11548                     params.Param.push({name: i, value: paramMap[i]});
11549                 }
11550             }
11551             return params;
11552         },
11553 
11554         /**
11555          * @private
11556          * Build actionVariables array out of all the values coming into add or update methods
11557          * actionVariableMap is a map of actionVariables.. we need to translate it into an array of ActionVariable objects
11558          * where path and windowName are params for the BROWSER_POP type
11559          */
11560         buildActionVariablesForRest: function(actionVariableMap){
11561             var actionVariables = {"ActionVariable": []},
11562                 i,
11563                 actionVariable;
11564             for(i in actionVariableMap){
11565                 if(actionVariableMap.hasOwnProperty(i)){
11566                     // {name: "callVariable1", type: "SYSTEM", node: "", testValue: "<blink>"}
11567                     actionVariable = {
11568                         "name": actionVariableMap[i].name,
11569                         "type": actionVariableMap[i].type,
11570                         "node": actionVariableMap[i].node,
11571                         "testValue": actionVariableMap[i].testValue
11572                     };
11573                     actionVariables.ActionVariable.push(actionVariable);
11574                 }
11575             }
11576             return actionVariables;
11577         },
11578 
11579         /**
11580          * Add
11581          */
11582         add: function (newValues, handlers) {
11583             var contentBody = {};
11584 
11585             contentBody[this.getRestType()] = {
11586                 "name": newValues.name,
11587                 "type": newValues.type,
11588                 "handledBy": newValues.handledBy,
11589                 "params": this.buildParamsForRest(newValues.params),
11590                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
11591             };
11592 
11593             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11594             handlers = handlers || {};
11595 
11596             this.restRequest(this.getRestUrl(), {
11597                 method: 'POST',
11598                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
11599                 error: handlers.error,
11600                 content: contentBody
11601             });
11602 
11603             return this; // Allow cascading
11604         },
11605 
11606         /**
11607          * @private
11608          * Update
11609          */
11610         update: function (newValues, handlers) {
11611             this.isLoaded();
11612             var contentBody = {};
11613             
11614             contentBody[this.getRestType()] = {
11615                 "uri": this.getId(),
11616                 "name": newValues.name,
11617                 "type": newValues.type,
11618                 "handledBy": newValues.handledBy,
11619                 "params": this.buildParamsForRest(newValues.params),
11620                 "actionVariables": this.buildActionVariablesForRest(newValues.actionVariables)
11621             };
11622 
11623             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11624             handlers = handlers || {};
11625 
11626             this.restRequest(this.getRestUrl(), {
11627                 method: 'PUT',
11628                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
11629                 error: handlers.error,
11630                 content: contentBody
11631             });
11632 
11633             return this; // Allow cascading
11634         },
11635 
11636 
11637         /**
11638          * @private
11639          * Delete
11640          */
11641         "delete": function ( handlers) {
11642             this.isLoaded();
11643 
11644             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
11645             handlers = handlers || {};
11646 
11647             this.restRequest(this.getRestUrl(), {
11648                 method: 'DELETE',
11649                 success: this.createPutSuccessHandler(this, {}, handlers.success),
11650                 error: handlers.error,
11651                 content: undefined
11652             });
11653 
11654             return this; // Allow cascading
11655         }
11656 
11657 
11658 
11659     });
11660 
11661     window.finesse = window.finesse || {};
11662     window.finesse.restservices = window.finesse.restservices || {};
11663     window.finesse.restservices.WorkflowAction = WorkflowAction;
11664     
11665     return WorkflowAction;
11666 });
11667 
11668 /**
11669 * JavaScript representation of the Finesse WorkflowActions collection
11670 * object which contains a list of WorkflowAction objects.
11671  *
11672  * @requires finesse.clientservices.ClientServices
11673  * @requires Class
11674  * @requires finesse.FinesseBase
11675  * @requires finesse.restservices.RestBase
11676  * @requires finesse.restservices.Dialog
11677  * @requires finesse.restservices.RestCollectionBase
11678  */
11679 
11680 /** @private */
11681 define('restservices/WorkflowActions',[
11682     'restservices/RestCollectionBase',
11683     'restservices/RestBase',
11684     'restservices/WorkflowAction'
11685 ],
11686 function (RestCollectionBase, RestBase, WorkflowAction) {
11687 
11688     var WorkflowActions = RestCollectionBase.extend({
11689         
11690         /**
11691          * @class
11692          * JavaScript representation of a WorkflowActions collection object. 
11693          * @augments finesse.restservices.RestCollectionBase
11694          * @constructs
11695          * @see finesse.restservices.WorkflowAction
11696          * @see finesse.restservices.Workflow
11697          * @see finesse.restservices.Workflows
11698          * @example
11699          *  _workflowActions = _user.getWorkflowActions( {
11700          *      onCollectionAdd : _handleWorkflowActionAdd,
11701          *      onCollectionDelete : _handleWorkflowActionDelete,
11702          *      onLoad : _handleWorkflowActionsLoaded
11703          *  });
11704         */
11705         _fakeConstuctor: function () {
11706             /* This is here to hide the real init constructor from the public docs */
11707         },
11708         
11709         /**
11710          * @private
11711          * JavaScript representation of a WorkflowActions collection object. Also exposes
11712          * methods to operate on the object against the server.
11713          *
11714          * @param {Object} options
11715          *     An object with the following properties:<ul>
11716          *         <li><b>id:</b> The id of the object being constructed</li>
11717          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11718          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11719          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11720          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11721          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11722          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11723          *             <li><b>content:</b> {String} Raw string of response</li>
11724          *             <li><b>object:</b> {Object} Parsed object of response</li>
11725          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11726          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11727          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11728          *             </ul></li>
11729          *         </ul></li>
11730          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11731          **/
11732         init: function (options) {
11733             this._super(options);           
11734         },
11735 
11736         /**
11737          * @private
11738          * Gets the REST class for the current object - this is the WorkflowActions class.
11739          */
11740         getRestClass: function () {
11741             return WorkflowActions;
11742         },
11743 
11744         /**
11745          * @private
11746          * Gets the REST class for the objects that make up the collection. - this
11747          * is the WorkflowAction class.
11748          */
11749         getRestItemClass: function () {
11750             return WorkflowAction;
11751         },
11752 
11753         /**
11754          * @private
11755          * Gets the REST type for the current object - this is a "WorkflowActions".
11756          */
11757         getRestType: function () {
11758             return "WorkflowActions";
11759         },
11760         
11761         /**
11762          * @private
11763          * Gets the REST type for the objects that make up the collection - this is "WorkflowActions".
11764          */
11765         getRestItemType: function () {
11766             return "WorkflowAction";
11767         },
11768 
11769         /**
11770          * @private
11771          * Override default to indicates that the collection supports making
11772          * requests.
11773          */
11774         supportsRequests: true,
11775 
11776         /**
11777          * @private
11778          * Override default to indicates that the collection subscribes to its objects.
11779          */
11780         supportsRestItemSubscriptions: false,
11781         
11782         /**
11783          * @private
11784          * Retrieve the WorkflowActions.
11785          *
11786          * @returns {finesse.restservices.WorkflowActions}
11787          *     This WorkflowActions object to allow cascading.
11788          */
11789         get: function () {
11790             // set loaded to false so it will rebuild the collection after the get
11791             this._loaded = false;
11792             // reset collection
11793             this._collection = {};
11794             // perform get
11795             this._synchronize();
11796             return this;
11797         }
11798     });
11799 
11800     window.finesse = window.finesse || {};
11801     window.finesse.restservices = window.finesse.restservices || {};
11802     window.finesse.restservices.WorkflowActions = WorkflowActions;
11803         
11804     return WorkflowActions;
11805 });
11806 
11807 /**
11808  * JavaScript representation of the Finesse Workflow object.
11809  *
11810  * @requires finesse.clientservices.ClientServices
11811  * @requires Class
11812  * @requires finesse.FinesseBase
11813  * @requires finesse.restservices.RestBase
11814  */
11815 
11816 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
11817 /*global define,finesse */
11818 
11819 /** @private */
11820 define('restservices/Workflow',[
11821     'restservices/RestBase',
11822     'restservices/WorkflowActions'
11823 ],
11824 function (RestBase, WorkflowActions) {
11825 
11826     var Workflow = RestBase.extend({
11827 
11828         /**
11829          * @class
11830          * JavaScript representation of a Workflow object. Also exposes
11831          * methods to operate on the object against the server.
11832          *
11833          * @param {Object} options
11834          *     An object with the following properties:<ul>
11835          *         <li><b>id:</b> The id of the object being constructed</li>
11836          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
11837          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
11838          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
11839          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
11840          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
11841          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
11842          *             <li><b>content:</b> {String} Raw string of response</li>
11843          *             <li><b>object:</b> {Object} Parsed object of response</li>
11844          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
11845          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
11846          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
11847          *             </ul></li>
11848          *         </ul></li>
11849          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
11850          * @constructs
11851          **/
11852         init: function (options) {
11853             this._super(options);
11854         },
11855 
11856         /**
11857          * @private
11858          * Gets the REST class for the current object - this is the Workflow class.
11859          * @returns {Object} The Workflow class.
11860          */
11861         getRestClass: function () {
11862             return Workflow;
11863         },
11864 
11865         /**
11866          * @private
11867          * Gets the REST type for the current object - this is a "Workflow".
11868          * @returns {String} The Workflow string.
11869          */
11870         getRestType: function () {
11871             return "Workflow";
11872         },
11873 
11874         /**
11875          * @private
11876          * Override default to indicate that this object doesn't support making
11877          * requests.
11878          */
11879         supportsRequests: false,
11880 
11881         /**
11882          * @private
11883          * Override default to indicate that this object doesn't support subscriptions.
11884          */
11885         supportsSubscriptions: false,
11886 
11887         /**
11888          * @private
11889          * Getter for the Uri value.
11890          * @returns {String} The Uri.
11891          */
11892         getUri: function () {
11893             this.isLoaded();
11894             return this.getData().uri;
11895         },
11896 
11897         /**
11898          * Getter for the name.
11899          * @returns {String} The name.
11900          */
11901         getName: function () {
11902             this.isLoaded();
11903             return this.getData().name;
11904         },
11905 
11906         /**
11907          * Getter for the description.
11908          * @returns {String} The description.
11909          */
11910         getDescription: function () {
11911             this.isLoaded();
11912             return this.getData().description;
11913         },
11914         
11915         /**
11916          * Getter for the media.
11917          * @returns {String} The media.
11918          */
11919         getMedia: function () {
11920             this.isLoaded();
11921             return this.getData().media;
11922         },
11923 
11924         /**
11925          * Getter for the trigger set.
11926          * @returns {String} The trigger set.
11927          */
11928         getTriggerSet: function () {
11929             this.isLoaded();
11930             return this.getData().TriggerSet;
11931         },
11932 
11933         /**
11934          * Getter for the condition set.
11935          * @returns {String} The condition set.
11936          */
11937         getConditionSet: function () {
11938             this.isLoaded();
11939             return this.getData().ConditionSet;
11940         },
11941         
11942         /**
11943          * Getter for the assigned workflowActions.
11944          * @returns {String} The workflowActions object.
11945          */
11946         getWorkflowActions: function () {
11947             this.isLoaded();
11948             var workflowActions = this.getData().workflowActions;
11949             if (workflowActions === null) {
11950                 workflowActions = "";
11951             }
11952             return workflowActions;
11953         },
11954 
11955         createPutSuccessHandler: function (workflow, contentBody, successHandler) {
11956             return function (rsp) {
11957                 // Update internal structure based on response. Here we
11958                 // inject the contentBody from the PUT request into the
11959                 // rsp.object element to mimic a GET as a way to take
11960                 // advantage of the existing _processResponse method.
11961                 rsp.object = contentBody;
11962                 workflow._processResponse(rsp);
11963 
11964                 //Remove the injected Workflow object before cascading response
11965                 rsp.object = {};
11966 
11967                 //cascade response back to consumer's response handler
11968                 successHandler(rsp);
11969             };
11970         },
11971 
11972         createPostSuccessHandler: function (workflow, contentBody, successHandler) {
11973             return function (rsp) {
11974                 rsp.object = contentBody;
11975                 workflow._processResponse(rsp);
11976 
11977                 //Remove the injected Workflow object before cascading response
11978                 rsp.object = {};
11979 
11980                 //cascade response back to consumer's response handler
11981                 successHandler(rsp);
11982             };
11983         },
11984 
11985         /**
11986          * @private
11987          * Add
11988          */
11989         add: function (newValues, handlers) {
11990             // this.isLoaded();
11991             var contentBody = {};
11992 
11993             contentBody[this.getRestType()] = {
11994             	"media": newValues.media,
11995                 "name": newValues.name,
11996                 "description": newValues.description,
11997                 "TriggerSet" : newValues.TriggerSet,
11998                 "ConditionSet" : newValues.ConditionSet,
11999                 "workflowActions" : newValues.workflowActions
12000             };
12001 
12002             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12003             handlers = handlers || {};
12004 
12005             this.restRequest(this.getRestUrl(), {
12006                 method: 'POST',
12007                 success: this.createPostSuccessHandler(this, contentBody, handlers.success),
12008                 error: handlers.error,
12009                 content: contentBody
12010             });
12011 
12012             return this; // Allow cascading
12013         },
12014 
12015         /**
12016          * @private
12017          * Update
12018          */
12019         update: function (newValues, handlers) {
12020             this.isLoaded();
12021             var contentBody = {};
12022 
12023             contentBody[this.getRestType()] = {
12024                 "uri": this.getId(),
12025                 "media": this.getMedia(),
12026                 "name": newValues.name,
12027                 "description": newValues.description,
12028                 "TriggerSet" : newValues.TriggerSet,
12029                 "ConditionSet" : newValues.ConditionSet,
12030                 "workflowActions" : newValues.workflowActions
12031             };
12032 
12033             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12034             handlers = handlers || {};
12035 
12036             this.restRequest(this.getRestUrl(), {
12037                 method: 'PUT',
12038                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
12039                 error: handlers.error,
12040                 content: contentBody
12041             });
12042 
12043             return this; // Allow cascading
12044         },
12045 
12046 
12047         /**
12048          * @private
12049          * Delete
12050          */
12051         "delete": function (handlers) {
12052             this.isLoaded();
12053 
12054             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12055             handlers = handlers || {};
12056 
12057             this.restRequest(this.getRestUrl(), {
12058                 method: 'DELETE',
12059                 success: this.createPutSuccessHandler(this, {}, handlers.success),
12060                 error: handlers.error,
12061                 content: undefined
12062             });
12063 
12064             return this; // Allow cascading
12065         }
12066 
12067 
12068 
12069     });
12070 
12071     window.finesse = window.finesse || {};
12072     window.finesse.restservices = window.finesse.restservices || {};
12073     window.finesse.restservices.Workflow = Workflow;
12074 
12075     return Workflow;
12076 });
12077 
12078 /**
12079 * JavaScript representation of the Finesse workflows collection
12080 * object which contains a list of workflow objects.
12081  *
12082  * @requires finesse.clientservices.ClientServices
12083  * @requires Class
12084  * @requires finesse.FinesseBase
12085  * @requires finesse.restservices.RestBase
12086  * @requires finesse.restservices.Dialog
12087  * @requires finesse.restservices.RestCollectionBase
12088  */
12089 
12090 /** @private */
12091 define('restservices/Workflows',[
12092     'restservices/RestCollectionBase',
12093     'restservices/RestBase',
12094     'restservices/Workflow'
12095 ],
12096 function (RestCollectionBase, RestBase, Workflow) {
12097 
12098     var Workflows = RestCollectionBase.extend({
12099 
12100         /**
12101          * @class
12102          * JavaScript representation of a workflows collection object. Also exposes
12103          * methods to operate on the object against the server.
12104          *
12105          * @param {Object} options
12106          *     An object with the following properties:<ul>
12107          *         <li><b>id:</b> The id of the object being constructed</li>
12108          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12109          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12110          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12111          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12112          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12113          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12114          *             <li><b>content:</b> {String} Raw string of response</li>
12115          *             <li><b>object:</b> {Object} Parsed object of response</li>
12116          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12117          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12118          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12119          *             </ul></li>
12120          *         </ul></li>
12121          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12122          *  @constructs
12123          **/
12124         init: function (options) {
12125             this._super(options);
12126         },
12127 
12128         /**
12129          * @private
12130          * Gets the REST class for the current object - this is the workflows class.
12131          */
12132         getRestClass: function () {
12133             return Workflows;
12134         },
12135 
12136         /**
12137          * @private
12138          * Gets the REST class for the objects that make up the collection. - this
12139          * is the workflow class.
12140          */
12141         getRestItemClass: function () {
12142             return Workflow;
12143         },
12144 
12145         /**
12146          * @private
12147          * Gets the REST type for the current object - this is a "workflows".
12148          */
12149         getRestType: function () {
12150             return "Workflows";
12151         },
12152 
12153         /**
12154          * @private
12155          * Gets the REST type for the objects that make up the collection - this is "workflows".
12156          */
12157         getRestItemType: function () {
12158             return "Workflow";
12159         },
12160 
12161         /**
12162          * @private
12163          * Override default to indicates that the collection supports making requests.
12164          */
12165         supportsRequests: true,
12166 
12167         /**
12168          * @private
12169          * Override default to indicates that the collection does not subscribe to its objects.
12170          */
12171         supportsRestItemSubscriptions: false,
12172 
12173         /**
12174          * @private
12175          * Retrieve the workflows. This call will re-query the server and refresh the collection.
12176          *
12177          * @returns {finesse.restservices.workflows}
12178          *     This workflows object to allow cascading.
12179          */
12180         get: function () {
12181             // set loaded to false so it will rebuild the collection after the get
12182             this._loaded = false;
12183             // reset collection
12184             this._collection = {};
12185             // perform get
12186             this._synchronize();
12187             return this;
12188         }
12189     });
12190 
12191     window.finesse = window.finesse || {};
12192     window.finesse.restservices = window.finesse.restservices || {};
12193     window.finesse.restservices.Workflows = Workflows;
12194         
12195     return Workflows;
12196 });
12197 
12198 /**
12199  * JavaScript representation of the Finesse MediaPropertiesLayout object for the Admin webapp.
12200  * @requires finesse.clientservices.ClientServices
12201  * @requires Class
12202  * @requires finesse.FinesseBase
12203  * @requires finesse.restservices.RestBase
12204  */
12205 
12206 /** The following comment is to prevent jslint errors about 
12207  * using variables before they are defined.
12208  */
12209 /*global finesse*/
12210 
12211 /**
12212  * @class
12213  * JavaScript representation of a MediaPropertiesLayout object for the Admin webapp. Also exposes
12214  * methods to operate on the object against the server.
12215  *
12216  * @constructor
12217  * @param {String} id
12218  *     Not required...
12219  * @param {Object} callbacks
12220  *     An object containing callbacks for instantiation and runtime
12221  * @param {Function} callbacks.onLoad(this)
12222  *     Callback to invoke upon successful instantiation, passes in MediaPropertiesLayout object
12223  * @param {Function} callbacks.onLoadError(rsp)
12224  *     Callback to invoke on instantiation REST request error
12225  *     as passed by finesse.clientservices.ClientServices.ajax()
12226  *     {
12227  *         status: {Number} The HTTP status code returned
12228  *         content: {String} Raw string of response
12229  *         object: {Object} Parsed object of response
12230  *         error: {Object} Wrapped exception that was caught
12231  *         error.errorType: {String} Type of error that was caught
12232  *         error.errorMessage: {String} Message associated with error
12233  *     }
12234  * @param {Function} callbacks.onChange(this)
12235  *     Callback to invoke upon successful update, passes in MediaPropertiesLayout object
12236  * @param {Function} callbacks.onError(rsp)
12237  *     Callback to invoke on update error (refresh or event)
12238  *     as passed by finesse.clientservices.ClientServices.ajax()
12239  *     {
12240  *         status: {Number} The HTTP status code returned
12241  *         content: {String} Raw string of response
12242  *         object: {Object} Parsed object of response
12243  *         error: {Object} Wrapped exception that was caught
12244  *         error.errorType: {String} Type of error that was caught
12245  *         error.errorMessage: {String} Message associated with error
12246  *     }
12247  */
12248 
12249 /** @private */
12250 define('restservices/MediaPropertiesLayout',['restservices/RestBase'], function (RestBase) {
12251     var MediaPropertiesLayout = RestBase.extend(/** @lends finesse.restservices.MediaPropertiesLayout.prototype */{
12252 
12253         /**
12254          * @class
12255          * The MediaPropertiesLayout handles which call variables are associated with Dialogs.
12256          * 
12257          * @augments finesse.restservices.RestBase
12258          * @see finesse.restservices.Dialog#getMediaProperties
12259          * @see finesse.restservices.User#getMediaPropertiesLayout
12260          * @constructs
12261          */
12262         _fakeConstuctor: function () {
12263             /* This is here to hide the real init constructor from the public docs */
12264         },
12265         
12266         /**
12267          * @private
12268          * JavaScript representation of a MediaPropertiesLayout object. Also exposes
12269          * methods to operate on the object against the server.
12270          *
12271          * @param {Object} options
12272          *     An object with the following properties:<ul>
12273          *         <li><b>id:</b> The id of the object being constructed</li>
12274          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12275          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12276          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12277          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12278          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12279          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12280          *             <li><b>content:</b> {String} Raw string of response</li>
12281          *             <li><b>object:</b> {Object} Parsed object of response</li>
12282          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12283          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12284          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12285          *             </ul></li>
12286          *         </ul></li>
12287          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12288          **/
12289         init: function (options) {
12290             this._super(options);
12291         },
12292 
12293         /**
12294          * @private
12295          * Gets the REST class for the current object - this is the MediaPropertiesLayout object.
12296          * @returns {Object} The MediaPropertiesLayout constructor.
12297          */
12298         getRestClass: function () {
12299             return MediaPropertiesLayout;
12300         },
12301 
12302         /**
12303          * @private
12304          * Gets the REST type for the current object - this is a "MediaPropertiesLayout".
12305          * @returns {String} The MediaPropertiesLayout string
12306          */
12307         getRestType: function () {
12308             return "MediaPropertiesLayout";
12309         },
12310 
12311         /**
12312          * @private
12313          * Returns whether this object supports subscriptions
12314          */
12315         supportsSubscriptions: false,
12316 
12317         /**
12318          * Getter for the name.
12319          * @returns {String} The name.
12320          */
12321         getName: function () {
12322             this.isLoaded();
12323             return this._data.name;
12324         },
12325 
12326         /**
12327          * Getter for the description.
12328          * @returns {String} The description.
12329          */
12330         getDescription: function () {
12331             this.isLoaded();
12332             return this._data.description || "";
12333         },
12334 
12335         /**
12336          * Getter for the layout type (should be DEFAULT or CUSTOM).
12337          * @returns {String} The layout type.
12338          */
12339         getType: function () {
12340             this.isLoaded();
12341             return this._data.type || "";
12342         },
12343 
12344         /**
12345          * Retrieve the media properties layout. This call will re-query the server and refresh the layout object.
12346          * @returns {finesse.restservices.MediaPropertiesLayout}
12347          *     This MediaPropertiesLayout object to allow cascading
12348          */
12349         get: function () {
12350             this._synchronize();
12351 
12352             return this; //Allow cascading
12353         },
12354 
12355         /**
12356          * Gets the data for this object.
12357          * 
12358          * Performs safe conversion from raw API data to ensure that the returned layout object
12359          * always has a header with correct entry fields, and exactly two columns with lists of entries.
12360          *
12361          * @returns {finesse.restservices.MediaPropertiesLayout.Object} Data in columns (unless only one defined).
12362          */
12363         getData: function () {
12364 
12365             var layout = this._data, result, _addColumnData;
12366 
12367             result = this.getEmptyData();
12368             result.name = layout.name;
12369             result.description = layout.description;
12370             result.type = layout.type;
12371 
12372             /**
12373              * @private
12374              */
12375             _addColumnData = function (entryData, colIndex) {
12376 
12377                 if (!entryData) {
12378                     //If there's no entry data at all, rewrite entryData to be an empty collection of entries
12379                     entryData = {};
12380                 } else if (entryData.mediaProperty) {
12381                     //If entryData contains the keys for a single entry rather than being a collection of entries,
12382                     //rewrite it to be a collection containing a single entry
12383                     entryData = { "": entryData };
12384                 }
12385 
12386                 //Add each of the entries in the list to the column
12387                 jQuery.each(entryData, function (i, entryData) {
12388 
12389                     //If the entry has no displayName specified, explicitly set it to the empty string
12390                     if (!entryData.displayName) {
12391                         entryData.displayName = "";
12392                     }
12393 
12394                     result.columns[colIndex].push(entryData);
12395 
12396                 });
12397 
12398             };
12399 
12400             //The header should only contain a single entry
12401             if (layout.header && layout.header.entry) {
12402 
12403                 //If the entry has no displayName specified, explicitly set it to the empty string
12404                 if (!layout.header.entry.displayName) {
12405                     layout.header.entry.displayName = "";
12406                 }
12407 
12408                 result.header = layout.header.entry;
12409 
12410             } else {
12411 
12412                 throw "MediaPropertiesLayout.getData() - Header does not contain an entry";
12413 
12414             }
12415 
12416             //If the column object contains an entry object that wasn't part of a list of entries,
12417             //it must be a single right-hand entry object (left-hand entry object would be part of a list.)
12418             //Force the entry object to be the 2nd element in an otherwise-empty list.
12419             if (layout.column && layout.column.entry) {
12420                 layout.column = [
12421                     null,
12422                     { "entry": layout.column.entry }
12423                 ];
12424             }
12425 
12426             if (layout.column && layout.column.length > 0 && layout.column.length <= 2) {
12427 
12428                 //Render left column entries
12429                 if (layout.column[0] && layout.column[0].entry) {
12430                     _addColumnData(layout.column[0].entry, 0);
12431                 }
12432 
12433                 //Render right column entries
12434                 if (layout.column[1] && layout.column[1].entry) {
12435                     _addColumnData(layout.column[1].entry, 1);
12436                 }
12437 
12438             }
12439 
12440             return result;
12441 
12442         },
12443 
12444         /**
12445          * @private
12446          * Empty/template version of getData().
12447          *
12448          * Used by getData(), and by callers of getData() in error cases.
12449          * @returns Empty/template version of getData()
12450          */
12451         getEmptyData: function () {
12452 
12453             return {
12454                 header : {
12455                     displayName: null,
12456                     mediaProperty: null
12457                 },
12458                 columns : [[], []]
12459             };
12460 
12461         },
12462 
12463         /**
12464          * Update the layout of this MediaPropertiesLayout
12465          * @param {Object} layout
12466          *      The object representation of the layout you are setting
12467          * @param {finesse.interfaces.RequestHandlers} handlers
12468          *      An object containing the handlers for the request
12469          * @returns {finesse.restservices.MediaPropertiesLayout}
12470          *      This MediaPropertiesLayout object to allow cascading
12471          * @private
12472          */
12473         update: function (newLayoutObject, handlers) {
12474             var contentBody = {};
12475 
12476             // Make sure type is kept the same
12477             newLayoutObject.type = this.getType();
12478 
12479             contentBody[this.getRestType()] = newLayoutObject;
12480 
12481             //Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
12482             handlers = handlers || {};
12483 
12484             this.restRequest(this.getRestUrl(), {
12485                 method: 'PUT',
12486                 success: handlers.success,
12487                 error: handlers.error,
12488                 content: contentBody
12489             });
12490 
12491             return this; // Allow cascading
12492         },
12493 
12494         /**
12495          * Create a new MediaPropertiesLayout object with the layout passed in
12496          * @param {Object} layout
12497          *      The object representation of the layout you are creating
12498          * @param {finesse.interfaces.RequestHandlers} handlers
12499          *      An object containing the handlers for the request
12500          * @returns {finesse.restservices.MediaPropertiesLayout}
12501          *      This MediaPropertiesLayout object to allow cascading
12502          * @private
12503          */
12504         add: function (layout, handlers) {
12505             var contentBody = {};
12506 
12507             contentBody[this.getRestType()] = layout;
12508 
12509             handlers = handlers || {};
12510 
12511             this.restRequest(this.getRestUrl(), {
12512                 method: 'POST',
12513                 success: handlers.success,
12514                 error: handlers.error,
12515                 content: contentBody
12516             });
12517 
12518             return this; // Allow cascading
12519         },
12520 
12521         /**
12522          * Delete this MediaPropertiesLayout
12523          * @param {finesse.interfaces.RequestHandlers} handlers
12524          *      An object containing the handlers for the request
12525          * @returns {finesse.restservices.MediaPropertiesLayout}
12526          *      This MediaPropertiesLayout object to allow cascading
12527          * @private
12528          */
12529         "delete": function (handlers) {
12530             handlers = handlers || {};
12531 
12532             this.restRequest(this.getRestUrl(), {
12533                 method: 'DELETE',
12534                 success: handlers.success,
12535                 error: handlers.error,
12536                 content: undefined
12537             });
12538 
12539             return this; // Allow cascading
12540         }
12541 
12542     });
12543     
12544     MediaPropertiesLayout.Object = /** @lends finesse.restservices.MediaPropertiesLayout.Object.prototype */ {
12545         /**
12546          * @class Format of MediaPropertiesLayout Object.<br>
12547          * Object { <ul>
12548          *      <li>header : { <ul>
12549          *          <li>dispayName {String} 
12550          *          <li>mediaProperty {String}</ul>}
12551          *      <li>columns : { <ul>
12552          *          <li>[ [] , [] ]
12553          *          </ul>
12554          *      where column arrays consists of the same Object format as header.<br>
12555          *          }</ul>
12556          *      }<br>         
12557          * @constructs
12558          */
12559         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
12560         
12561     };
12562 
12563 	window.finesse = window.finesse || {};
12564     window.finesse.restservices = window.finesse.restservices || {};
12565     window.finesse.restservices.MediaPropertiesLayout = MediaPropertiesLayout;
12566     
12567     return MediaPropertiesLayout;
12568 });
12569 
12570 /**
12571  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
12572  *
12573  * @requires MediaPropertiesLayout
12574  * @requires ClientServices
12575  * @requires finesse.FinesseBase
12576  * @requires finesse.restservices.RestBase
12577  */
12578 
12579 /** The following comment is to prevent jslint errors about 
12580  * using variables before they are defined.
12581  */
12582 /*global finesse*/
12583 
12584 /** @private */
12585 define('restservices/UserMediaPropertiesLayout',['restservices/MediaPropertiesLayout'], function (MediaPropertiesLayout) {
12586      var UserMediaPropertiesLayout = MediaPropertiesLayout.extend(/** @lends finesse.restservices.UserMediaPropertiesLayout.prototype */{
12587 
12588 		/**
12589 		 * @class
12590 		 * JavaScript representation of a UserMediaPropertiesLayout collection object. Also exposes
12591 		 * methods to operate on the object against the server.
12592 		 * 
12593 		 * @param {Object} options
12594 		 * An object with the following properties:<ul>
12595 		 *        <li><b>id:</b> The id of the object being constructed</li>
12596 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12597 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12598 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12599 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12600 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12601 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
12602 		 *            <li><b>content:</b> {String} Raw string of response</li>
12603 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
12604 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12605 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
12606 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
12607 		 *            </ul></li>
12608 		 *        </ul></li>
12609 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
12610 		 * @constructs
12611 		**/
12612 		init: function (options) {
12613 		    this._super(options);
12614 		},
12615 		
12616 		/**
12617 		 * @private
12618 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayout class.
12619 		 */
12620 		getRestClass: function () {
12621 		    return UserMediaPropertiesLayout;
12622 		},
12623 
12624         /**
12625          * Overrides the parent class.  Returns the url for the UserMediaPropertiesLayout resource
12626          */
12627         getRestUrl: function () {
12628             return ("/finesse/api/User/" + this.getId() + "/" + this.getRestType());
12629         },
12630 
12631         /**
12632          * @private
12633          * Override to throw an error because we cannot do an update on the User's
12634          * MediaPropertiesLayout node
12635          */
12636         update: function (layout, handlers) {
12637             throw new Error("update(): Cannot update layout for User's MediaPropertiesLayout");
12638         },
12639 
12640         /**
12641          * @private
12642          * Override to throw an error because we cannot create a new layout on the User's
12643          * MediaPropertiesLayout node
12644          */
12645         add: function (layout, handlers) {
12646             throw new Error("add(): Cannot create a new layout for User's MediaPropertiesLayout");
12647         },
12648 
12649         /**
12650          * @private
12651          * Override to throw an error because we cannot delete the layout on the User's
12652          * MediaPropertiesLayout node
12653          */
12654         "delete": function (layout, handlers) {
12655             throw new Error("delete(): Cannot delete the layout for User's MediaPropertiesLayout");
12656         }
12657 
12658     });
12659 	
12660 	window.finesse = window.finesse || {};
12661     window.finesse.restservices = window.finesse.restservices || {};
12662     window.finesse.restservices.UserMediaPropertiesLayout = UserMediaPropertiesLayout;
12663     
12664     return UserMediaPropertiesLayout;
12665 });
12666 
12667 /**
12668 * JavaScript representation of the Finesse mediaPropertiesLayouts collection
12669 * object which contains a list of mediaPropertiesLayout 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/MediaPropertiesLayouts',[
12681     'restservices/RestCollectionBase',
12682     'restservices/RestBase',
12683     'restservices/MediaPropertiesLayout'
12684 ],
12685 function (RestCollectionBase, RestBase, MediaPropertiesLayout) {
12686 
12687     var MediaPropertiesLayouts = RestCollectionBase.extend({
12688 
12689         /**
12690          * @class
12691          * JavaScript representation of a mediaPropertiesLayouts 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             this._super(options);
12715         },
12716 
12717         /**
12718          * @private
12719          * Gets the REST class for the current object - this is the mediaPropertiesLayouts class.
12720          */
12721         getRestClass: function () {
12722             return MediaPropertiesLayouts;
12723         },
12724 
12725         /**
12726          * @private
12727          * Gets the REST class for the objects that make up the collection. - this
12728          * is the mediaPropertiesLayout class.
12729          */
12730         getRestItemClass: function () {
12731             return MediaPropertiesLayout;
12732         },
12733 
12734         /**
12735          * @private
12736          * Gets the REST type for the current object - this is a "mediaPropertiesLayouts".
12737          */
12738         getRestType: function () {
12739             return "MediaPropertiesLayouts";
12740         },
12741 
12742         /**
12743          * @private
12744          * Gets the REST type for the objects that make up the collection - this is "mediaPropertiesLayouts".
12745          */
12746         getRestItemType: function () {
12747             return "MediaPropertiesLayout";
12748         },
12749 
12750         /**
12751          * @private
12752          * Override default to indicates that the collection supports making requests.
12753          */
12754         supportsRequests: true,
12755 
12756         /**
12757          * @private
12758          * Override default to indicates that the collection does not subscribe to its objects.
12759          */
12760         supportsRestItemSubscriptions: false,
12761 
12762         /**
12763          * @private
12764          * Retrieve the MediaPropertiesLayouts. This call will re-query the server and refresh the collection.
12765          *
12766          * @returns {finesse.restservices.MediaPropertiesLayouts}
12767          *     This MediaPropertiesLayouts object to allow cascading.
12768          */
12769         get: function () {
12770             // set loaded to false so it will rebuild the collection after the get
12771             this._loaded = false;
12772             // reset collection
12773             this._collection = {};
12774             // perform get
12775             this._synchronize();
12776             return this;
12777         }
12778     });
12779 
12780     window.finesse = window.finesse || {};
12781     window.finesse.restservices = window.finesse.restservices || {};
12782     window.finesse.restservices.MediaPropertiesLayouts = MediaPropertiesLayouts;
12783         
12784     return MediaPropertiesLayouts;
12785 });
12786 
12787 /**
12788  * JavaScript representation of the Finesse MediaPropertiesLayout object for a User
12789  *
12790  * @requires MediaPropertiesLayout
12791  * @requires ClientServices
12792  * @requires finesse.FinesseBase
12793  * @requires finesse.restservices.RestBase
12794  */
12795 
12796 /** The following comment is to prevent jslint errors about 
12797  * using variables before they are defined.
12798  */
12799 /*global finesse*/
12800 
12801 /** @private */
12802 define('restservices/UserMediaPropertiesLayouts',[
12803 	'restservices/MediaPropertiesLayouts',
12804 	'restservices/UserMediaPropertiesLayout'
12805 ],
12806 function (MediaPropertiesLayouts, UserMediaPropertiesLayout) {
12807      var UserMediaPropertiesLayouts = MediaPropertiesLayouts.extend(/** @lends finesse.restservices.UserMediaPropertiesLayouts.prototype */{
12808 
12809 		/**
12810 		 * @class
12811 		 * JavaScript representation of a UserMediaPropertiesLayouts collection object. Also exposes
12812 		 * methods to operate on the object against the server.
12813 		 * 
12814 		 * @param {Object} options
12815 		 * An object with the following properties:<ul>
12816 		 *        <li><b>id:</b> The id of the object being constructed</li>
12817 		 *        <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12818 		 *        <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12819 		 *        <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12820 		 *        <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12821 		 *        <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12822 		 *            <li><b>status:</b> {Number} The HTTP status code returned</li>
12823 		 *            <li><b>content:</b> {String} Raw string of response</li>
12824 		 *            <li><b>object:</b> {Object} Parsed object of response</li>
12825 		 *            <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12826 		 *                <li><b>errorType:</b> {String} Type of error that was caught</li>
12827 		 *                <li><b>errorMessage:</b> {String} Message associated with error</li>
12828 		 *            </ul></li>
12829 		 *        </ul></li>
12830 		 *        <li><b>parentObj: (optional)</b> The parent object</li></ul>
12831 		 * @constructs
12832 		**/
12833 		init: function (options) {
12834 		    this._super(options);
12835 		},
12836 		
12837 		/**
12838 		 * @private
12839 		 * Gets the REST class for the current object - this is the UserMediaPropertiesLayouts class.
12840 		 */
12841 		getRestClass: function () {
12842 		    return UserMediaPropertiesLayouts;
12843 		},
12844 
12845         /**
12846          * @private
12847          * Gets the REST class for the objects that make up the collection. - this
12848          * is the UserMediaPropertiesLayout class.
12849          */
12850 		getRestItemClass: function() {
12851 			return UserMediaPropertiesLayout;
12852 		}
12853     });
12854 	
12855 	window.finesse = window.finesse || {};
12856     window.finesse.restservices = window.finesse.restservices || {};
12857     window.finesse.restservices.UserMediaPropertiesLayouts = UserMediaPropertiesLayouts;
12858     
12859     return UserMediaPropertiesLayouts;
12860 });
12861 
12862 /**
12863  * JavaScript representation of the Finesse Dialog object for non-voice media.
12864  *
12865  * @requires finesse.restservices.DialogBase
12866  */
12867 
12868 /** @private */
12869 define('restservices/MediaDialog',[
12870         'restservices/DialogBase'
12871     ],
12872     function (DialogBase) {
12873         var MediaDialog = DialogBase.extend(/** @lends finesse.restservices.MediaDialog.prototype */{
12874 
12875             /**
12876              * @private
12877              *
12878              * Support requests so that applications can refresh non-voice dialogs when the media channel that the
12879              * dialog belongs to is interrupted. An event is not sent to update a dialog's actions when the media is
12880              * interrupted so a refresh is required so that the application can get an updated set of actions.
12881              */
12882             supportsRequests: true,
12883 
12884             /**
12885              * @class
12886              * A MediaDialog is an attempted connection between or among multiple participants,
12887              * for example, a chat or email.
12888              *
12889              * @augments finesse.restservices.DialogBase
12890              * @constructs
12891              */
12892             _fakeConstuctor: function () {
12893                 /* This is here to hide the real init constructor from the public docs */
12894             },
12895 
12896             /**
12897              * @private
12898              *
12899              * @param {Object} options
12900              *     An object with the following properties:<ul>
12901              *         <li><b>id:</b> The id of the object being constructed</li>
12902              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
12903              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
12904              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
12905              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
12906              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
12907              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
12908              *             <li><b>content:</b> {String} Raw string of response</li>
12909              *             <li><b>object:</b> {Object} Parsed object of response</li>
12910              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
12911              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
12912              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
12913              *             </ul></li>
12914              *         </ul></li>
12915              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
12916              **/
12917             init: function (options) {
12918                 this._super(options);
12919             },
12920 
12921             /**
12922              * @private
12923              * Gets the REST class for the current object - this is the MediaDialog class.
12924              * @returns {Object} The Dialog class.
12925              */
12926             getRestClass: function () {
12927                 return MediaDialog;
12928             },
12929 
12930             /**
12931              * Transfers a Media Dialog to the target specified
12932              * @param {String} target script selector
12933              *     The script selector to transfer the dialog.
12934              * @param {finesse.interfaces.RequestHandlers} handlers
12935              *     An object containing the handlers for the request
12936              */
12937             transfer: function(target, handlers) {
12938                 this.setTaskState(MediaDialog.TaskActions.TRANSFER, handlers, target);
12939             },
12940 
12941             /**
12942              * Set the state on a Media Dialog based on the action given.
12943              * @param {finesse.restservices.MediaDialog.TaskActions} action
12944              *     The action string indicating the action to invoke on a Media dialog.
12945              * @param {finesse.interfaces.RequestHandlers} handlers
12946              *     An object containing the handlers for the request
12947              * @param {String} target
12948              *     The target to transfer the dialog.  Pass null if not transfer
12949              *
12950              * @example
12951              *     _mediaDialog.setTaskState(finesse.restservices.MediaDialog.TaskActions.ACCEPT,
12952              *                               {
12953              *                                   success: _handleAcceptSuccess,
12954              *                                   error: _handleAcceptError
12955              *                               },
12956              *                               null);
12957              */
12958             setTaskState: function (state,handlers,target) {
12959                 this.isLoaded();
12960 
12961                 var contentBody = {};
12962                 contentBody[this.getRestType()] = {
12963                     "requestedAction": state,
12964                     "target": target
12965                 };
12966                 // (nonexistent) keys to be read as undefined
12967                 handlers = handlers || {};
12968                 this.restRequest(this.getRestUrl(), {
12969                     method: 'PUT',
12970                     success: handlers.success,
12971                     error: handlers.error,
12972                     content: contentBody
12973                 });
12974                 return this; // Allow cascading
12975             }
12976 
12977         });
12978 
12979         MediaDialog.TaskActions = /** @lends finesse.restservices.MediaDialog.TaskActions.prototype */ {
12980             /**
12981              * Accept an incoming task.
12982              */
12983             ACCEPT: "ACCEPT",
12984             /**
12985              * Start work on a task.
12986              */
12987             START : "START",
12988             /**
12989              * Pause work on an active task.
12990              */
12991             PAUSE: "PAUSE",
12992             /**
12993              * Resume work on a paused task.
12994              */
12995             RESUME : "RESUME",
12996             /**
12997              * Wrap up work for a task.
12998              */
12999             WRAP_UP : "WRAP_UP",
13000             /**
13001              * Transfer task to another target.
13002              */
13003             TRANSFER : "TRANSFER",
13004             /**
13005              * End a task.
13006              */
13007             CLOSE : "CLOSE",
13008             /**
13009              * @class Set of action constants for a Media Dialog.  These should be used for
13010              * {@link finesse.restservices.MediaDialog#setTaskState}.
13011              * @constructs
13012              */
13013             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
13014         };
13015 
13016 
13017 
13018         MediaDialog.States = /** @lends finesse.restservices.MediaDialog.States.prototype */ {
13019             /**
13020              * Indicates that the task has been offered to an agent.
13021              */
13022             OFFERED: "OFFERED",
13023             /**
13024              * Indicates that the user has started work on the task.
13025              */
13026             ACTIVE: "ACTIVE",
13027             /**
13028              * Indicates that the user has paused work on the task.
13029              */
13030             PAUSED: "PAUSED",
13031             /**
13032              * Indicates that the user is wrapping up the task.
13033              */
13034             WRAPPING_UP: "WRAPPING_UP",
13035             /**
13036              * Indicates that the task was interrupted.
13037              */
13038             INTERRUPTED: "INTERRUPTED",
13039             /**
13040              * Indicates that the task has ended.
13041              */
13042             CLOSED: "CLOSED",
13043             /**
13044              * Indicates that the user has accepted the task.
13045              */
13046             ACCEPTED: "ACCEPTED",
13047             /**
13048              * Finesse has recovered a task after a failure. It does not have enough information to build a complete set
13049              * of actions for the task so it only allows the user to end the task.
13050              */
13051             UNKNOWN: "UNKNOWN",
13052             /**
13053              * @class Possible Dialog State constants.
13054              * The State flow of a typical in-bound Dialog is as follows: OFFERED, ACCEPTED, ACTIVE, CLOSED.
13055              * @constructs
13056              */
13057             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
13058         };
13059 
13060         MediaDialog.ParticipantStates = MediaDialog.States;
13061 
13062         window.finesse = window.finesse || {};
13063         window.finesse.restservices = window.finesse.restservices || {};
13064         window.finesse.restservices.MediaDialog = MediaDialog;
13065 
13066 
13067         return MediaDialog;
13068     });
13069 
13070 /* Simple JavaScript Inheritance
13071  * By John Resig http://ejohn.org/
13072  * MIT Licensed.
13073  */
13074 // Inspired by base2 and Prototype
13075 define('restservices/../../thirdparty/Class',[], function () {
13076         var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
13077         // The base Class implementation (does nothing)
13078         /** @private */
13079         Class = function(){};
13080         
13081         // Create a new Class that inherits from this class
13082         /** @private */
13083         Class.extend = function(prop) {
13084           var _super = this.prototype;
13085           
13086           // Instantiate a base class (but only create the instance,
13087           // don't run the init constructor)
13088           initializing = true;
13089           var prototype = new this();
13090           initializing = false;
13091           
13092           // Copy the properties over onto the new prototype
13093           for (var name in prop) {
13094             // Check if we're overwriting an existing function
13095             prototype[name] = typeof prop[name] == "function" && 
13096               typeof _super[name] == "function" && fnTest.test(prop[name]) ?
13097               (function(name, fn){
13098                 return function() {
13099                   var tmp = this._super;
13100                   
13101                   // Add a new ._super() method that is the same method
13102                   // but on the super-class
13103                   this._super = _super[name];
13104                   
13105                   // The method only need to be bound temporarily, so we
13106                   // remove it when we're done executing
13107                   var ret = fn.apply(this, arguments);        
13108                   this._super = tmp;
13109                   
13110                   return ret;
13111                 };
13112               })(name, prop[name]) :
13113               prop[name];
13114           }
13115           
13116           // The dummy class constructor
13117           /** @private */
13118           function Class() {
13119             // All construction is actually done in the init method
13120             if ( !initializing && this.init )
13121               this.init.apply(this, arguments);
13122           }
13123           
13124           // Populate our constructed prototype object
13125           Class.prototype = prototype;
13126           
13127           // Enforce the constructor to be what we expect
13128           Class.prototype.constructor = Class;
13129 
13130           // And make this class extendable
13131           Class.extend = arguments.callee;
13132           
13133           return Class;
13134         };
13135     return Class;
13136 });
13137 
13138 /**
13139  * Class used to establish a Media/Dialogs subscription to be shared by MediaDialogs objects for non-voice
13140  * dialog events.
13141  *
13142  * @requires Class
13143  * @requires finesse.clientservices.ClientServices
13144  * @requires finesse.clientservices.Topics
13145  */
13146 /** @private */
13147 define('restservices/MediaDialogsSubscriptionManager',[
13148         "../../thirdparty/Class",
13149         "clientservices/ClientServices",
13150         "clientservices/Topics"
13151     ],
13152     function (Class, ClientServices, Topics) {
13153         var MediaDialogsSubscriptionManager = Class.extend(/** @lends finesse.restservices.MediaDialogsSubscriptionManager.prototype */{
13154 
13155             /**
13156              * Map used to track the MediaDialogs objects managed by this object.
13157              * @private
13158              */
13159             _mediaDialogsMap: {},
13160 
13161             /**
13162              * The regex used to match the source of BOSH/XMPP events. If an event matches this source, the event will
13163              * be processed by the subscription manager.
13164              * @private
13165              */
13166             _sourceRegEx: null,
13167 
13168             /**
13169              * The subscription ID/handle for Media/Dialogs events.
13170              * @private
13171              */
13172             _subscriptionId: null,
13173 
13174             _fakeConstuctor: function ()
13175             {
13176                 /* This is here to hide the real init constructor from the public docs */
13177             },
13178 
13179             /**
13180              * Create the regex used to match the source of BOSH/XMPP events. If an event matches this source, the event
13181              * will be processed by the subscription manager.
13182              *
13183              * @param {Object} restObj
13184              *     The restObj whose REST URL will be used as the base of the regex.
13185              *
13186              * @returns {RegExp}
13187              *     The regex used to match the source of XMPP events.
13188              * @private
13189              */
13190             _makeSourceRegEx: function(restObj)
13191             {
13192                 return new RegExp("^" + restObj.getRestUrl() + "/Media/[0-9]+/Dialogs$");
13193             },
13194 
13195             /**
13196              * Return the media ID associated with the update.
13197              *
13198              * @param {Object} update
13199              *     The content of the update event.
13200              *
13201              * @returns {String}
13202              *     The media ID associated with the update.
13203              * @private
13204              */
13205             _getMediaIdFromEventUpdate: function(update)
13206             {
13207                 var parts = update.object.Update.source.split("/");
13208                 return parts[MediaDialogsSubscriptionManager.MEDIA_ID_INDEX_IN_SOURCE];
13209             },
13210 
13211             /**
13212              * Handler for update events. This handler forwards the update to the MediaDialogs object associated with
13213              * the media ID carried in the update event.
13214              *
13215              * @param {Object} update
13216              *     The content of the update event.
13217              *
13218              * @private
13219              */
13220             _updateEventHandler: function(update)
13221             {
13222                 var mediaId = this._getMediaIdFromEventUpdate(update),
13223                     mediaDialogs = this._mediaDialogsMap[mediaId];
13224 
13225                 if ( mediaDialogs )
13226                 {
13227                     mediaDialogs._updateEventHandler(mediaDialogs, update);
13228                 }
13229             },
13230 
13231             /**
13232              * Return the media ID associated with the REST update.
13233              *
13234              * @param {Object} update
13235              *     The content of the REST update.
13236              *
13237              * @returns {String}
13238              *     The media ID associated with the update.
13239              * @private
13240              */
13241             _getMediaIdFromRestUpdate: function(update)
13242             {
13243                 return update.object.Update.data.dialog.mediaProperties.mediaId;
13244             },
13245 
13246             /**
13247              * Handler for REST updates. This handler forwards the update to the MediaDialogs object associated with
13248              * the media ID carried in the REST update.
13249              *
13250              * @param {Object} update
13251              *     The content of the REST update.
13252              *
13253              * @private
13254              */
13255             _processRestItemUpdate: function(update)
13256             {
13257                 var mediaId = this._getMediaIdFromRestUpdate(update),
13258                     mediaDialogs = this._mediaDialogsMap[mediaId];
13259 
13260                 if ( mediaDialogs )
13261                 {
13262                     mediaDialogs._processRestItemUpdate(update);
13263                 }
13264             },
13265 
13266             /**
13267              * Utility method to create a callback to be given to OpenAjax to invoke when a message
13268              * is published on the topic of our REST URL (also XEP-0060 node).
13269              * This needs to be its own defined method so that subclasses can have their own implementation.
13270              * @returns {Function} callback(update)
13271              *     The callback to be invoked when an update event is received. This callback will
13272              *     process the update by notifying the MediaDialogs object associated with the media ID in the update.
13273              *
13274              * @private
13275              */
13276             _createPubsubCallback: function ()
13277             {
13278                 var _this = this;
13279                 return function (update) {
13280                     //If the source of the update is our REST URL, this means the collection itself is modified
13281                     if (update.object.Update.source.match(_this._sourceRegEx)) {
13282                         _this._updateEventHandler(update);
13283                     } else {
13284                         //Otherwise, it is safe to assume that if we got an event on our topic, it must be a
13285                         //rest item update of one of our children that was published on our node (OpenAjax topic)
13286                         _this._processRestItemUpdate(update);
13287                     }
13288                 };
13289             },
13290 
13291             /**
13292              * Track the MediaDialogs object so that events and REST updates signalled to this subscription manager
13293              * can be forwarded to the given MediaDialogs object.
13294              * @param {finesse.restservices.MediaDialogs} mediaDialogs MediaDialogs object to be tracked by the
13295              *     subscription manager.
13296              * @private
13297              */
13298             _manage: function(mediaDialogs)
13299             {
13300                 this._mediaDialogsMap[mediaDialogs.getMedia().getMediaId()] = mediaDialogs;
13301             },
13302 
13303             /**
13304              * Stop tracking the MediaDialogs object. Events and REST updates signalled to this subscription manager
13305              * will no longer be forwarded to the given MediaDialogs object.
13306              * @param {finesse.restservices.MediaDialogs} mediaDialogs MediaDialogs object to no longer track.
13307              * @private
13308              */
13309             _unManage: function(mediaDialogs)
13310             {
13311                 var mediaId = mediaDialogs.getMedia().getMediaId();
13312                 if ( this._callbackMap[mediaId] )
13313                 {
13314                     delete this._callbackMap[mediaId];
13315                 }
13316             },
13317 
13318             /**
13319              * @class
13320              * An internal class used to establish a Media/Dialogs subscription to be shared by MediaDialogs objects
13321              * for non-voice dialog events.
13322              *
13323              * @constructor
13324              * @param {RestBase} restObj
13325              *     A RestBase object used to build the user portion of XMPP and REST paths.
13326              * @constructs
13327              * @private
13328              */
13329             init: function (restObj)
13330             {
13331                 var _this;
13332 
13333                 this._sourceRegEx = this._makeSourceRegEx(restObj);
13334             },
13335 
13336             /**
13337              * Create the BOSH/XMPP subscription used for non-voice dialog events. Additionally, store the given
13338              * MediaDialogs object so that events for the object can be forwarded to it.
13339              *
13340              * @param {finesse.restservices.MediaDialogs} mediaDialogs a MediaDialogs object to manage (forward events)
13341              * @param {Object} callbacks an object containing success and error callbacks used to signal the result of
13342              *     the subscription.
13343              * @returns {MediaDialogsSubscriptionManager}
13344              * @private
13345              */
13346             subscribe: function (mediaDialogs, callbacks)
13347             {
13348                 var topic = Topics.getTopic(mediaDialogs.getXMPPNodePath()),
13349                     _this = mediaDialogs,
13350                     handlers,
13351                     successful;
13352 
13353                 callbacks = callbacks || {};
13354 
13355                 handlers = {
13356                     /** @private */
13357                     success: function () {
13358                         // Add item to the refresh list in ClientServices to refresh if
13359                         // we recover due to our resilient connection.
13360                         ClientServices.addToRefreshList(_this);
13361                         if (typeof callbacks.success === "function") {
13362                             callbacks.success();
13363                         }
13364                     },
13365                     /** @private */
13366                     error: function (err) {
13367                         if (successful) {
13368                             _this._unManage(mediaDialogs);
13369                             ClientServices.unsubscribe(topic);
13370                         }
13371 
13372                         if (typeof callbacks.error === "function") {
13373                             callbacks.error(err);
13374                         }
13375                     }
13376                 };
13377 
13378                 this._manage(mediaDialogs);
13379                 if ( this._subscriptionId )
13380                 {
13381                     successful = true;
13382                 }
13383                 else
13384                 {
13385                     successful = ClientServices.subscribe(topic, this._createPubsubCallback(), true);
13386                     if ( successful )
13387                     {
13388                         this._subscriptionId = "OpenAjaxOnly";
13389                     }
13390                 }
13391 
13392                 if (successful) {
13393                     handlers.success();
13394                 } else {
13395                     handlers.error();
13396                 }
13397 
13398                 return this;
13399             }
13400         });
13401 
13402         MediaDialogsSubscriptionManager.MEDIA_ID_INDEX_IN_SOURCE = 6;
13403 
13404         window.finesse = window.finesse || {};
13405         window.finesse.restservices = window.finesse.restservices || {};
13406         window.finesse.restservices.MediaDialogsSubscriptionManager = MediaDialogsSubscriptionManager;
13407 
13408         return MediaDialogsSubscriptionManager;
13409     });
13410 
13411 /**
13412  * JavaScript representation of the Finesse MediaDialogs collection
13413  * object which contains a list of Dialog objects.
13414  *
13415  * @requires finesse.clientservices.ClientServices
13416  * @requires Class
13417  * @requires finesse.FinesseBase
13418  * @requires finesse.restservices.RestBase
13419  * @requires finesse.restservices.Dialogs
13420  * @requires finesse.restservices.MediaDialogsSubscriptionManager
13421  */
13422 /** @private */
13423 define('restservices/MediaDialogs',[
13424     'restservices/RestCollectionBase',
13425     'restservices/RestBase',
13426     'restservices/Dialogs',
13427     'restservices/MediaDialog',
13428     'restservices/MediaDialogsSubscriptionManager'
13429 ],
13430 function (RestCollectionBase, RestBase, Dialogs, MediaDialog, MediaDialogsSubscriptionManager) {
13431     var MediaDialogs = Dialogs.extend(/** @lends finesse.restservices.MediaDialogs.prototype */{
13432 
13433         /**
13434          * @class
13435          * JavaScript representation of a collection of Dialogs for a specific non-voice Media.
13436          * @augments finesse.restservices.Dialogs
13437          * @constructs
13438          * @see finesse.restservices.Dialog
13439          * @example
13440          *  _MediaDialogs = _media.getMediaDialogs( {
13441          *      onCollectionAdd : _handleDialogAdd,
13442          *      onCollectionDelete : _handleDialogDelete,
13443          *      onLoad : _handleMediaDialogsLoaded
13444          *  });
13445          *  
13446          * _dialogCollection = _MediaDialogs.getCollection();
13447          * for (var dialogId in _dialogCollection) {
13448          *     if (_dialogCollection.hasOwnProperty(dialogId)) {
13449          *         _dialog = _dialogCollection[dialogId];
13450          *         etc...
13451          *     }
13452          * }
13453          */
13454         _fakeConstuctor: function () {
13455             /* This is here to hide the real init constructor from the public docs */
13456         },
13457         
13458         /**
13459          * @private
13460          * @param {Object} options
13461          *     An object with the following properties:<ul>
13462          *         <li><b>id:</b> The id of the object being constructed</li>
13463          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13464          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13465          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13466          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13467          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13468          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13469          *             <li><b>content:</b> {String} Raw string of response</li>
13470          *             <li><b>object:</b> {Object} Parsed object of response</li>
13471          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13472          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13473          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13474          *             </ul></li>
13475          *         </ul></li>
13476          *         <li><b>parentObj:</b> The parent object</li></ul>
13477          *         <li><b>mediaObj:</b> The media object</li></ul>
13478          **/
13479         init: function (options) {
13480             this._mediaObj = options.mediaObj;
13481             this._super(options);
13482         },
13483 
13484         /**
13485          * Returns the associated Media object.
13486          * @returns {finesse.restservices.Media} the associated media object.
13487          */
13488         getMedia: function() {
13489             return this._mediaObj;
13490         },
13491 
13492         /**
13493          * @private
13494          * Gets the REST class for the objects that make up the collection. - this
13495          * is the Dialog class.
13496          */
13497         getRestItemClass: function () {
13498             return MediaDialog;
13499         },
13500 
13501         /**
13502          * @private
13503          * Gets the node path for the current object - this is the media node
13504          * @returns {String} The node path
13505          */
13506         getXMPPNodePath: function () {
13507             var
13508                 restObj = this._restObj,
13509                 nodePath = "";
13510 
13511             //Prepend the base REST object if one was provided.
13512             if (restObj instanceof RestBase) {
13513                 nodePath += restObj.getRestUrl();
13514             }
13515             //Otherwise prepend with the default webapp name.
13516             else {
13517                 nodePath += "/finesse/api";
13518             }
13519             
13520             //Append the REST type.
13521             nodePath += "/" + this.getRestType() + "/Media";
13522             return nodePath;
13523         },
13524         
13525         /**
13526          * The REST URL in which this object can be referenced.
13527          * @return {String}
13528          *     The REST URI for this object.
13529          * @private
13530          */
13531         getRestUrl: function () {
13532             var
13533             restObj = this._mediaObj,
13534             restUrl = "";
13535 
13536             //Prepend the base REST object if one was provided.
13537             if (restObj instanceof RestBase) {
13538                 restUrl += restObj.getRestUrl();
13539             }
13540             //Otherwise prepend with the default webapp name.
13541             else {
13542                 restUrl += "/finesse/api";
13543             }
13544 
13545             //Append the REST type.
13546             restUrl += "/" + this.getRestType();
13547 
13548             //Append ID if it is not undefined, null, or empty.
13549             if (this._id) {
13550                 restUrl += "/" + this._id;
13551             }
13552             return restUrl;
13553         },
13554 
13555         /**
13556          * Overridden so that MediaDialogsSubscriptionManager can be used to share events across media dialogs.
13557          *
13558          * @param {Object} callbacks
13559          *     An object containing success and error handlers for the subscription request.
13560          * @private
13561          */
13562         subscribe: function (callbacks)
13563         {
13564             if ( !MediaDialogs.subscriptionManager )
13565             {
13566                 MediaDialogs.subscriptionManager = new MediaDialogsSubscriptionManager(this._restObj);
13567             }
13568 
13569             MediaDialogs.subscriptionManager.subscribe(this, callbacks);
13570 
13571             return this;
13572         }
13573     });
13574 
13575     MediaDialogs.subscriptionManager = /** @lends finesse.restservices.MediaDialogsSubscriptionManager.prototype */ null;
13576 
13577     window.finesse = window.finesse || {};
13578     window.finesse.restservices = window.finesse.restservices || {};
13579     window.finesse.restservices.MediaDialogs = MediaDialogs;
13580     
13581     return MediaDialogs;
13582 });
13583 
13584 /**
13585  * Utility class used to recover a media object after recovering from a connection or system failure.
13586  *
13587  * @requires Class
13588  * @requires finesse.restservices.Notifier
13589  * @requires finesse.clientservices.ClientServices
13590  * @requires finesse.restservices.Media
13591  */
13592 
13593 /** @private */
13594 define('restservices/MediaOptionsHelper',['../../thirdparty/Class',
13595         '../utilities/Utilities',
13596         '../clientservices/ClientServices',
13597         '../cslogger/ClientLogger'
13598 ],
13599 function (Class, Utilities, ClientServices, ClientLogger)
13600 {
13601     /**
13602      * Utility class used to synchronize media login options after recovering from a connection or system failure. This
13603      * class will ensure that the Finesse server that the application fails over to has the same maxDialogLimit,
13604      * interruptAction, and dialogLogoutAction as the previous Finesse server.
13605      */
13606     var MediaOptionsHelper = Class.extend(/** @lends finesse.restservices.MediaOptionsHelper.prototype */
13607     {
13608         /**
13609          * @private
13610          *
13611          * The media that this helper is responsible for recovering in case of failover.
13612          */
13613         _media: null,
13614 
13615         /**
13616          * @private
13617          *
13618          * The media options that this helper will ensure are set properly across failures.
13619          */
13620         _mediaOptions: null,
13621 
13622         /**
13623          * @private
13624          *
13625          * The current state of the failover recovery.
13626          */
13627         _state: null,
13628 
13629         /**
13630          * @class
13631          *
13632          * Utility class used to synchronize media login options after recovering from a connection or system failure. This
13633          * class will ensure that the Finesse server that the application fails over to has the same maxDialogLimit,
13634          * interruptAction, and dialogLogoutAction as the previous Finesse server.
13635          *
13636          * @constructs
13637          */
13638         _fakeConstuctor: function ()
13639         {
13640             /* This is here to hide the real init constructor from the public docs */
13641         },
13642 
13643         /**
13644          * Utility method to format a message logged by an instance of this class.
13645          *
13646          * @param {string} message the message to format
13647          * @returns {string} the given message prefixed with the name of this class and the ID of the Media object
13648          *      associated with this class.
13649          * @private
13650          */
13651         _formatLogMessage: function(message)
13652         {
13653             return "MediaOptionsHelper[" + this.media.getMediaId() + "]: " + message;
13654         },
13655 
13656         /**
13657          * Utility method to log an informational message.
13658          *
13659          * Note that this method piggy-backs on the logger setup by the gadget. If the gadget does not initialize
13660          * logger, this class will not log.
13661          *
13662          * @param {string} message the message to log
13663          * @private
13664          */
13665         _log: function(message)
13666         {
13667             ClientLogger.log(this._formatLogMessage(message));
13668         },
13669 
13670         /**
13671          * Utility method to log an error message.
13672          *
13673          * Note that this method piggy-backs on the logger setup by the gadget. If the gadget does not initialize
13674          * logger, this class will not log.
13675          *
13676          * @param {string} message the message to log
13677          * @private
13678          */
13679         _error: function(message)
13680         {
13681             ClientLogger.error(this._formatLogMessage(message));
13682         },
13683 
13684         /**
13685          * @private
13686          *
13687          * Set the running state of this failover helper.
13688          *
13689          * @param {String} newState the new state of the failover helper.
13690          */
13691         _setState: function(newState)
13692         {
13693             this._state = newState;
13694             this._log("changed state to " + this._state);
13695         },
13696 
13697         /**
13698          * Check the given media object to see if the maxDialogLimit, interruptAction, and dialogLogoutAction options
13699          * need to be reset. These options need to be reset if the application specified login options and any of the
13700          * following conditions are true:<ul>
13701          *     <li>the dialogLogoutAction in the given media object does not match the action set by the application</li>
13702          *     <li>the interruptAction in the given media object does not match the action set by the application</li>
13703          *     <li>the maxDialogLimit in the given media object does not match the limit set by the application</li></ul>
13704          *
13705          * @param {Object} media the media object to evaluate
13706          * @returns {*|{}|boolean} true if a login request should be sent to correct the media options
13707          * @private
13708          */
13709         _shouldLoginToFixOptions: function(media)
13710         {
13711             return this._mediaOptions
13712                 && media.isLoggedIn()
13713                 && (media.getDialogLogoutAction() !== this._mediaOptions.dialogLogoutAction
13714                     || media.getInterruptAction() !== this._mediaOptions.interruptAction
13715                     || media.getMaxDialogLimit() !== this._mediaOptions.maxDialogLimit);
13716         },
13717 
13718         /**
13719          * @private
13720          *
13721          * Determine if the given response is an "agent already logged in" error.
13722          *
13723          * @param {Object} response the response to evaluate
13724          *
13725          * @returns {boolean} true if
13726          */
13727         _agentIsAlreadyLoggedIn: function(response)
13728         {
13729             return response
13730                 && response.object
13731                 && response.object.ApiErrors
13732                 && response.object.ApiErrors.ApiError
13733                 && response.object.ApiErrors.ApiError.ErrorMessage === "E_ARM_STAT_AGENT_ALREADY_LOGGED_IN";
13734         },
13735 
13736         /**
13737          * Determine if the given response to a media login request is successful. A response is successful under these
13738          * conditions:<ul>
13739          *     <li>the response has a 202 status</li>
13740          *     <li>the response has a 400 status and the error indicates the agent is already logged in</li>
13741          *     </ul>
13742          *
13743          * @param {Object} loginResponse the response to evaluate
13744          *
13745          * @returns {*|boolean} true if the response status is 202 or if the response status is 400 and the error states
13746          *      that the agent is already logged in.
13747          * @private
13748          */
13749         _isSuccessfulLoginResponse: function(loginResponse)
13750         {
13751             return loginResponse && ((loginResponse.status === 202) || this._agentIsAlreadyLoggedIn(loginResponse));
13752         },
13753 
13754         /**
13755          * Process a media load or change while in the connected state. This involves checking the media options to
13756          * ensure they are the same as those set by the application.
13757          *
13758          * @param {Object} media the media object that was loaded or changed.
13759          * @private
13760          */
13761         _processConnectedState: function(media)
13762         {
13763             var _this = this, processResponse;
13764 
13765             if ( this._shouldLoginToFixOptions(media) )
13766             {
13767                 processResponse = function(response)
13768                 {
13769                     _this._setState(MediaOptionsHelper.States.MONITORING_OPTIONS);
13770 
13771                     if ( !_this._isSuccessfulLoginResponse(response) )
13772                     {
13773                         _this._error("failed to reset options: " + response.status + ": " + response.content);
13774                     }
13775                 };
13776 
13777                 this._setState(MediaOptionsHelper.States.SETTING_OPTIONS);
13778 
13779                 this._log("logging in to fix options");
13780 
13781                 this.media.login({
13782                     dialogLogoutAction: _this._mediaOptions.dialogLogoutAction,
13783                     interruptAction: _this._mediaOptions.interruptAction,
13784                     maxDialogLimit: _this._mediaOptions.maxDialogLimit,
13785                     handlers: {
13786                         success: processResponse,
13787                         error: processResponse
13788                     }
13789                 });
13790             }
13791         },
13792 
13793         /**
13794          * Process a media load or change while in the resetting options state. All that is done in this state is log a
13795          * message that a reset is already in progress.
13796          *
13797          * @param {Object} media the media object that was loaded or changed.
13798          * @private
13799          */
13800         _processResettingOptionsState: function(media)
13801         {
13802             this._log("Resetting options is in progress");
13803         },
13804 
13805         /**
13806          * Initialize a helper class used to recover media objects following connectivity or component failures related
13807          * to Finesse and/or CCE services.
13808          *
13809          * Initialize the failover helper to manage the recovery of the given media object.
13810          *
13811          * @param {Object} media the media object to recover
13812          * @param {Object} mediaOptions an object containing the media options used by the application:<ul>
13813          *         <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
13814          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
13815          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
13816          */
13817         init: function (media, mediaOptions)
13818         {
13819             var _this = this, processMediaStateChange = function(media)
13820             {
13821                 switch ( _this._state )
13822                 {
13823                     case MediaOptionsHelper.States.MONITORING_OPTIONS:
13824                         _this._processConnectedState(media);
13825                         break;
13826                     case MediaOptionsHelper.States.SETTING_OPTIONS:
13827                         _this._processResettingOptionsState(media);
13828                         break;
13829                     default:
13830                         _this._error("unexpected state: " + _this.state);
13831                         break;
13832                 }
13833             };
13834 
13835             this.media = media;
13836 
13837             this._mediaOptions = mediaOptions || {};
13838 
13839             // The maxDialogLimit is a string in media events. Ensure _mediaOptions.maxDialogLimit is a string to
13840             // make sure it can be compared to the maxDialogLimit field in media events.
13841             //
13842             if ( this._mediaOptions.maxDialogLimit )
13843             {
13844                 this._mediaOptions.maxDialogLimit = this._mediaOptions.maxDialogLimit.toString();
13845             }
13846 
13847             // Add the media object to the refresh list so that ClientServices calls refresh on the media object when
13848             // the connection is reestablished. This will trigger processMediaStateChange() which will trigger a login
13849             // to restore media options if media options are different.
13850             //
13851             ClientServices.addToRefreshList(this.media);
13852 
13853             this._setState(MediaOptionsHelper.States.MONITORING_OPTIONS);
13854 
13855             this.media.addHandler('load', processMediaStateChange);
13856             this.media.addHandler('change', processMediaStateChange);
13857 
13858             this._log("initialized");
13859         }
13860     });
13861 
13862     /**
13863      * @private
13864      *
13865      * The states that a running MediaOptionsHelper executes.
13866      *
13867      * @type {{CONNECTED: string, RECOVERING: string, RECOVERY_LOGIN: string, _fakeConstructor: _fakeConstructor}}
13868      */
13869     MediaOptionsHelper.States = /** @lends finesse.restservices.MediaOptionsHelper.States.prototype */ {
13870 
13871         /**
13872          * The media is synchronized with the Finesse server. Media options are being monitored for changes.
13873          */
13874         MONITORING_OPTIONS: "MONITORING_OPTIONS",
13875 
13876         /**
13877          * A media login request has been sent to Finesse to set the correct media options.
13878          */
13879         SETTING_OPTIONS: "SETTING_OPTIONS",
13880 
13881         /**
13882          * @class Possible MediaOptionsHelper state values.
13883          * @constructs
13884          */
13885         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
13886     };
13887 
13888     window.finesse = window.finesse || {};
13889     window.finesse.restservices = window.finesse.restservices || {};
13890     window.finesse.restservices.MediaOptionsHelper = MediaOptionsHelper;
13891 
13892     return MediaOptionsHelper;
13893 });
13894 /**
13895  * JavaScript representation of the Finesse Media object
13896  *
13897  * @requires finesse.clientservices.ClientServices
13898  * @requires Class
13899  * @requires finesse.FinesseBase
13900  * @requires finesse.restservices.RestBase
13901  * @requires finesse.restservices.MediaDialogs
13902  * @requires finesse.restservices.MediaOptionsHelper
13903  */
13904 
13905 /** @private */
13906 define('restservices/Media',[
13907     'restservices/RestBase',
13908     'restservices/MediaDialogs',
13909     'restservices/MediaOptionsHelper'
13910 ],
13911 function (RestBase, MediaDialogs, MediaOptionsHelper) {
13912     var Media = RestBase.extend(/** @lends finesse.restservices.Media.prototype */{
13913 
13914         /**
13915          * @private
13916          * Media objects support GET REST requests.
13917          */
13918         supportsRequests: true,
13919 
13920         /**
13921          * @private
13922          * The list of dialogs associated with this media.
13923          */
13924         _mdialogs : null,
13925 
13926         /**
13927          * @private
13928          * The options used to log into this media.
13929          */
13930         _mediaOptions: null,
13931 
13932         /**
13933          * @private
13934          * Object used to keep the maxDialogLimit, interruptAction, and dialogLogoutAction in-synch across fail-overs.
13935          */
13936         _optionsHelper: null,
13937 
13938         /**
13939          * @class
13940          * A Media represents a non-voice channel,
13941          * for example, a chat or a email.
13942          *
13943          * @augments finesse.restservices.RestBase
13944          * @constructs
13945          */
13946         _fakeConstuctor: function () {
13947             /* This is here to hide the real init constructor from the public docs */
13948         },
13949 
13950         /**
13951          * @private
13952          *
13953          * @param {Object} options
13954          *     An object with the following properties:<ul>
13955          *         <li><b>id:</b> The id of the object being constructed</li>
13956          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
13957          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
13958          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
13959          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
13960          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
13961          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
13962          *             <li><b>content:</b> {String} Raw string of response</li>
13963          *             <li><b>object:</b> {Object} Parsed object of response</li>
13964          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
13965          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
13966          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
13967          *             </ul></li>
13968          *         </ul></li>
13969          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
13970          **/
13971         init: function (options) {
13972             this._super(options);
13973         },
13974 
13975         /**
13976          * @private
13977          * Utility method used to retrieve an attribute from this media object's underlying data.
13978          *
13979          * @param {String} attributeName the name of the attribute to retrieve
13980          * @returns {String} the value of the attribute or undefined if the attribute is not found
13981          */
13982         _getDataAttribute: function(attributeName) {
13983             this.isLoaded();
13984             return this.getData()[attributeName];
13985         },
13986 
13987         /**
13988          * @private
13989          * Gets the REST class for the current object - this is the Media class.
13990          * @returns {Object} The Media class.
13991          */
13992         getRestClass: function () {
13993             return Media;
13994         },
13995         
13996         /**
13997          * @private
13998          * Gets the REST type for the current object - this is a "Media".
13999          * @returns {String} The Media string.
14000          */
14001         getRestType: function () {
14002             return "Media";
14003         },
14004 
14005         /**
14006          * @private
14007          * Getter for the uri.
14008          * @returns {String} The uri.
14009          */
14010         getMediaUri: function () {
14011             return this._getDataAttribute('uri');
14012         },
14013 
14014         /**
14015          * Returns the id.
14016          * @returns {String} The id.
14017          */
14018         getId: function () {
14019             return this._getDataAttribute('id');
14020         },
14021 
14022         /**
14023          * Returns the name.
14024          * @returns {String} The name.
14025          */
14026         getName: function () {
14027             return this._getDataAttribute('name');
14028         },
14029 
14030         /**
14031          * Returns the reason code id.
14032          * @returns {String} The reason code id.
14033          */
14034         getReasonCodeId: function () {
14035             return this._getDataAttribute('reasonCodeId');
14036         },
14037 
14038         /**
14039          * Returns the reason code label.
14040          * @returns {String} The reason code label.
14041          */
14042         getReasonCodeLabel: function() {
14043             this.isLoaded();
14044             if (this.getData().reasonCode) {
14045                 return this.getData.reasonCode.label;
14046             }
14047             return "";
14048         },
14049 
14050         /**
14051          * Returns the action to be taken in the event this media is interrupted. The action will be one of the
14052          * following:<ul>
14053          *     <li><b>ACCEPT:</b> the interrupt will be accepted and the agent will not work on tasks in this media
14054          *     until the media is no longer interrupted.</li>
14055          *     <li><b>IGNORE:</b> the interrupt will be ignored and the agent is allowed to work on the task while the
14056          *     media is interrupted.</li></ul>
14057          * @returns {*|Object}
14058          */
14059         getInterruptAction: function() {
14060             return this._getDataAttribute('interruptAction');
14061         },
14062 
14063         /**
14064          * Returns the action to be taken in the event the agent logs out with dialogs associated with this media.
14065          * The action will be one of the following:<ul>
14066          *     <li><b>CLOSE:</b> the dialog will be closed.</li>
14067          *     <li><b>TRANSFER:</b> the dialog will be transferred to another agent.</li></ul>
14068          * @returns {*|Object}
14069          */
14070         getDialogLogoutAction: function() {
14071             return this._getDataAttribute('dialogLogoutAction');
14072         },
14073 
14074         /**
14075          * Returns the state of the User on this Media.
14076          * @returns {String}
14077          *     The current (or last fetched) state of the User on this Media
14078          * @see finesse.restservices.Media.States
14079          */
14080         getState: function() {
14081             return this._getDataAttribute('state');
14082         },
14083 
14084         /**
14085          * Returns the Media id
14086          * @returns {String} The Media id
14087          */
14088         getMediaId: function() {
14089             return this._getDataAttribute('id');
14090         },
14091 
14092         /**
14093          * Returns maximum number of dialogs allowed on this Media
14094          * @returns {String} The maximum number of Dialogs on this Media
14095          */
14096         getMaxDialogLimit: function() {
14097             return this._getDataAttribute('maxDialogLimit');
14098         },
14099 
14100         /**
14101          * Returns true if this media is interruptible
14102          * @returns {Boolean} true if interruptible; false otherwise
14103          */
14104         getInterruptible: function() {
14105             var interruptible = this._getDataAttribute('interruptible');
14106             return interruptible === 'true';
14107         },
14108 
14109         /**
14110          * Returns true if the user interruptible on this Media.
14111          * @returns {Boolean} true if interruptible; false otherwise
14112          */
14113         isInterruptible: function() {
14114             return this.getInterruptible();
14115         },
14116 
14117         /**
14118          * Returns true if the user is routable on this Media
14119          * @returns {Boolean} true if routable, false otherwise
14120          */
14121         getRoutable: function() {
14122             var routable = this._getDataAttribute('routable');
14123             return routable === 'true';
14124         },
14125 
14126         /**
14127          * Returns true if the user is routable on this Media.
14128          * @returns {Boolean} true if routable, false otherwise
14129          */
14130         isRoutable: function() {
14131             return this.getRoutable();
14132         },
14133 
14134         /**
14135          * Sets the routable status of this media
14136          * .
14137          * @param {Object} options
14138          *     An object with the following properties:<ul>
14139          *         <li><b>routable:</b> true if the agent is routable, false otherwise</li>
14140          *         <li><b>{@link finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
14141          * @returns {finesse.restservices.Media}
14142          *     This Media object, to allow cascading
14143          */
14144         setRoutable: function(params) {
14145             var handlers, contentBody = {},
14146                 restType = this.getRestType(),
14147                 url = this.getRestUrl();
14148             params = params || {};
14149 
14150             contentBody[restType] = {
14151                 "routable": params.routable
14152             };
14153 
14154             handlers = params.handlers || {};
14155 
14156             this._makeRequest(contentBody, url, handlers);
14157 
14158             return this;
14159         },
14160 
14161         /**
14162          * @private
14163          * Invoke a request to the server given a content body and handlers.
14164          *
14165          * @param {Object} contentBody
14166          *     A JS object containing the body of the action request.
14167          * @param {finesse.interfaces.RequestHandlers} handlers
14168          *     An object containing the handlers for the request
14169          */
14170         _makeRequest: function (contentBody, url, handlers) {
14171             // Protect against null dereferencing of options allowing its
14172             // (nonexistent) keys to be read as undefined
14173             handlers = handlers || {};
14174 
14175             this.restRequest(url, {
14176                 method: 'PUT',
14177                 success: handlers.success,
14178                 error: handlers.error,
14179                 content: contentBody
14180             });
14181         },
14182 
14183         /**
14184          * Return true if the params object contains one of the following:<ul>
14185          *     <li>maxDialogLimit</li>
14186          *     <li>interruptAction</li>
14187          *     <li>dialogLogoutAction</li></ul>
14188          *
14189          * @param {Object} params the parameters to evaluate
14190          * @returns {*|Boolean}
14191          * @private
14192          */
14193         _containsLoginOptions: function(params) {
14194             return params.maxDialogLimit || params.interruptAction || params.dialogLogoutAction;
14195         },
14196 
14197         /**
14198          * Create login parameters using the given algorithm:<ul>
14199          *     <li>if loginOptions have not be set in the call to MediaList.getMedia(), use the params given by the application</li>
14200          *     <li>if no params were set by the application, use the loginOptions set in the call to MediaList.getMedia()</li>
14201          *     <li>if the parameters given by the application contains login options, use the parameters given by the application</li>
14202          *     <li>if login options were given by the application but callbacks were given, create new login params with the
14203          *     loginOptions set in the call to MediaList.getMedia() and the callbacks specified in the given login params</li></ul>
14204          *
14205          * @param params the parameters specified by the application in the call to Media.login()
14206          *
14207          * @see finesse.restservices.Media#login
14208          * @see finesse.restservices.MediaList#getMedia
14209          *
14210          * @returns {Object} login parameters built based on the algorithm listed above.
14211          * @private
14212          */
14213         _makeLoginOptions: function(params) {
14214             if ( !this._mediaOptions ) {
14215                 // If loginOptions have not be set, use the params given by the application.
14216                 //
14217                 return params;
14218             }
14219 
14220             if ( !params ) {
14221                 // If there were no params given by the application, use the loginOptions set in the call to
14222                 // MediaList.getMedia().
14223                 //
14224                 return this._mediaOptions;
14225             }
14226 
14227             if (  this._containsLoginOptions(params) ) {
14228                 // if the parameters given by the application contains login options, use the parameters given by the
14229                 // application.
14230                 //
14231                 return params;
14232             }
14233 
14234             // If login options were given by the application but callbacks were given, create new login params with the
14235             // loginOptions set in the call to MediaList.getMedia() and the callbacks specified in the given login
14236             // params.
14237             //
14238             return {
14239                 maxDialogLimit: this._mediaOptions.maxDialogLimit,
14240                 interruptAction: this._mediaOptions.interruptAction,
14241                 dialogLogoutAction: this._mediaOptions.dialogLogoutAction,
14242                 handlers: params.handlers
14243             };
14244         },
14245 
14246         /**
14247          * Ensure that the maxDialogLimit, interruptAction, and dialogLogoutAction options are kept in synch across
14248          * fail-overs for this media object.
14249          * @private
14250          */
14251         _ensureOptionsAreInSynch: function() {
14252             if ( !this._optionsHelper && this._mediaOptions ) {
14253                 this._optionsHelper = new MediaOptionsHelper(this, this._mediaOptions);
14254             }
14255         },
14256 
14257         /**
14258          * Log the agent into this media.
14259          *
14260          * @param {Object} params
14261          *     An object with the following properties:<ul>
14262          *         <li><b>maxDialogLimit:</b>The maximum number of tasks that is allowed to handle concurrently</li>
14263          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
14264          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li>
14265          *         <li><b>{@link finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
14266          *
14267          *     If maxDialogLimit, interruptAction, and dialogLogoutAction are not present, loginOptions specified in the
14268          *     call to finesse.restservices.MediaList.getMedia() will be used.
14269          *
14270          * @see finesse.restservices.MediaList#getMedia
14271          *
14272          * @returns {finesse.restservices.Media}
14273          *     This Media object, to allow cascading
14274          */
14275         login: function(params) {
14276             this.setState(Media.States.LOGIN, null, this._makeLoginOptions(params));
14277             return this; // Allow cascading
14278         },
14279 
14280         /**
14281          * Perform a logout for a user on this media.
14282          * 
14283          * @param {String} reasonCode
14284          *     The reason code for this user to logging out of this media.  Pass null for no reason.
14285          * @param {Object} params
14286          *     An object with the following properties:<ul>
14287          *         <li><b>{@link finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
14288          * 
14289          * @returns {finesse.restservices.Media}
14290          *     This Media object, to allow cascading
14291          */
14292         logout: function(reasonCode, params) {
14293             var state = Media.States.LOGOUT;
14294             return this.setState(state, reasonCode, params);
14295         },
14296 
14297         /**
14298          * Set the state of the user on this Media.
14299          * 
14300          * @param {String} newState
14301          *     The new state to be set.
14302          * @param {ReasonCode} reasonCode
14303          *     The reason code for the state change for this media. Pass null for no reason.
14304          * @param {Object} params
14305          *     An object with the following properties:<ul>
14306          *         <li><b>maxDialogLimit:</b>The maximum number of tasks that is allowed to handle concurrently</li>
14307          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
14308          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li>
14309          *         <li><b>{@link finesse.interfaces.RequestHandlers} handlers:</b> An object containing the handlers for the request</li></ul>
14310          *
14311          * @see finesse.restservices.User.States
14312          * @returns {finesse.restservices.Media}
14313          *     This Media object, to allow cascading
14314          * @example
14315          *     _media.setState(finesse.restservices.Media.States.NOT_READY, 
14316          *         {
14317          *             id: _reasonCodeId
14318          *         },
14319          *         {
14320          *             handlers: {
14321          *                 success: _handleStateChangeSuccess,
14322          *                 error : _handleStateChangeError
14323          *             }
14324          *         });
14325          */
14326         setState: function(state, reasonCode, params) {
14327             var handlers, reasonCodeId, contentBody = {},
14328                 restType = this.getRestType(),
14329                 url = this.getRestUrl();
14330             params = params || {};
14331 
14332             if(reasonCode) {
14333                 reasonCodeId = reasonCode.id;
14334             }
14335 
14336             contentBody[restType] = {
14337                 "state": state,
14338                 "maxDialogLimit": params.maxDialogLimit,
14339                 "interruptAction": params.interruptAction,
14340                 "dialogLogoutAction": params.dialogLogoutAction,
14341                 "reasonCodeId": reasonCodeId
14342             };
14343 
14344             handlers = params.handlers || {};
14345 
14346             this._makeRequest(contentBody, url, handlers);
14347 
14348             return this;
14349         },
14350 
14351         /**
14352          * Returns a MediaDialogs collection object that is associated with User on this Media.
14353          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
14354          * applicable when Object has not been previously created).
14355          * @returns {finesse.restservices.MediaDialogs}
14356          *     A MediaDialogs collection object.
14357          * @example
14358          *  First call:
14359          *      _mediaDialogs = _media.getMediaDialogs({
14360          *                      onLoad : _handleMediaDialogsLoad,
14361          *                      onChange : _handleTeamChange,
14362          *                      onAdd: _handleMediaDialogAdd,
14363          *                      onDelete: _handleMediaDialogDelete,
14364          *                      onError: _errorHandler
14365          *      });
14366          *  Subsequent calls on the same object, after the media dialogs are loaded:
14367          *      ...
14368          *      _mediaDialogsNew = _media.getMediaDialogs();
14369          *      _dialogsCollection = _mediaDialogsNew.getCollection();
14370          *      ...
14371          */
14372         getMediaDialogs: function (callbacks) {
14373             var options = callbacks || {};
14374             options.parentObj = this._restObj;
14375             options.mediaObj = this;
14376             this.isLoaded();
14377 
14378             if (this._mdialogs === null) {
14379                 this._mdialogs = new MediaDialogs(options);
14380             }
14381 
14382             return this._mdialogs;
14383         },
14384 
14385         /**
14386          * Refresh the dialog collection associated with this media.
14387          * The refresh will happen only if the media dialogs have been initialized.
14388          */
14389         refreshMediaDialogs: function() {
14390             if ( this._mdialogs ) {
14391                 this._mdialogs.refresh();
14392             }
14393         },
14394 
14395         /**
14396          * Set the maxDialogLimit, interruptAction, and dialogLogoutAction settings that the application will use for
14397          * this media. In the event of a failure, these options will be set on the new Finesse server.
14398          *
14399          * @param {Object} mediaOptions an object with the following properties:<ul>
14400          *         <li><b>maxDialogLimit:</b>The maximum number of tasks that is allowed to handle concurrently</li>
14401          *         <li><b>interruptAction:</b> Accept or ignore interrupts</li>
14402          *         <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
14403          */
14404         setMediaOptions: function(mediaOptions) {
14405             if ( mediaOptions ) {
14406                 this._mediaOptions = mediaOptions;
14407                 if ( !this._optionsHelper ) {
14408                     this._optionsHelper = new MediaOptionsHelper(this, this._mediaOptions);
14409                 }
14410             }
14411         },
14412 
14413         /**
14414          * Refresh this media object and optionally refresh the list of media dialogs associated with this object.
14415          *
14416          * @param {Integer} retries the number of times to retry synchronizing this media object.
14417          */
14418         refresh: function(retries) {
14419             retries = retries || 1;
14420             this._synchronize(retries);
14421             this.refreshMediaDialogs();
14422         },
14423 
14424         /**
14425          * Returns true if the user in work state on this Media.
14426          * @returns {boolean} true if the media is in work state; false otherwise
14427          */
14428         isInWorkState: function() {
14429             return this.getState() === Media.States.WORK;
14430         },
14431 
14432         /**
14433          * Returns true if the user in any state except LOGOUT on this Media.
14434          * @returns {boolean} returns true if the agent is in any state except LOGOUT in this media
14435          */
14436         isLoggedIn: function() {
14437          var state = this.getState();
14438          return state && state !== Media.States.LOGOUT;
14439         }
14440     });
14441 
14442     Media.States = /** @lends finesse.restservices.Media.States.prototype */ {
14443         /**
14444          * User Login on a non-voice Media.  Note that while this is an action, is not technically a state, since a
14445          * logged-in User will always be in a specific state (READY, NOT_READY, etc.).
14446          */
14447         LOGIN: "LOGIN",
14448         /**
14449          * User is logged out of this Media.
14450          */
14451         LOGOUT: "LOGOUT",
14452         /**
14453          * User is not ready on this Media.
14454          */
14455         NOT_READY: "NOT_READY",
14456         /**
14457          * User is ready for activity on this Media.
14458          */
14459         READY: "READY",
14460         /**
14461          * User has a dialog coming in, but has not accepted it.
14462          */
14463         RESERVED: "RESERVED",
14464         /**
14465          * The dialogs in this media have been interrupted by a dialog in a non-interruptible media.
14466          */
14467         INTERRUPTED: "INTERRUPTED",
14468         /**
14469          * User enters this state when failing over from one Finesse to the other or when failing over from one
14470          * PG side to the other.
14471          */
14472         WORK: "WORK",
14473         /**
14474          * @class Possible Media state values. Used in finesse.restservices.Media#setState.
14475          * @constructs
14476          */
14477         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
14478 
14479     };
14480 
14481     window.finesse = window.finesse || {};
14482     window.finesse.restservices = window.finesse.restservices || {};
14483     window.finesse.restservices.Media = Media;
14484 
14485     return Media;
14486 });
14487 /**
14488  * JavaScript representation of the Finesse Media collection
14489  * object which contains a list of Media objects.
14490  *
14491  * @requires finesse.clientservices.ClientServices
14492  * @requires Class
14493  * @requires finesse.FinesseBase
14494  * @requires finesse.restservices.RestBase
14495  * @requires finesse.restservices.Media
14496  */
14497 /** @private */
14498 define('restservices/MediaList',[
14499         'restservices/RestCollectionBase',
14500         'restservices/Media',
14501         'restservices/RestBase',
14502         'utilities/Utilities'
14503     ],
14504     function (RestCollectionBase, Media, RestBase, Utilities) {
14505         var MediaList = RestCollectionBase.extend(/** @lends finesse.restservices.MediaList.prototype */{
14506 
14507             /**
14508              * @class
14509              * JavaScript representation of a MediaList collection object.
14510              * @augments finesse.restservices.RestCollectionBase
14511              * @constructs
14512              * @see finesse.restservices.Media
14513              * @example
14514              *  mediaList = _user.getMediaList( {
14515              *      onCollectionAdd : _handleMediaAdd,
14516              *      onCollectionDelete : _handleMediaDelete,
14517              *      onLoad : _handleMediaListLoaded
14518              *  });
14519              *
14520              * _mediaCollection = mediaList.getCollection();
14521              * for (var mediaId in _mediaCollection) {
14522              *     if (_mediaCollection.hasOwnProperty(mediaId)) {
14523              *         media = _mediaCollection[mediaId];
14524              *         etc...
14525              *     }
14526              * }
14527              */
14528             _fakeConstuctor: function () {
14529                 /* This is here to hide the real init constructor from the public docs */
14530             },
14531 
14532             /**
14533              * @private
14534              * @param {Object} options
14535              *     An object with the following properties:<ul>
14536              *         <li><b>id:</b> The id of the object being constructed</li>
14537              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
14538              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
14539              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
14540              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
14541              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
14542              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14543              *             <li><b>content:</b> {String} Raw string of response</li>
14544              *             <li><b>object:</b> {Object} Parsed object of response</li>
14545              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14546              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14547              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14548              *             </ul></li>
14549              *         </ul></li>
14550              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14551              **/
14552             init: function (options) {
14553                 this._super(options);
14554             },
14555 
14556             /**
14557              * @private
14558              * Gets the REST class for the current object - this is the MediaList class.
14559              */
14560             getRestClass: function () {
14561                 return MediaList;
14562             },
14563 
14564             /**
14565              * @private
14566              * Gets the REST class for the objects that make up the collection. - this
14567              * is the Media class.
14568              */
14569             getRestItemClass: function () {
14570                 return Media;
14571             },
14572 
14573             /**
14574              * @private
14575              * Gets the REST type for the current object - this is a "MediaList".
14576              */
14577             getRestType: function () {
14578                 return "MediaList";
14579             },
14580 
14581             /**
14582              * @private
14583              * Gets the REST type for the objects that make up the collection - this is "Media".
14584              */
14585             getRestItemType: function () {
14586                 return "Media";
14587             },
14588 
14589             /**
14590              * @private
14591              * Override default to indicates that the collection doesn't support making
14592              * requests.
14593              */
14594             supportsRequests: true,
14595 
14596             /**
14597              * @private
14598              * Override default to indicates that the collection subscribes to its objects.
14599              */
14600             supportsRestItemSubscriptions: true,
14601 
14602             /**
14603              * The REST URL in which this object can be referenced.
14604              * @return {String}
14605              *     The REST URI for this object.
14606              * @private
14607              */
14608             getRestUrl: function () {
14609                 var
14610                     restObj = this._restObj,
14611                     restUrl = "";
14612 
14613                 //Prepend the base REST object if one was provided.
14614                 if (restObj instanceof RestBase) {
14615                     restUrl += restObj.getRestUrl();
14616                 }
14617                 //Otherwise prepend with the default webapp name.
14618                 else {
14619                     restUrl += "/finesse/api";
14620                 }
14621 
14622                 //Append the REST type. (Media not MediaList)
14623                 restUrl += "/" + this.getRestItemType();
14624 
14625                 //Append ID if it is not undefined, null, or empty.
14626                 if (this._id) {
14627                     restUrl += "/" + this._id;
14628                 }
14629 
14630                 return restUrl;
14631             },
14632 
14633             /**
14634              * Returns a specific Media with the id passed, from the MediaList collection.
14635              *  * @param {Object} options
14636              *     An object with the following properties:<ul>
14637              *         <li><b>id:</b> The id of the media to fetch</li>
14638              *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
14639              *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
14640              *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
14641              *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
14642              *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
14643              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14644              *             <li><b>content:</b> {String} Raw string of response</li>
14645              *             <li><b>object:</b> {Object} Parsed object of response</li>
14646              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14647              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14648              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14649              *             </ul></li>
14650              *         </ul></li>
14651              *         <li><b>mediaOptions:</b> {Object} An object with the following properties:<ul>
14652              *             <li><b>maxDialogLimit:</b> The id of the object being constructed</li>
14653              *             <li><b>interruptAction:</b> Accept or ignore interrupts</li>
14654              *             <li><b>dialogLogoutAction:</b> transfer or close the task at logout time</li></ul>
14655              *         </li></ul>
14656              *
14657              * @returns {finesse.restservices.Media}
14658              *     A Media object.
14659              */
14660             getMedia: function (options) {
14661                 this.isLoaded();
14662                 options = options || {};
14663                 var objectId = options.id,
14664                     media = this._collection[objectId];
14665 
14666                 //throw error if media not found
14667                 if(!media) {
14668                     throw new Error("No media found with id: " + objectId);
14669                 }
14670 
14671                 media.addHandler('load', options.onLoad);
14672                 media.addHandler('change', options.onChange);
14673                 media.addHandler('add', options.onAdd);
14674                 media.addHandler('delete', options.onDelete);
14675                 media.addHandler('error', options.onError);
14676 
14677                 media.setMediaOptions(options.mediaOptions);
14678 
14679                 return media;
14680             },
14681 
14682             /**
14683              * Utility method to build the internal collection data structure (object) based on provided data
14684              * @param {Object} data
14685              *     The data to build the internal collection from
14686              * @private
14687              */
14688             _buildCollection: function (data) {
14689                 //overriding this from RestBaseCollection because we need to pass in parentObj
14690                 //because restUrl is finesse/api/User/<useri>/Media/<mediaId>
14691                 //other objects like dialog have finesse/api/Dialog/<dialogId>
14692 
14693                 var i, object, objectId, dataArray;
14694                 if (data && this.getProperty(data, this.getRestItemType()) !== null) {
14695                     dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
14696                     for (i = 0; i < dataArray.length; i += 1) {
14697 
14698                         object = {};
14699                         object[this.getRestItemType()] = dataArray[i];
14700 
14701                         //get id from object.id instead of uri, since uri is not there for some reason
14702                         objectId = object[this.getRestItemType()].id;//this._extractId(object);
14703 
14704                         //create the Media Object
14705                         this._collection[objectId] = new (this.getRestItemClass())({
14706                             id: objectId,
14707                             data: object,
14708                             parentObj: this._restObj
14709                         });
14710                         this.length += 1;
14711                     }
14712                 }
14713             }
14714         });
14715 
14716         window.finesse = window.finesse || {};
14717         window.finesse.restservices = window.finesse.restservices || {};
14718         window.finesse.restservices.MediaList = MediaList;
14719 
14720         return MediaList;
14721     });
14722 
14723 /**
14724 * JavaScript representation of the Finesse ReasonCodes collection
14725 * object which contains a list of ReasonCodes objects.
14726  *
14727  * @requires Class
14728  */
14729 
14730 /** @private */
14731 define('restservices/ReasonCodes',[], function () {
14732 
14733     var ReasonCodes = Class.extend(/** @lends finesse.restservices.ReasonCodes.prototype */{
14734         
14735         /**
14736          * @class
14737          * JavaScript representation of a ReasonCodes collection object. 
14738          * @augments Class
14739          * @constructs
14740          * @example
14741          *  _user.getNotReadyReasonCodes({
14742          *           success: handleNotReadyReasonCodesSuccess,
14743          *           error: handleNotReadyReasonCodesError
14744          *       });
14745          *  
14746          * handleNotReadyReasonCodesSuccess = function(reasonCodes) {
14747          *          for(var i = 0; i < reasonCodes.length; i++) {
14748          *             var reasonCode = reasonCodes[i];
14749          *             var reasonCodeId = reasonCode.id;
14750          *             var reasonCodeLabel = reasonCode.label;
14751          *          }
14752          *      }
14753          * }
14754         */
14755 
14756         _fakeConstuctor: function () {
14757             /* This is here to hide the real init constructor from the public docs */
14758         }
14759         
14760     });
14761  
14762     window.finesse = window.finesse || {};
14763     window.finesse.restservices = window.finesse.restservices || {};
14764     window.finesse.restservices.ReasonCodes = ReasonCodes;
14765        
14766     return ReasonCodes;
14767 });
14768 
14769 /**
14770  * JavaScript representation of the Finesse User object
14771  *
14772  * @requires finesse.clientservices.ClientServices
14773  * @requires Class
14774  * @requires finesse.FinesseBase
14775  * @requires finesse.restservices.RestBase
14776  */
14777 
14778 /** @private */
14779 define('restservices/User',[
14780     'restservices/RestBase',
14781     'restservices/Dialogs',
14782     'restservices/ClientLog',
14783     'restservices/Queues',
14784     'restservices/WrapUpReasons',
14785     'restservices/PhoneBooks',
14786     'restservices/Workflows',
14787     'restservices/UserMediaPropertiesLayout',
14788     'restservices/UserMediaPropertiesLayouts',
14789     'restservices/Media',
14790     'restservices/MediaList',
14791     'restservices/ReasonCodes',
14792     'utilities/Utilities'
14793 ],
14794 function (RestBase, Dialogs, ClientLog, Queues, WrapUpReasons, PhoneBooks, Workflows, UserMediaPropertiesLayout, UserMediaPropertiesLayouts, Media, MediaList, ReasonCodes, Utilities) {
14795 
14796     var User = RestBase.extend(/** @lends finesse.restservices.User.prototype */{
14797 
14798         _dialogs : null,
14799         _clientLogObj : null,
14800         _wrapUpReasons : null,
14801         _phoneBooks : null,
14802         _workflows : null,
14803         _mediaPropertiesLayout : null,
14804         _mediaPropertiesLayouts : null,
14805         _queues : null,
14806         media : null,
14807         mediaList : null,
14808 
14809         /**
14810          * @class
14811          * The User represents a Finesse Agent or Supervisor.
14812          *
14813          * @param {Object} options
14814          *     An object with the following properties:<ul>
14815          *         <li><b>id:</b> The id of the object being constructed</li>
14816          *         <li><b>onLoad(this): (optional)</b> callback handler for when the object is successfully loaded from the server</li>
14817          *         <li><b>onChange(this): (optional)</b> callback handler for when an update notification of the object is received</li>
14818          *         <li><b>onAdd(this): (optional)</b> callback handler for when a notification that the object is created is received</li>
14819          *         <li><b>onDelete(this): (optional)</b> callback handler for when a notification that the object is deleted is received</li>
14820          *         <li><b>onError(rsp): (optional)</b> callback handler for if loading of the object fails, invoked with the error response object:<ul>
14821          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
14822          *             <li><b>content:</b> {String} Raw string of response</li>
14823          *             <li><b>object:</b> {Object} Parsed object of response</li>
14824          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
14825          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
14826          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
14827          *             </ul></li>
14828          *         </ul></li>
14829          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
14830          * @augments finesse.restservices.RestBase
14831          * @constructs
14832          * @example
14833          *      _user = new finesse.restservices.User({
14834          *                      id: _id,
14835          *                      onLoad : _handleUserLoad,
14836          *                      onChange : _handleUserChange
14837          *      });
14838          **/
14839         init: function (options) {
14840             this._super(options);
14841         },
14842 
14843         Callbacks: {},
14844 
14845         /**
14846          * @private
14847          * Gets the REST class for the current object - this is the User object.
14848          * @returns {Object}
14849          *      The User constructor.
14850          */
14851         getRestClass: function () {
14852             return User;
14853         },
14854 
14855         /**
14856         * @private
14857          * Gets the REST type for the current object - this is a "User".
14858          * @returns {String}
14859          *      The User String
14860          */
14861         getRestType: function () {
14862             return "User";
14863         },
14864         /**
14865          * @private
14866          * overloading this to return URI
14867          * @returns {String}
14868          *      The REST URI for this object.
14869          */
14870         getXMPPNodePath: function () {
14871             return this.getRestUrl();
14872         },
14873         /**
14874         * @private
14875          * Returns whether this object supports subscriptions
14876          * @returns {Boolean} True
14877          */
14878         supportsSubscriptions: function () {
14879             return true;
14880         },
14881 
14882         /**
14883          * Getter for the firstName of this User.
14884          * @returns {String}
14885          *     The firstName for this User
14886          */
14887         getFirstName: function () {
14888             this.isLoaded();
14889             return Utilities.convertNullToEmptyString(this.getData().firstName);
14890         },
14891 
14892                                 
14893         /**
14894          * Getter for the reasonCode of this User.
14895          * 
14896          * @returns {Object} The reasonCode for the state of the User<br>
14897          * The contents may include the following:<ul>
14898          *         <li>uri: The URI for the reason code object.
14899          *         <li>id: The unique ID for the reason code.
14900          *         <li>category: The category. Can be either NOT_READY or LOGOUT.
14901          *         <li>code: The numeric reason code value.
14902          *         <li>label: The label for the reason code.
14903          *         <li>forAll: Boolean flag that denotes the global status for the reason code.
14904          *         <li>systemCode: Boolean flag which denotes whether the reason code is system generated or custom one.
14905          *     </ul>
14906          * 
14907          */
14908         getReasonCode : function() {
14909             this.isLoaded();
14910             return this.getData().reasonCode;
14911         },
14912         
14913         /**
14914          * Getter for the pending state reasonCode of this User.
14915          * 
14916          * @returns {Object} The reasonCode for the pending state of the User.<br>
14917          * The contents may include the following:<ul>
14918          *         <li>uri: The URI for the reason code object.
14919          *         <li>id: The unique ID for the reason code.
14920          *         <li>category: The category. Can be either NOT_READY or LOGOUT.
14921          *         <li>code: The numeric reason code value.
14922          *         <li>label: The label for the reason code.
14923          *         <li>forAll: Boolean flag that denotes the global status for the reason code.
14924          *         <li>systemCode: Boolean flag which denotes whether the reason code is system generated or custom one.
14925          *     </ul>
14926          */
14927         getPendingStateReasonCode : function() {
14928             this.isLoaded();
14929             return this.getData().pendingStateReasonCode;
14930         },
14931 
14932                                 
14933         /**
14934          * Getter for the reasonCode of this User.
14935          * 
14936          * @returns {Boolean} True if the reason code for the state of the user
14937          * is a system generated reason code. 
14938          */
14939         isReasonCodeReserved : function() {
14940             var resonCode = this.getReasonCode();
14941             if (resonCode) {
14942                 return resonCode.systemCode === "true" ? true
14943                         : false;
14944             }
14945             return false;
14946         },
14947 
14948 
14949         /**
14950          * Getter for the lastName of this User.
14951          * @returns {String}
14952          *     The lastName for this User
14953          */
14954         getLastName: function () {
14955             this.isLoaded();
14956             return Utilities.convertNullToEmptyString(this.getData().lastName);
14957         },
14958 
14959         /**
14960          * Getter for the full name of this User.
14961          * The full name is of the format "FirstName LastName" (for example: "John Doe")
14962          * 
14963          * @returns {String}
14964          *     The full name of this User.
14965          */
14966         getFullName : function() {
14967             this.isLoaded();
14968 
14969             /*
14970              * Currently, the expected format is "FirstName LastName", but can differ later
14971              * to something "LastName, FirstName".
14972              * To accommodate such, we use formatString utility method so that, if required,
14973              * the same can be achieved just by flipping the format place holders as follows:
14974              * "{1}, {0}"
14975              * Also, the function could be enhanced to take the format parameter.
14976              */
14977             var fmt = "{0} {1}";
14978             return Utilities.formatString(fmt,
14979                 Utilities.convertNullToEmptyString(this.getData().firstName),
14980                 Utilities.convertNullToEmptyString(this.getData().lastName));
14981         },
14982 
14983         /**
14984          * Getter for the extension of this User.
14985          * @returns {String}
14986          *     The extension, if any, of this User
14987          */
14988         getExtension: function () {
14989             this.isLoaded();
14990             return Utilities.convertNullToEmptyString(this.getData().extension);
14991         },
14992 
14993         /**
14994          * Getter for the id of the Team of this User
14995          * @returns {String}
14996          *     The current (or last fetched) id of the Team of this User
14997          */
14998         getTeamId: function () {
14999             this.isLoaded();
15000             return this.getData().teamId;
15001         },
15002 
15003         /**
15004          * Getter for the name of the Team of this User
15005          * @returns {String}
15006          *     The current (or last fetched) name of the Team of this User
15007          */
15008         getTeamName: function () {
15009             this.isLoaded();
15010             return this.getData().teamName;
15011         },
15012 
15013         /**
15014          * Is user an agent?
15015          * @returns {Boolean} True if user has role of agent, else false.
15016          */
15017         hasAgentRole: function () {
15018             this.isLoaded();
15019             return this.hasRole("Agent");
15020         },
15021 
15022         /**
15023          * Is user a supervisor?
15024          * @returns {Boolean} True if user has role of supervisor, else false.
15025          */
15026         hasSupervisorRole: function () {
15027             this.isLoaded();
15028             return this.hasRole("Supervisor");
15029         },
15030 
15031         /**
15032          * @private
15033          * Checks to see if user has "theRole"
15034          * @returns {Boolean} True if "theRole" has the role of supervisor or agent, else false.
15035          */
15036         hasRole: function (theRole) {
15037             this.isLoaded();
15038             var result = false, i, roles, len;
15039 
15040             roles = this.getData().roles.role;
15041             len = roles.length;
15042             if (typeof roles === 'string') {
15043                 if (roles === theRole) {
15044                     result = true;
15045                 }
15046             } else {
15047                 for (i = 0; i < len ; i = i + 1) {
15048                     if (roles[i] === theRole) {
15049                         result = true;
15050                         break;
15051                     }
15052                 }
15053             }
15054 
15055             return result;
15056         },
15057 
15058         /**
15059          * Getter for the pending state of this User.
15060          * @returns {String}
15061          *     The pending state of this User
15062          * @see finesse.restservices.User.States
15063          */
15064         getPendingState: function () {
15065             this.isLoaded();
15066             return Utilities.convertNullToEmptyString(this.getData().pendingState);
15067         },
15068         
15069 
15070         /**
15071          * Getter for the work mode timer for the user
15072          * @returns {String}
15073          *     The WrapUpTimer for the user
15074          * @since 12.0.1
15075          */
15076         getWrapUpTimer: function () {
15077             this.isLoaded();
15078             return this.getData().wrapUpTimer;
15079         },
15080 
15081         /**
15082          * Getter for the state of this User.
15083          * @returns {String}
15084          *     The current (or last fetched) state of this User
15085          * @see finesse.restservices.User.States
15086          */
15087         getState: function () {
15088             this.isLoaded();
15089             return this.getData().state;
15090         },
15091         
15092         /**
15093          * Getter for the media state of this User.
15094          * @returns {String}
15095          *     The current (or last fetched) media state of this User
15096          *     Will be applicable only in CCX deployments
15097          *     When the agent is talking on a manual outbound call, it returns busy 
15098          *     The value will not be present in other cases.
15099          */
15100         getMediaState: function () {
15101             this.isLoaded();
15102             /* There is an assertion that the value should not be undefined while setting the value in datastore. Hence setting it to null*/
15103             if(this.getData().mediaState === undefined) {
15104                   this.getData().mediaState = null;
15105             }
15106             
15107             return this.getData().mediaState;
15108          },
15109         
15110         /**
15111          * Getter for the state change time of this User.
15112          * @returns {String}
15113          *     The state change time of this User
15114          */
15115         getStateChangeTime: function () {
15116             this.isLoaded();
15117             return this.getData().stateChangeTime;
15118         },
15119 
15120         /**
15121          * Getter for the wrap-up mode of this User.
15122          * @returns {String} The wrap-up mode of this user that is a value of {@link finesse.restservices.User.WrapUpMode}
15123          * @see finesse.restservices.User.WrapUpMode
15124          */
15125         getWrapUpOnIncoming: function () {
15126             this.isLoaded();
15127             return this.getData().settings.wrapUpOnIncoming;
15128         },
15129 
15130         /**
15131          * Is User required to go into wrap-up?
15132          * @return {Boolean}
15133          *      True if this agent is required to go into wrap-up.
15134          * @see finesse.restservices.User.WrapUpMode
15135          */
15136         isWrapUpRequired: function () {
15137             return (this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED ||
15138                     this.getWrapUpOnIncoming() === User.WrapUpMode.REQUIRED_WITH_WRAP_UP_DATA);
15139         },
15140 
15141         /**
15142          * Checks to see if the user is considered a mobile agent by checking for
15143          * the existence of the mobileAgent node.
15144          * @returns {Boolean}
15145          *      True if this agent is a mobile agent.
15146          */
15147         isMobileAgent: function () {
15148             this.isLoaded();
15149             var ma = this.getData().mobileAgent;
15150             return ma !== null && typeof ma === "object";
15151         },
15152 
15153         /**
15154          * Getter for the mobile agent work mode.
15155          * @returns {finesse.restservices.User.WorkMode}
15156          *      If available, return the mobile agent work mode, otherwise null.
15157          * @see finesse.restservices.User.WorkMode
15158          */
15159         getMobileAgentMode: function () {
15160             this.isLoaded();
15161             if (this.isMobileAgent()) {
15162                 return this.getData().mobileAgent.mode;
15163             }
15164             return null;
15165         },
15166 
15167         /**
15168          * Getter for the mobile agent dial number.
15169          * @returns {String}
15170          *      If available, return the mobile agent dial number, otherwise null.
15171          */
15172         getMobileAgentDialNumber: function () {
15173             this.isLoaded();
15174             if (this.isMobileAgent()) {
15175                 return this.getData().mobileAgent.dialNumber;
15176             }
15177             return null;
15178         },
15179 
15180         /**
15181          * Getter for a Dialogs collection object that is associated with User.
15182          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
15183          * applicable when Object has not been previously created).
15184          * @returns {finesse.restservices.Dialogs}
15185          *     A Dialogs collection object.
15186          * @see finesse.restservices.Dialogs
15187          */
15188         getDialogs: function (callbacks) {
15189             var options = callbacks || {};
15190             options.parentObj = this;
15191             this.isLoaded();
15192 
15193             if (this._dialogs === null) {
15194                 this._dialogs = new Dialogs(options);
15195             }
15196 
15197             return this._dialogs;
15198         },
15199         
15200         /**
15201          * Getter for a Dialogs collection object that is associated with User.This will always query from server
15202          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers 
15203          * @returns {finesse.restservices.Dialogs}
15204          *     A Dialogs collection object.
15205          * @see finesse.restservices.Dialogs
15206          */
15207         getDialogsNoCache: function (callbacks) {
15208             var options = callbacks || {};
15209             options.parentObj = this;
15210             this.isLoaded();
15211             this._dialogs = new Dialogs(options);
15212 
15213             return this._dialogs;
15214         },
15215 
15216         /**
15217          * Getter for Media collection object that is associated with User.
15218          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only
15219          * applicable when Object has not been previously created).
15220          * @returns {finesse.restservices.MediaList}
15221          *     A Media Dialogs collection object.
15222          * @see finesse.restservices.MediaList
15223          */
15224         getMediaList: function (callbacks) {
15225             var options = callbacks || {};
15226             options.parentObj = this;
15227             this.isLoaded();
15228 
15229             if (this.mediaList === null) {
15230                 this.mediaList = new MediaList(options);
15231             }
15232 
15233             return this.mediaList;
15234         },
15235 
15236         /**
15237          * @private
15238          * Getter for a ClientLog object that is associated with User.
15239          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
15240          * applicable when Object has not been previously created).
15241          * @returns {finesse.restservices.ClientLog}
15242          *     A ClientLog collection object.
15243          * @see finesse.restservices.ClientLog
15244          */
15245         getClientLog: function (callbacks) {
15246             var options = callbacks || {};
15247             options.parentObj = this;
15248             this.isLoaded();
15249            
15250             if (this._clientLogObj === null) {
15251                 this._clientLogObj = new ClientLog(options);
15252             }
15253             else {
15254                 if(options.onLoad && typeof options.onLoad === "function") {
15255                 options.onLoad(this._clientLogObj);
15256                 }
15257             }
15258             return this._clientLogObj;
15259         },
15260        
15261         /**
15262          * Getter for a Queues collection object that is associated with User.
15263          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
15264          * applicable when Object has not been previously created).
15265          * @returns {finesse.restservices.Queues}
15266          *     A Queues collection object.
15267          * @see finesse.restservices.Queues
15268          */
15269         getQueues: function (callbacks) {
15270             var options = callbacks || {};
15271             options.parentObj = this;
15272             this.isLoaded();
15273     
15274             if (this._queues === null) {
15275                 this._queues = new Queues(options);
15276             }
15277     
15278             return this._queues;
15279         },
15280 
15281         /**
15282          * Getter for a WrapUpReasons collection object that is associated with User.
15283          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
15284          * applicable when Object has not been previously created).
15285          * @returns {finesse.restservices.WrapUpReasons}
15286          *     A WrapUpReasons collection object.
15287          * @see finesse.restservices.WrapUpReasons
15288          */
15289         getWrapUpReasons: function (callbacks) {
15290             var options = callbacks || {};
15291             options.parentObj = this;
15292             this.isLoaded();
15293     
15294             if (this._wrapUpReasons === null) {
15295                 this._wrapUpReasons = new WrapUpReasons(options);
15296             }
15297     
15298             return this._wrapUpReasons;
15299         },
15300 
15301         /**
15302          * Getter for a PhoneBooks collection object that is associated with User.
15303          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
15304          * applicable when Object has not been previously created).
15305          * @returns {finesse.restservices.PhoneBooks}
15306          *     A PhoneBooks collection object.
15307          * @see finesse.restservices.PhoneBooks
15308          */
15309         getPhoneBooks: function (callbacks) {
15310             var options = callbacks || {};
15311             options.parentObj = this;
15312             this.isLoaded();
15313     
15314             if (this._phoneBooks === null) {
15315                 this._phoneBooks = new PhoneBooks(options);
15316             }
15317     
15318             return this._phoneBooks;
15319         },
15320 
15321         /**
15322          * @private
15323          * Loads the Workflows collection object that is associated with User and
15324          * 'returns' them to the caller via the handlers.
15325          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
15326          * applicable when Object has not been previously created).
15327          * @see finesse.restservices.Workflow
15328          * @see finesse.restservices.Workflows
15329          * @see finesse.restservices.RestCollectionBase
15330          */
15331         loadWorkflows: function (callbacks) {
15332             var options = callbacks || {};
15333             options.parentObj = this;
15334             this.isLoaded();
15335 
15336             if (this._workflows === null) {
15337                 this._workflows = new Workflows(options);
15338             } else {
15339                 this._workflows.refresh();
15340             }
15341 
15342         },
15343 
15344         /**
15345          * Getter for a UserMediaPropertiesLayout object that is associated with User.
15346          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
15347          * applicable when Object has not been previously created).
15348          * @returns {finesse.restservices.UserMediaPropertiesLayout}
15349          *     The UserMediaPropertiesLayout object associated with this user
15350          * @see finesse.restservices.UserMediaPropertiesLayout
15351          */
15352         getMediaPropertiesLayout: function (callbacks) {
15353             var options = callbacks || {};
15354             options.parentObj = this;
15355             options.id = this._id;
15356     
15357             this.isLoaded();
15358             if (this._mediaPropertiesLayout === null) {
15359                 this._mediaPropertiesLayout = new UserMediaPropertiesLayout(options);
15360             }
15361             return this._mediaPropertiesLayout;
15362         },
15363 
15364         /**
15365          * Getter for a UserMediaPropertiesLayouts object that is associated with User.
15366          * @param {finesse.interfaces.RestObjectHandlers} [handlers] Object that sets callback handlers (only 
15367          * applicable when Object has not been previously created).
15368          * @returns {finesse.restservices.UserMediaPropertiesLayout}
15369          *     The UserMediaPropertiesLayout object associated with this user
15370          * @see finesse.restservices.UserMediaPropertiesLayout
15371          */
15372         getMediaPropertiesLayouts: function (callbacks) {
15373             var options = callbacks || {};
15374             options.parentObj = this;
15375     
15376             this.isLoaded();
15377             if (this._mediaPropertiesLayouts === null) {
15378                 this._mediaPropertiesLayouts = new UserMediaPropertiesLayouts(options);
15379             }
15380             return this._mediaPropertiesLayouts;
15381         },
15382     
15383         /**
15384          * Getter for the Teams managed by this User(Supervisor), if any.
15385          * 
15386          * @returns {Array} of objects containing id, name, uri of the Teams managed by this User(Supervisor).<br>
15387          * The object content includes the following:<ul>
15388          * 		<li>id: The unique ID for the team.
15389          * 		<li>name: The team name for the team.
15390          * 		<li>uri: The URI for the team.
15391          * </ul>
15392          * 
15393          */
15394         getSupervisedTeams: function () {
15395             this.isLoaded();
15396     
15397             try {
15398                 return Utilities.getArray(this.getData().teams.Team);
15399             } catch (e) {
15400                 return [];
15401             }
15402     
15403         },
15404     
15405         /**
15406          * Perform an agent login for this user, associating him with the
15407          * specified extension.
15408          * @param {Object} params
15409          *     An object containing properties for agent login.
15410          * @param {String} params.reasonCodeId 
15411          *     The reason code id associated with this login
15412          * @param {String} params.extension
15413          *     The extension to associate with this user
15414          * @param {Object} [params.mobileAgent]
15415          *     A mobile agent object containing the mode and dial number properties.
15416          * @param {finesse.interfaces.RequestHandlers} params.handlers
15417          * @see finesse.interfaces.RequestHandlers
15418          * @returns {finesse.restservices.User}
15419          *     This User object, to allow cascading
15420          * @private
15421          */
15422         _login: function (params) {
15423             var handlers, contentBody = {},
15424             restType = this.getRestType();
15425             
15426             // Protect against null dereferencing.
15427             params = params || {};
15428     
15429             contentBody[restType] = {
15430                 "state": User.States.LOGIN,
15431                 "extension": params.extension
15432             };
15433             
15434             if(params.reasonCodeId){
15435                  contentBody[restType].reasonCodeId = params.reasonCodeId
15436             }
15437     
15438             // Create mobile agent node if available.
15439             if (typeof params.mobileAgent === "object") {
15440                 contentBody[restType].mobileAgent = {
15441                     "mode": params.mobileAgent.mode,
15442                     "dialNumber": params.mobileAgent.dialNumber
15443                 };
15444             }
15445     
15446             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
15447             handlers = params.handlers || {};
15448     
15449             this.restRequest(this.getRestUrl(), {
15450                 method: 'PUT',
15451                 success: handlers.success,
15452                 error: handlers.error,
15453                 content: contentBody
15454             });
15455     
15456             return this; // Allow cascading
15457         },
15458     
15459         /**
15460          * Perform an agent login for this user, associating him with the
15461          * specified extension.
15462          * @param {String} extension
15463          *     The extension to associate with this user
15464          * @param {finesse.interfaces.RequestHandlers} handlers
15465          *     An object containing the handlers for the request
15466          * @returns {finesse.restservices.User}
15467          *     This User object, to allow cascading
15468          */
15469         login: function (extension, handlers) {
15470             this.isLoaded();
15471             var params = {
15472                 "extension": extension,
15473                 "handlers": handlers
15474             };
15475             return this._login(params);
15476         },
15477         
15478         
15479 
15480     
15481         /**
15482          * Perform an agent login for this user, associating him with the
15483          * specified extension.
15484          * @param {String} extension
15485          *     The extension to associate with this user
15486          * @param {String} mode
15487          *     The mobile agent work mode as defined in finesse.restservices.User.WorkMode.
15488          * @param {String} extension
15489          *     The external dial number desired to be used by the mobile agent.
15490          * @param {finesse.interfaces.RequestHandlers} handlers
15491          *     An object containing the handlers for the request
15492          * @param {Object} reasonCode
15493          *     An object containing the reasonCode for the login request
15494          * @returns {finesse.restservices.User}
15495          *     This User object, to allow cascading
15496          */
15497         loginMobileAgent: function (extension, mode, dialNumber, handlers, reasonCode) {
15498             this.isLoaded();
15499             
15500             var params = {
15501                 "extension": extension,
15502                 "mobileAgent": {
15503                     "mode": mode,
15504                     "dialNumber": dialNumber
15505                 },
15506                 "handlers": handlers
15507             };
15508             //US303866 - reasonCode added for restoring MobileAgent after CTI client disconnect
15509             if(reasonCode) {
15510                 params.reasonCodeId = reasonCode.id;
15511             }
15512             return this._login(params);
15513         },
15514         
15515         
15516         _updateMobileAgent: function (params) {
15517             var handlers, contentBody = {},
15518             restType = this.getRestType();
15519 
15520             params = params || {};
15521 
15522             contentBody[restType] = {
15523             };
15524 
15525             if (typeof params.mobileAgent === "object") {
15526                 contentBody[restType].mobileAgent = {
15527                     "mode": params.mobileAgent.mode,
15528                     "dialNumber": params.mobileAgent.dialNumber
15529                 };
15530             }
15531 
15532             handlers = params.handlers || {};
15533 
15534             this.restRequest(this.getRestUrl(), {
15535                 method: 'PUT',
15536                 success: handlers.success,
15537                 error: handlers.error,
15538                 content: contentBody
15539             });
15540 
15541             return this;
15542         },
15543         
15544         /**
15545          * Update user object in Finesse with agent's mobile login information (Refer defect CSCvc35407)
15546          * @param {String} mode
15547          *      The mobile agent work mode as defined in finesse.restservices.User.WorkMode.
15548          * @param {String} dialNumber
15549          *      The external dial number desired to be used by the mobile agent.
15550          * @param {finesse.interfaces.RequestHandlers} handlers
15551          *     An object containing the handlers for the request
15552          * @returns {finesse.restservices.User}
15553          *     This User object, to allow cascading
15554          */
15555         updateToMobileAgent: function (mode, dialNumber, handlers) {
15556             this.isLoaded();
15557             var params = {
15558                 "mobileAgent": {
15559                     "mode": mode,
15560                     "dialNumber": dialNumber
15561                 },
15562                 "handlers": handlers
15563             };
15564             return this._updateMobileAgent(params);
15565         },
15566     
15567         /**
15568          * Perform an agent logout for this user.
15569          * @param {String} reasonCode
15570          *     The reason this user is logging out.  Pass null for no reason.
15571          * @param {finesse.interfaces.RequestHandlers} handlers
15572          *     An object containing the handlers for the request
15573          * @returns {finesse.restservices.User}
15574          *     This User object, to allow cascading
15575          */
15576         logout: function (reasonCode, handlers) {
15577             return this.setState("LOGOUT", reasonCode, handlers);
15578         },
15579     
15580         /**
15581          * Set the state of the user.
15582          * @param {String} newState
15583          *     The state you are setting
15584          * @param {ReasonCode} reasonCode
15585          *     The reason this user is logging out.  Pass null for no reason.
15586          * @param {finesse.interfaces.RequestHandlers} handlers
15587          *     An object containing the handlers for the request
15588          * @see finesse.restservices.User.States
15589          * @returns {finesse.restservices.User}
15590          *     This User object, to allow cascading
15591          */
15592         setState: function (newState, reasonCode, handlers) {
15593             this.isLoaded();
15594     
15595             var options, contentBody = {};
15596     
15597             if (!reasonCode) {
15598             	contentBody[this.getRestType()] = {
15599             			"state": newState
15600             	};
15601                 
15602             } else {
15603             	contentBody[this.getRestType()] = {
15604             			"state": newState,
15605             			"reasonCodeId": reasonCode.id
15606             	};
15607                
15608             }
15609     
15610             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
15611             handlers = handlers || {};
15612     
15613             options = {
15614                 method: 'PUT',
15615                 success: handlers.success,
15616                 error: handlers.error,
15617                 content: contentBody
15618             };
15619     
15620             // After removing the selective 202 handling, we should be able to just use restRequest
15621             this.restRequest(this.getRestUrl(), options);
15622     
15623             return this; // Allow cascading
15624         },
15625     
15626         /**
15627          * Make call to a particular phone number.
15628          *
15629          * @param {String} 
15630          *     The number to call
15631          * @param {finesse.interfaces.RequestHandlers} handlers
15632          *     An object containing the handlers for the request
15633          * @returns {finesse.restservices.User}
15634          *     This User object, to allow cascading
15635          */ 
15636         makeCall: function (number, handlers) {
15637             this.isLoaded();
15638     
15639             this.getDialogs().createNewCallDialog(number, this.getExtension(), handlers);
15640     
15641             return this; // Allow cascading
15642         },
15643     
15644         /**
15645          * Make a silent monitor call to a particular agent's phone number.
15646          *
15647          * @param {String} 
15648          *     The number to call
15649          * @param {finesse.interfaces.RequestHandlers} handlers
15650          *     An object containing the handlers for the request
15651          * @returns {finesse.restservices.User}
15652          *     This User object, to allow cascading
15653          */
15654         makeSMCall: function (number, handlers) {
15655             this.isLoaded();
15656     
15657             var actionType = "SILENT_MONITOR";
15658     
15659             this.getDialogs().createNewSuperviseCallDialog(number, this.getExtension(), actionType, handlers);
15660     
15661             return this; // Allow cascading
15662         },
15663         
15664     
15665         /**
15666          * Make a silent monitor call to a particular agent's phone number.
15667          *
15668          * @param {String}
15669          *     The number to call
15670          * @param {String} dialogUri
15671          *     The associated dialog uri of SUPERVISOR_MONITOR call
15672          * @param {finesse.interfaces.RequestHandlers} handlers
15673          *     An object containing the handlers for the request
15674          * @see finesse.restservices.dialog
15675          * @returns {finesse.restservices.User}
15676          *     This User object, to allow cascading
15677          */
15678         makeBargeCall:function (number, dialogURI, handlers) {
15679             this.isLoaded();
15680             var actionType = "BARGE_CALL";
15681             this.getDialogs().createNewBargeCall( this.getExtension(), number, actionType, dialogURI,handlers);
15682     
15683             return this; // Allow cascading
15684         },
15685         
15686         /**
15687          * Returns true if the user's current state will result in a pending state change. A pending state
15688          * change is a request to change state that does not result in an immediate state change. For
15689          * example if an agent attempts to change to the NOT_READY state while in the TALKING state, the
15690          * agent will not change state until the call ends.
15691          *
15692          * The current set of states that result in pending state changes is as follows:
15693          *     TALKING
15694          *     HOLD
15695          *     RESERVED_OUTBOUND_PREVIEW
15696          *  @returns {Boolean} True if there is a pending state change.
15697          *  @see finesse.restservices.User.States
15698          */
15699         isPendingStateChange: function () {
15700             var state = this.getState();
15701             return state && ((state === User.States.TALKING) || (state === User.States.HOLD) || (state === User.States.RESERVED_OUTBOUND_PREVIEW));
15702         },
15703         
15704         /**
15705          * Returns true if the user's current state is WORK or WORK_READY. This is used so
15706          * that a pending state is not cleared when moving into wrap up (work) mode. 
15707          * Note that we don't add this as a pending state, since changes while in wrap up
15708          * occur immediately (and we don't want any "pending state" to flash on screen.
15709          * 
15710          * @see finesse.restservices.User.States
15711          * @returns {Boolean} True if user is in wrap-up mode.
15712          */
15713         isWrapUp: function () {
15714             var state = this.getState();
15715             return state && ((state === User.States.WORK) || (state === User.States.WORK_READY));
15716         },
15717     
15718         /**
15719          * @private
15720          * Parses a uriString to retrieve the id portion
15721          * @param {String} uriString
15722          * @return {String} id
15723          */
15724         _parseIdFromUriString : function (uriString) {
15725             return Utilities.getId(uriString);
15726         },
15727                         
15728         /**
15729          * Gets the user's Reason Code label. Works for both Not Ready and
15730          * Logout reason codes
15731          * 
15732          * @return {String} the reason code label, or empty string if none
15733          */
15734         getReasonCodeLabel : function() {
15735             this.isLoaded();
15736 
15737             if (this.getData().reasonCode) {
15738                 return this.getData().reasonCode.label;
15739             } else {
15740                 return "";
15741             }
15742         },
15743     
15744         /**
15745          * Gets the user's Not Ready reason code.
15746          * 
15747          * @return {String} Reason Code Id, or undefined if not set or
15748          *         indeterminate
15749          */
15750         getNotReadyReasonCodeId : function () {
15751             this.isLoaded();
15752     
15753             var reasoncodeIdResult, finesseServerReasonCodeId;
15754             finesseServerReasonCodeId = this.getData().reasonCodeId;
15755     
15756             //FinesseServer will give "-l" => we will set to undefined (for convenience)
15757             if (finesseServerReasonCodeId !== "-1") {
15758                 reasoncodeIdResult = finesseServerReasonCodeId;
15759             }
15760     
15761             return reasoncodeIdResult;
15762         },
15763     
15764         /**
15765          * Performs a GET against the Finesse server looking up the reasonCodeId specified.
15766          * Note that there is no return value; use the success handler to process a
15767          * valid return.
15768          * @param {finesse.interfaces.RequestHandlers} handlers
15769          *     An object containing the handlers for the request
15770          * @param {String} reasonCodeId The id for the reason code to lookup
15771          * 
15772          */
15773         getReasonCodeById : function (handlers, reasonCodeId)
15774         {
15775             var self = this, contentBody, reasonCode, url;
15776             contentBody = {};
15777     
15778             url = this.getRestUrl() + "/ReasonCode/" + reasonCodeId;
15779             this.restRequest(url, {
15780                 method: 'GET',
15781                 success: function (rsp) {
15782                     reasonCode = {
15783                         uri: rsp.object.ReasonCode.uri,
15784                         label: rsp.object.ReasonCode.label,
15785                         id: self._parseIdFromUriString(rsp.object.ReasonCode.uri)
15786                     };
15787                     handlers.success(reasonCode);
15788                 },
15789                 error: function (rsp) {
15790                     handlers.error(rsp);
15791                 },
15792                 content: contentBody
15793             });
15794         },
15795     
15796         /**
15797          * Performs a GET against Finesse server retrieving all the specified type of reason codes.
15798          * @param {String} type (LOGOUT or NOT_READY)
15799          * @param {finesse.interfaces.RequestHandlers} handlers
15800          *     An object containing the handlers for the request
15801          */
15802         _getReasonCodesByType : function (type, handlers)
15803         {
15804             var self = this, contentBody = {}, url, reasonCodes, i, reasonCodeArray;
15805     
15806             url = this.getRestUrl() + "/ReasonCodes?category=" + type;
15807             this.restRequest(url, {
15808                 method: 'GET',
15809                 success: function (rsp) {
15810                     reasonCodes = [];
15811     
15812                     reasonCodeArray = rsp.object.ReasonCodes.ReasonCode;
15813                     if (reasonCodeArray === undefined) {
15814                         reasonCodes = undefined;
15815                     } else if (reasonCodeArray[0] !== undefined) {
15816                         for (i = 0; i < reasonCodeArray.length; i = i + 1) {
15817                             reasonCodes[i] = {
15818                                 label: rsp.object.ReasonCodes.ReasonCode[i].label,
15819                                 id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode[i].uri)
15820                             };
15821                         }
15822                     } else {
15823                         reasonCodes[0] = {
15824                             label: rsp.object.ReasonCodes.ReasonCode.label,
15825                             id: self._parseIdFromUriString(rsp.object.ReasonCodes.ReasonCode.uri)
15826                         };
15827                     }
15828                     handlers.success(reasonCodes);
15829                 },
15830                 error: function (rsp) {
15831                     handlers.error(rsp);
15832                 },
15833                 content: contentBody
15834             });
15835         },
15836     
15837         /**
15838          * Performs a GET against the Finesse server and retrieves all of the Not Ready
15839          * reason codes. Note that there is no return value; use the success handler to
15840          * process a valid return.
15841          *
15842          * <pre class="code">
15843          *      _user.getSignoutReasonCodes({
15844          *           success: handleSignoutReasonCodesSuccess,
15845          *           error: handleSignoutReasonCodesError
15846          *       });
15847          * </pre>
15848          *
15849          * @see finesse.restservices.ReasonCodes
15850          *
15851          * @param {finesse.interfaces.RequestHandlers} handlers
15852          *     An object containing the handlers for the request
15853          */
15854         getSignoutReasonCodes : function (handlers)
15855         {
15856             this._getReasonCodesByType("LOGOUT", handlers);
15857         },
15858     
15859         /**
15860          * Performs a GET against the Finesse server and retrieves all of the Not Ready
15861          * reason codes. Note that there is no return value; use the success handler to
15862          * process a valid return.
15863          *
15864          * <pre class="code">
15865          *      _user.getNotReadyReasonCodes({
15866          *           success: handleNotReadyReasonCodesSuccess,
15867          *           error: handleNotReadyReasonCodesError
15868          *       });
15869          * </pre>
15870          *
15871          * @see finesse.restservices.ReasonCodes
15872          *
15873          * @param {finesse.interfaces.RequestHandlers} handlers
15874          *     An object containing the handlers for the request
15875          */
15876         getNotReadyReasonCodes : function (handlers)
15877         {
15878             this._getReasonCodesByType("NOT_READY", handlers);
15879         }
15880     });
15881     User.MediaStates = /** @lends finesse.restservices.User.MediaStates.prototype */ {
15882          /**
15883          * Will be applicable only in CCX deployments
15884          * When the agent is talking on a manual outbound call
15885          */
15886          BUSY: "BUSY",
15887              /**
15888              * @class Possible Agent Media States.
15889              * @constructs
15890              */
15891             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15892             
15893     };
15894     User.States = /** @lends finesse.restservices.User.States.prototype */ {
15895             /**
15896              * User Login.  Note that while this is an action, is not technically a state, since a 
15897              * logged-in User will always be in a specific state (READY, NOT_READY, TALKING, etc.).
15898              */
15899             LOGIN: "LOGIN",
15900             /**
15901              * User is logged out.
15902              */
15903             LOGOUT: "LOGOUT",
15904             /**
15905              * User is not ready. Note that in UCCX implementations, the user is in this state while on a non-routed call.
15906              */
15907             NOT_READY: "NOT_READY",
15908             /**
15909              * User is ready for calls.
15910              */
15911             READY: "READY",
15912             /**
15913              * User has a call coming in, but has not answered it.
15914              */
15915             RESERVED: "RESERVED",
15916             /**
15917              * User has an outbound call being made, but has not been connected to it.
15918              */
15919             RESERVED_OUTBOUND: "RESERVED_OUTBOUND",
15920             /**
15921              * User has an outbound call's preview information being displayed, but has not acted on it.
15922              */
15923             RESERVED_OUTBOUND_PREVIEW: "RESERVED_OUTBOUND_PREVIEW",
15924             /**
15925              * User is on a call.  Note that in UCCX implementations, this is for routed calls only.
15926              */
15927             TALKING: "TALKING",
15928             /**
15929              * User is on hold.  Note that in UCCX implementations, the user remains in TALKING state while on hold.
15930              */
15931             HOLD: "HOLD",
15932             /**
15933              * User is wrap-up/work mode.  This mode is typically configured to time out, after which the user becomes NOT_READY.
15934              */
15935             WORK: "WORK",
15936             /**
15937              * This is the same as WORK, except that after time out user becomes READY.
15938              */
15939             WORK_READY: "WORK_READY",
15940             /**
15941              * @class Possible User state values.
15942              * @constructs
15943              */
15944             _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15945           
15946         };
15947     
15948     User.WorkMode = { /** @lends finesse.restservices.User.WorkMode.prototype */
15949         /**
15950          * Mobile agent is connected (dialed) for each incoming call received.
15951          */
15952         CALL_BY_CALL: "CALL_BY_CALL",
15953         /**
15954          * Mobile agent is connected (dialed) at login.
15955          */
15956         NAILED_CONNECTION: "NAILED_CONNECTION",
15957         /**
15958          * @class Possible Mobile Agent Work Mode Types.
15959          * @constructs
15960          */
15961         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15962         
15963     };
15964 
15965     User.WrapUpMode = { /** @lends finesse.restservices.User.WrapUpMode.prototype */
15966         /**
15967          * Agent must go into wrap-up when call ends
15968          */
15969         REQUIRED: "REQUIRED",
15970         /**
15971          * Agent must go into wrap-up when call ends and must enter wrap-up data
15972          */
15973         REQUIRED_WITH_WRAP_UP_DATA: "REQUIRED_WITH_WRAP_UP_DATA",
15974         /**
15975          * Agent can choose to go into wrap-up on a call-by-call basis when the call ends
15976          */
15977         OPTIONAL: "OPTIONAL",
15978         /**
15979          * Agent is not allowed to go into wrap-up when call ends.
15980          */
15981         NOT_ALLOWED: "NOT_ALLOWED",
15982         /**
15983          * @class Possible Wrap-up Mode Types.
15984          * @constructs
15985          */
15986         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
15987         
15988     };
15989 
15990     window.finesse = window.finesse || {};
15991     window.finesse.restservices = window.finesse.restservices || {};
15992     window.finesse.restservices.User = User;
15993         
15994     return User;
15995 });
15996 
15997 /**
15998  * JavaScript representation of the Finesse Users collection
15999  * object which contains a list of Users objects.
16000  *
16001  * @requires finesse.clientservices.ClientServices
16002  * @requires Class
16003  * @requires finesse.FinesseBase
16004  * @requires finesse.restservices.RestBase
16005  * @requires finesse.restservices.RestCollectionBase
16006  * @requires finesse.restservices.User
16007  */
16008 
16009 /** @private */
16010 define('restservices/Users',[
16011     'restservices/RestCollectionBase',
16012     'restservices/RestBase',
16013     'restservices/User'
16014 ],
16015 function (RestCollectionBase, RestBase, User) {
16016 
16017     var Users = RestCollectionBase.extend(/** @lends finesse.restservices.Users.prototype */{
16018 
16019     /**
16020      * @class
16021      * JavaScript representation of a Users collection object. 
16022      * While there is no method provided to retrieve all Users, this collection is
16023      * used to return the Users in a supervised Team.
16024      * @augments finesse.restservices.RestCollectionBase
16025      * @constructs
16026      * @see finesse.restservices.Team
16027      * @see finesse.restservices.User
16028      * @see finesse.restservices.User#getSupervisedTeams
16029      * @example
16030      *  // Note: The following method gets an Array of Teams, not a Collection.
16031      *  _teams = _user.getSupervisedTeams();
16032      *  if (_teams.length > 0) {
16033      *      _team0Users = _teams[0].getUsers();
16034      *  }
16035      */
16036     _fakeConstuctor: function () {
16037         /* This is here to hide the real init constructor from the public docs */
16038     },
16039         
16040     /**
16041      * @private
16042      * JavaScript representation of the Finesse Users collection
16043      * object which contains a list of Users objects.
16044      *
16045 	 * @param {Object} options
16046 	 *     An object with the following properties:<ul>
16047      *         <li><b>id:</b> The id of the object being constructed</li>
16048      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16049      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16050      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16051      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16052      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16053      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16054      *             <li><b>content:</b> {String} Raw string of response</li>
16055      *             <li><b>object:</b> {Object} Parsed object of response</li>
16056      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16057      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16058      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16059      *             </ul></li>
16060      *         </ul></li>
16061      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16062      **/
16063 	init: function (options) {
16064 		this._super(options);
16065 	},
16066 
16067 	/**
16068      * @private
16069 	 * Gets the REST class for the current object - this is the Users class.
16070      * @returns {Object} 
16071      *      The User constructor.
16072 	 */
16073 	getRestClass: function () {
16074 	    return Users;
16075 	},
16076 
16077 	/**
16078      * @private
16079 	 * Gets the REST class for the objects that make up the collection. - this
16080 	 * is the User class.
16081      * @returns {finesse.restservices.User}
16082      *      This User object
16083      * @see finesse.restservices.User
16084 	 */
16085 	getRestItemClass: function () {
16086 		return User;
16087 	},
16088 
16089 	/**
16090      * @private
16091 	 * Gets the REST type for the current object - this is a "Users".
16092      * @returns {String} The Users String
16093 	 */
16094 	getRestType: function () {
16095 	    return "Users";
16096 	},
16097 
16098 	/**
16099      * @private
16100 	 * Gets the REST type for the objects that make up the collection - this is "User".
16101      * @returns {String} The User String
16102 	 */
16103 	getRestItemType: function () {
16104 	    return "User";
16105 	},
16106 
16107 	/**
16108      * @private
16109      * Gets the node path for the current object - this is the team Users node
16110      * @returns {String} The node path
16111      */
16112     getXMPPNodePath: function () {
16113 		return this.getRestUrl();
16114     },
16115 
16116     /**
16117      * @private
16118      * Overloading _doGET to reroute the GET to /Team/id from /Team/id/Users
16119      * This needs to be done because the GET /Team/id/Users API is missing
16120      * @returns {Users} This Users (collection) object to allow cascading
16121      */
16122     _doGET: function (handlers) {
16123         var _this = this;
16124         handlers = handlers || {};
16125         // Only do this for /Team/id/Users
16126         if (this._restObj && this._restObj.getRestType() === "Team") {
16127             this._restObj._doGET({
16128                 success: function (rspObj) {
16129                     // Making sure the response was a valid Team
16130                     if (_this._restObj._validate(rspObj.object)) {
16131                         // Shimmying the response to look like a Users collection by extracting it from the Team response
16132                         rspObj.object[_this.getRestType()] = rspObj.object[_this._restObj.getRestType()][_this.getRestType().toLowerCase()];
16133                         handlers.success(rspObj);
16134                     } else {
16135                         handlers.error(rspObj);
16136                     }
16137                 },
16138                 error: handlers.error
16139             });
16140             return this; // Allow cascading
16141         } else {
16142             return this._super(handlers);
16143         }
16144     },
16145 
16146 	/**
16147      * @private
16148      * Override default to indicates that the collection doesn't support making
16149 	 * requests.
16150 	 */
16151 	supportsRequests: false,
16152 
16153     /**
16154      * @private
16155      * Indicates that this collection handles the subscription for its items
16156      */
16157     handlesItemSubscription: true,
16158 	
16159     /**
16160      * @private
16161      * Override default to indicate that we need to subscribe explicitly
16162      */
16163     explicitSubscription: true
16164     
16165 	});
16166 
16167     window.finesse = window.finesse || {};
16168     window.finesse.restservices = window.finesse.restservices || {};
16169     window.finesse.restservices.Users = Users;
16170 
16171     return Users;
16172 });
16173 
16174 /**
16175  * JavaScript representation of the Finesse Team Not Ready Reason Code Assignment object.
16176  *
16177  * @requires finesse.clientservices.ClientServices
16178  * @requires Class
16179  * @requires finesse.FinesseBase
16180  * @requires finesse.restservices.RestBase
16181  */
16182 
16183 /** @private */
16184 define('restservices/TeamNotReadyReasonCode',['restservices/RestBase'], function (RestBase) {
16185     
16186     var TeamNotReadyReasonCode = RestBase.extend(/** @lends finesse.restservices.TeamNotReadyReasonCode.prototype */{
16187 
16188         /**
16189          * @class
16190          * JavaScript representation of a Team Not Ready ReasonCode object. Also exposes
16191          * methods to operate on the object against the server.
16192          *
16193          * @param {Object} options
16194          *     An object with the following properties:<ul>
16195          *         <li><b>id:</b> The id of the object being constructed</li>
16196          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16197          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16198          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16199          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16200          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16201          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16202          *             <li><b>content:</b> {String} Raw string of response</li>
16203          *             <li><b>object:</b> {Object} Parsed object of response</li>
16204          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16205          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16206          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16207          *             </ul></li>
16208          *         </ul></li>
16209          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16210          * @constructs
16211          **/
16212         init: function (options) {
16213             this._super(options);
16214         },
16215     
16216         /**
16217          * @private
16218          * Gets the REST class for the current object - this is the TeamNotReadyReasonCode class.
16219          * @returns {Object} The TeamNotReadyReasonCode class.
16220          */
16221         getRestClass: function () {
16222             return TeamNotReadyReasonCode;
16223         },
16224     
16225         /**
16226          * @private
16227          * Gets the REST type for the current object - this is a "ReasonCode".
16228          * @returns {String} The ReasonCode string.
16229          */
16230         getRestType: function () {
16231             return "ReasonCode";
16232         },
16233     
16234         /**
16235          * @private
16236          * Override default to indicate that this object doesn't support making
16237          * requests.
16238          */
16239         supportsRequests: false,
16240     
16241         /**
16242          * @private
16243          * Override default to indicate that this object doesn't support subscriptions.
16244          */
16245         supportsSubscriptions: false,
16246     
16247         /**
16248          * Getter for the category.
16249          * @returns {String} The category.
16250          */
16251         getCategory: function () {
16252             this.isLoaded();
16253             return this.getData().category;
16254         },
16255     
16256         /**
16257          * Getter for the code.
16258          * @returns {String} The code.
16259          */
16260         getCode: function () {
16261             this.isLoaded();
16262             return this.getData().code;
16263         },
16264     
16265         /**
16266          * Getter for the label.
16267          * @returns {String} The label.
16268          */
16269         getLabel: function () {
16270             this.isLoaded();
16271             return this.getData().label;
16272         },
16273     
16274         /**
16275          * Getter for the forAll value.
16276          * @returns {String} The forAll.
16277          */
16278         getForAll: function () {
16279             this.isLoaded();
16280             return this.getData().forAll;
16281         },
16282     
16283         /**
16284          * Getter for the Uri value.
16285          * @returns {String} The Uri.
16286          */
16287         getUri: function () {
16288             this.isLoaded();
16289             return this.getData().uri;
16290         },
16291         /**
16292 	     * Getter for the systemCode value.
16293 	     * @returns {String} The value for systemCode.
16294 	     * @since   11.6(1)-ES1 onwards
16295 	     */
16296 	    getSystemCode: function () {
16297 	        this.isLoaded();
16298 	        return this.getData().systemCode;
16299 	    }
16300 
16301     });
16302     
16303     window.finesse = window.finesse || {};
16304     window.finesse.restservices = window.finesse.restservices || {};
16305     window.finesse.restservices.TeamNotReadyReasonCode = TeamNotReadyReasonCode;
16306         
16307     return TeamNotReadyReasonCode;
16308 });
16309 
16310 /**
16311 * JavaScript representation of the Finesse TeamNotReadyReasonCodes collection
16312 * object which contains a list of TeamNotReadyReasonCode objects.
16313  *
16314  * @requires finesse.clientservices.ClientServices
16315  * @requires Class
16316  * @requires finesse.FinesseBase
16317  * @requires finesse.restservices.RestBase
16318  * @requires finesse.restservices.Dialog
16319  * @requires finesse.restservices.RestCollectionBase
16320  */
16321 
16322 /** @private */
16323 define('restservices/TeamNotReadyReasonCodes',[
16324     'restservices/RestCollectionBase',
16325     'restservices/RestBase',
16326     'restservices/TeamNotReadyReasonCode'
16327 ],
16328 function (RestCollectionBase, RestBase, TeamNotReadyReasonCode) {
16329 
16330     var TeamNotReadyReasonCodes = RestCollectionBase.extend(/** @lends finesse.restservices.TeamNotReadyReasonCodes.prototype */{
16331 
16332       /**
16333        * @class
16334        * JavaScript representation of a TeamNotReadyReasonCodes collection object. Also exposes
16335        * methods to operate on the object against the server.
16336        *
16337        * @param {Object} options
16338        *     An object with the following properties:<ul>
16339        *         <li><b>id:</b> The id of the object being constructed</li>
16340        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16341        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16342        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16343        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16344        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16345        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16346        *             <li><b>content:</b> {String} Raw string of response</li>
16347        *             <li><b>object:</b> {Object} Parsed object of response</li>
16348        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16349        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16350        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16351        *             </ul></li>
16352        *         </ul></li>
16353        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16354        * @augments finesse.restservices.RestCollectionBase
16355        * @constructs
16356        **/
16357       init: function (options) {
16358           this._super(options);
16359       },
16360     
16361       /**
16362        * @private
16363        * Gets the REST class for the current object - this is the TeamNotReadyReasonCodes class.
16364        * @returns {Object}
16365        *     The TeamNotReadyReasonCodes constructor.
16366        */
16367       getRestClass: function () {
16368           return TeamNotReadyReasonCodes;
16369       },
16370     
16371       /**
16372        * @private
16373        * Gets the REST class for the objects that make up the collection. - this
16374        * is the TeamNotReadyReasonCode class.
16375        * @returns {finesse.restservices.TeamNotReadyReasonCode}
16376        *        The TeamNotReadyReasonCode Object
16377        * @see finesse.restservices.TeamNotReadyReasonCode
16378        */
16379       getRestItemClass: function () {
16380           return TeamNotReadyReasonCode;
16381       },
16382     
16383       /**
16384        * @private
16385        * Gets the REST type for the current object - this is a "ReasonCodes".
16386        * @returns {String} The ReasonCodes String
16387        */
16388       getRestType: function () {
16389           return "ReasonCodes";
16390       },
16391     
16392       /**
16393        * @private
16394        * Overrides the parent class.  Returns the url for the NotReadyReasonCodes resource
16395        */
16396       getRestUrl: function () {
16397           // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
16398           var restObj = this._restObj,
16399               restUrl = "";
16400           //Prepend the base REST object if one was provided.
16401           //Otherwise prepend with the default webapp name.
16402           if (restObj instanceof RestBase) {
16403               restUrl += restObj.getRestUrl();
16404           }
16405           else {
16406               restUrl += "/finesse/api";
16407           }
16408           //Append the REST type.
16409           restUrl += "/ReasonCodes?category=NOT_READY";
16410           //Append ID if it is not undefined, null, or empty.
16411           if (this._id) {
16412               restUrl += "/" + this._id;
16413           }
16414           return restUrl;
16415       },
16416     
16417       /**
16418        * @private
16419        * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
16420        */
16421       getRestItemType: function () {
16422           return "ReasonCode";
16423       },
16424     
16425       /**
16426        * @private
16427        * Override default to indicates that the collection supports making
16428        * requests.
16429        */
16430       supportsRequests: true,
16431     
16432       /**
16433        * @private
16434        * Override default to indicate that this object doesn't support subscriptions.
16435        */
16436       supportsRestItemSubscriptions: false,
16437     
16438       /**
16439        * @private
16440        * Retrieve the Not Ready Reason Codes.
16441        *
16442        * @returns {TeamNotReadyReasonCodes}
16443        *     This TeamNotReadyReasonCodes object to allow cascading.
16444        */
16445       get: function () {
16446           // set loaded to false so it will rebuild the collection after the get
16447           this._loaded = false;
16448           // reset collection
16449           this._collection = {};
16450           // perform get
16451           this._synchronize();
16452           return this;
16453       },
16454     
16455       /**
16456        * @private
16457        * Set up the PutSuccessHandler for TeamNotReadyReasonCodes
16458        * @param {Object} reasonCodes
16459        * @param {String} contentBody
16460        * @param successHandler    
16461        * @return {function}
16462        */
16463       createPutSuccessHandler: function (reasonCodes, contentBody, successHandler) {
16464           return function (rsp) {
16465               // Update internal structure based on response. Here we
16466               // inject the contentBody from the PUT request into the
16467               // rsp.object element to mimic a GET as a way to take
16468               // advantage of the existing _processResponse method.
16469               rsp.object = contentBody;
16470               reasonCodes._processResponse(rsp);
16471     
16472               //Remove the injected contentBody object before cascading response
16473               rsp.object = {};
16474     
16475               //cascade response back to consumer's response handler
16476               successHandler(rsp);
16477           };
16478       },
16479     
16480       /**
16481        * @private
16482        * Perform the REST API PUT call to update the reason code assignments for the team
16483        * @param {string[]} newValues
16484        * @param handlers     
16485        */
16486       update: function (newValues, handlers) {
16487           this.isLoaded();
16488           var contentBody = {}, contentBodyInner = [], i, innerObject = {};
16489     
16490           contentBody[this.getRestType()] = {
16491           };
16492     
16493           for (i in newValues) {
16494               if (newValues.hasOwnProperty(i)) {
16495                   innerObject = {
16496                       "uri": newValues[i]
16497                   };
16498                   contentBodyInner.push(innerObject);
16499               }
16500           }
16501     
16502           contentBody[this.getRestType()] = {
16503               "ReasonCode" : contentBodyInner
16504           };
16505     
16506           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16507           handlers = handlers || {};
16508     
16509           this.restRequest(this.getRestUrl(), {
16510               method: 'PUT',
16511               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
16512               error: handlers.error,
16513               content: contentBody
16514           });
16515     
16516           return this; // Allow cascading
16517       }
16518   });
16519   
16520     window.finesse = window.finesse || {};
16521     window.finesse.restservices = window.finesse.restservices || {};
16522     window.finesse.restservices.TeamNotReadyReasonCodes = TeamNotReadyReasonCodes;
16523     
16524   return TeamNotReadyReasonCodes;
16525 });
16526 
16527 /**
16528  * JavaScript representation of the Finesse Team Wrap Up Reason object.
16529  *
16530  * @requires finesse.clientservices.ClientServices
16531  * @requires Class
16532  * @requires finesse.FinesseBase
16533  * @requires finesse.restservices.RestBase
16534  */
16535 /** @private */
16536 define('restservices/TeamWrapUpReason',['restservices/RestBase'], function (RestBase) {
16537 
16538     var TeamWrapUpReason = RestBase.extend({
16539 
16540     /**
16541      * @class
16542      * JavaScript representation of a TeamWrapUpReason object. Also exposes
16543      * methods to operate on the object against the server.
16544      *
16545      * @param {Object} options
16546      *     An object with the following properties:<ul>
16547      *         <li><b>id:</b> The id of the object being constructed</li>
16548      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16549      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16550      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16551      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16552      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16553      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16554      *             <li><b>content:</b> {String} Raw string of response</li>
16555      *             <li><b>object:</b> {Object} Parsed object of response</li>
16556      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16557      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16558      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16559      *             </ul></li>
16560      *         </ul></li>
16561      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16562      * @constructs
16563      **/
16564     init: function (options) {
16565         this._super(options);
16566     },
16567 
16568     /**
16569      * @private
16570      * Gets the REST class for the current object - this is the TeamWrapUpReason class.
16571      * @returns {Object} The TeamWrapUpReason class.
16572      */
16573     getRestClass: function () {
16574         return TeamWrapUpReason;
16575     },
16576 
16577     /**
16578      * @private
16579      * Gets the REST type for the current object - this is a "WrapUpReason".
16580      * @returns {String} The WrapUpReason string.
16581      */
16582     getRestType: function () {
16583         return "WrapUpReason";
16584     },
16585 
16586     /**
16587      * @private
16588      * Override default to indicate that this object doesn't support making
16589      * requests.
16590      */
16591     supportsRequests: false,
16592 
16593     /**
16594      * @private
16595      * Override default to indicate that this object doesn't support subscriptions.
16596      */
16597     supportsSubscriptions: false,
16598 
16599     /**
16600      * Getter for the label.
16601      * @returns {String} The label.
16602      */
16603     getLabel: function () {
16604         this.isLoaded();
16605         return this.getData().label;
16606     },
16607 
16608     /**
16609      * @private
16610      * Getter for the forAll value.
16611      * @returns {Boolean} True if global
16612      */
16613     getForAll: function () {
16614         this.isLoaded();
16615         return this.getData().forAll;
16616     },
16617 
16618     /**
16619      * @private
16620      * Getter for the Uri value.
16621      * @returns {String} The Uri.
16622      */
16623     getUri: function () {
16624         this.isLoaded();
16625         return this.getData().uri;
16626     }
16627 	});
16628 
16629     window.finesse = window.finesse || {};
16630     window.finesse.restservices = window.finesse.restservices || {};
16631     window.finesse.restservices.TeamWrapUpReason = TeamWrapUpReason;
16632 
16633     return TeamWrapUpReason;
16634 });
16635 
16636 /**
16637 * JavaScript representation of the Finesse Team Wrap-Up Reasons collection
16638 * object which contains a list of Wrap-Up Reasons objects.
16639  *
16640  * @requires finesse.clientservices.ClientServices
16641  * @requires Class
16642  * @requires finesse.FinesseBase
16643  * @requires finesse.restservices.RestBase
16644  * @requires finesse.restservices.Dialog
16645  * @requires finesse.restservices.RestCollectionBase
16646  */
16647 /** @private */
16648 define('restservices/TeamWrapUpReasons',[
16649     'restservices/RestCollectionBase',
16650     'restservices/RestBase',
16651     'restservices/TeamWrapUpReason'
16652 ],
16653 function (RestCollectionBase, RestBase, TeamWrapUpReason) {
16654 
16655     var TeamWrapUpReasons = RestCollectionBase.extend({
16656 
16657     /**
16658      * @class
16659      * JavaScript representation of a TeamWrapUpReasons collection object. Also exposes
16660      * methods to operate on the object against the server.
16661      *
16662      * @param {Object} options
16663      *     An object with the following properties:<ul>
16664      *         <li><b>id:</b> The id of the object being constructed</li>
16665      *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16666      *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16667      *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16668      *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16669      *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16670      *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16671      *             <li><b>content:</b> {String} Raw string of response</li>
16672      *             <li><b>object:</b> {Object} Parsed object of response</li>
16673      *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16674      *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16675      *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16676      *             </ul></li>
16677      *         </ul></li>
16678      *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16679      * @constructs
16680      **/
16681     init: function (options) {
16682         this._super(options);
16683     },
16684 
16685     /**
16686      * @private
16687      * Gets the REST class for the current object - this is the TeamWrapUpReasons class.
16688      */
16689     getRestClass: function () {
16690         return TeamWrapUpReasons;
16691     },
16692 
16693     /**
16694      * @private
16695      * Gets the REST class for the objects that make up the collection. - this
16696      * is the TeamWrapUpReason class.
16697      */
16698     getRestItemClass: function () {
16699         return TeamWrapUpReason;
16700     },
16701 
16702     /**
16703      * @private
16704      * Gets the REST type for the current object - this is a "WrapUpReasons".
16705      */
16706     getRestType: function () {
16707         return "WrapUpReasons";
16708     },
16709 
16710     /**
16711      * @private
16712      * Gets the REST type for the objects that make up the collection - this is "WrapUpReason".
16713      */
16714     getRestItemType: function () {
16715         return "WrapUpReason";
16716     },
16717 
16718     /**
16719      * @private
16720      * Override default to indicates that the collection supports making
16721      * requests.
16722      */
16723     supportsRequests: true,
16724 
16725     /**
16726      * @private
16727      * Override default to indicate that this object doesn't support subscriptions.
16728      */
16729     supportsRestItemSubscriptions: false,
16730 
16731     /**
16732      * Retrieve the Team Wrap Up Reasons.
16733      *
16734      * @returns {finesse.restservices.TeamWrapUpReasons}
16735      *     This TeamWrapUpReasons object to allow cascading.
16736      */
16737     get: function () {
16738         // set loaded to false so it will rebuild the collection after the get
16739         this._loaded = false;
16740         // reset collection
16741         this._collection = {};
16742         // perform get
16743         this._synchronize();
16744         return this;
16745     },
16746 
16747     /**
16748      * Set up the PutSuccessHandler for TeamWrapUpReasons
16749      * @param {Object} wrapUpReasons
16750      * @param {Object} contentBody
16751      * @param successHandler
16752      * @returns response
16753      */
16754     createPutSuccessHandler: function (wrapUpReasons, contentBody, successHandler) {
16755         return function (rsp) {
16756             // Update internal structure based on response. Here we
16757             // inject the contentBody from the PUT request into the
16758             // rsp.object element to mimic a GET as a way to take
16759             // advantage of the existing _processResponse method.
16760             rsp.object = contentBody;
16761             
16762             wrapUpReasons._processResponse(rsp);
16763 
16764             //Remove the injected contentBody object before cascading response
16765             rsp.object = {};
16766 
16767             //cascade response back to consumer's response handler
16768             successHandler(rsp);
16769         };
16770     },
16771 
16772     /**    
16773      * Perform the REST API PUT call to update the reason code assignments for the team
16774      * @param {String Array} newValues
16775      * @param handlers
16776      * @returns {Object} this
16777      */
16778     update: function (newValues, handlers) {
16779         this.isLoaded();
16780         var contentBody = {}, contentBodyInner = [], i, innerObject = {};
16781 
16782         contentBody[this.getRestType()] = {
16783         };
16784 
16785         for (i in newValues) {
16786             if (newValues.hasOwnProperty(i)) {
16787                 innerObject = {
16788                     "uri": newValues[i]
16789                 };
16790                 contentBodyInner.push(innerObject);
16791             }
16792         }
16793 
16794         contentBody[this.getRestType()] = {
16795             "WrapUpReason" : contentBodyInner
16796         };
16797 
16798         // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
16799         handlers = handlers || {};
16800 
16801         this.restRequest(this.getRestUrl(), {
16802             method: 'PUT',
16803             success: this.createPutSuccessHandler(this, contentBody, handlers.success),
16804             error: handlers.error,
16805             content: contentBody
16806         });
16807 
16808         return this; // Allow cascading
16809     }
16810 	});
16811 
16812     window.finesse = window.finesse || {};
16813     window.finesse.restservices = window.finesse.restservices || {};
16814     window.finesse.restservices.TeamWrapUpReasons = TeamWrapUpReasons;
16815 
16816     return TeamWrapUpReasons;
16817 });
16818 
16819 /**
16820  * JavaScript representation of a TeamSignOutReasonCode.
16821  *
16822  * @requires finesse.clientservices.ClientServices
16823  * @requires Class
16824  * @requires finesse.FinesseBase
16825  * @requires finesse.restservices.RestBase
16826  */
16827 
16828 /** @private */
16829 define('restservices/TeamSignOutReasonCode',['restservices/RestBase'], function (RestBase) {
16830     var TeamSignOutReasonCode = RestBase.extend(/** @lends finesse.restservices.TeamSignOutReasonCode.prototype */{
16831 
16832         /**
16833          * @class
16834          * JavaScript representation of a TeamSignOutReasonCode object. Also exposes
16835          * methods to operate on the object against the server.
16836          *
16837          * @param {Object} options
16838          *     An object with the following properties:<ul>
16839          *         <li><b>id:</b> The id of the object being constructed</li>
16840          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16841          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16842          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16843          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16844          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16845          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16846          *             <li><b>content:</b> {String} Raw string of response</li>
16847          *             <li><b>object:</b> {Object} Parsed object of response</li>
16848          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16849          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16850          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16851          *             </ul></li>
16852          *         </ul></li>
16853          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16854          * @constructs
16855          * @ignore
16856          **/
16857         init: function (options) {
16858             this._super(options);
16859         },
16860 
16861         /**
16862          * @private
16863          * Gets the REST class for the current object - this is the TeamSignOutReasonCode class.
16864          * @returns {Object} The TeamSignOutReasonCode class.
16865          */
16866         getRestClass: function () {
16867             return TeamSignOutReasonCode;
16868         },
16869 
16870         /**
16871          * @private
16872          * Gets the REST type for the current object - this is a "ReasonCode".
16873          * @returns {String} The ReasonCode string.
16874          */
16875         getRestType: function () {
16876             return "ReasonCode";
16877         },
16878 
16879         /**
16880          * @private
16881          * Override default to indicate that this object doesn't support making
16882          * requests.
16883          */
16884         supportsRequests: false,
16885 
16886         /**
16887          * @private
16888          * Override default to indicate that this object doesn't support subscriptions.
16889          */
16890         supportsSubscriptions: false,
16891 
16892         /**
16893          * Getter for the category.
16894          * @returns {String} The category.
16895          */
16896         getCategory: function () {
16897             this.isLoaded();
16898             return this.getData().category;
16899         },
16900 
16901         /**
16902          * Getter for the code.
16903          * @returns {String} The code.
16904          */
16905         getCode: function () {
16906             this.isLoaded();
16907             return this.getData().code;
16908         },
16909 
16910         /**
16911          * Getter for the label.
16912          * @returns {String} The label.
16913          */
16914         getLabel: function () {
16915             this.isLoaded();
16916             return this.getData().label;
16917         },
16918 
16919         /**
16920          * Getter for the forAll value.
16921          * @returns {String} The forAll.
16922          */
16923         getForAll: function () {
16924             this.isLoaded();
16925             return this.getData().forAll;
16926         },
16927 
16928         /**
16929          * Getter for the Uri value.
16930          * @returns {String} The Uri.
16931          */
16932         getUri: function () {
16933             this.isLoaded();
16934             return this.getData().uri;
16935         },
16936         /**
16937 	     * Getter for the systemCode value.
16938 	     * @returns {String} The value for systemCode.
16939 	     * @since   11.6(1)-ES1 onwards
16940 	     */
16941 	    getSystemCode: function () {
16942 	        this.isLoaded();
16943 	        return this.getData().systemCode;
16944 	    }
16945 
16946     });
16947 
16948     window.finesse = window.finesse || {};
16949     window.finesse.restservices = window.finesse.restservices || {};
16950     window.finesse.restservices.TeamSignOutReasonCode = TeamSignOutReasonCode;
16951     
16952     return TeamSignOutReasonCode;
16953 });
16954 
16955 /**
16956 * JavaScript representation of the TeamSignOutReasonCodes collection
16957 * object which contains a list of TeamSignOutReasonCode objects.
16958  *
16959  * @requires finesse.clientservices.ClientServices
16960  * @requires Class
16961  * @requires finesse.FinesseBase
16962  * @requires finesse.restservices.RestBase
16963  * @requires finesse.restservices.Dialog
16964  * @requires finesse.restservices.RestCollectionBase
16965  */
16966 
16967 /** @private */
16968 define('restservices/TeamSignOutReasonCodes',[
16969     'restservices/RestCollectionBase',
16970     'restservices/RestBase',
16971     'restservices/TeamSignOutReasonCode'
16972 ],
16973 function (RestCollectionBase, RestBase, TeamSignOutReasonCode) {
16974     
16975     var TeamSignOutReasonCodes = RestCollectionBase.extend(/** @lends finesse.restservices.TeamSignOutReasonCodes.prototype */{
16976         /**
16977          * @class
16978          * JavaScript representation of a TeamSignOutReasonCodes collection object. Also exposes
16979          * methods to operate on the object against the server.
16980          *
16981          * @param {Object} options
16982          *     An object with the following properties:<ul>
16983          *         <li><b>id:</b> The id of the object being constructed</li>
16984          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
16985          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
16986          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
16987          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
16988          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
16989          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
16990          *             <li><b>content:</b> {String} Raw string of response</li>
16991          *             <li><b>object:</b> {Object} Parsed object of response</li>
16992          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
16993          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
16994          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
16995          *             </ul></li>
16996          *         </ul></li>
16997          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
16998          * @constructs
16999          **/
17000         init: function (options) {
17001             this._super(options);
17002         },
17003 
17004         /**
17005          * @private
17006          * Gets the REST class for the current object - this is the TeamSignOutReasonCodes class.
17007          * @returns {Object}
17008          *      The TeamSignOutReasonCodes constructor.
17009          */
17010         getRestClass: function () {
17011             return TeamSignOutReasonCodes;
17012         },
17013 
17014         /**
17015          * @private
17016          * Gets the REST class for the objects that make up the collection. - this
17017          * is the TeamSignOutReasonCode class.
17018          * @returns {finesse.restservices.TeamSignOutReasonCode}
17019          *      The TeamSignOutReasonCode Object
17020          * @see finesse.restservices.TeamSignOutReasonCode
17021          */
17022         getRestItemClass: function () {
17023             return TeamSignOutReasonCode;
17024         },
17025 
17026         /**
17027          * @private
17028          * Gets the REST type for the current object - this is a "ReasonCodes".
17029          * @returns {String} The ReasonCodes String
17030          */
17031         getRestType: function () {
17032             return "ReasonCodes";
17033         },
17034 
17035         /**
17036          * Overrides the parent class.  Returns the url for the SignOutReasonCodes resource
17037          */
17038         getRestUrl: function () {
17039             var restObj = this._restObj, restUrl = "";
17040 
17041             //Prepend the base REST object if one was provided.
17042             //Otherwise prepend with the default webapp name.
17043             if (restObj instanceof RestBase) {
17044                 restUrl += restObj.getRestUrl();
17045             } else {
17046                 restUrl += "/finesse/api";
17047             }
17048             //Append the REST type.
17049             restUrl += "/ReasonCodes?category=LOGOUT";
17050             //Append ID if it is not undefined, null, or empty.
17051             if (this._id) {
17052                 restUrl += "/" + this._id;
17053             }
17054             return restUrl;
17055         },
17056 
17057         /**
17058          * @private
17059          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
17060          */
17061         getRestItemType: function () {
17062             return "ReasonCode";
17063         },
17064 
17065         /**
17066          * @private
17067          * Override default to indicates that the collection supports making requests.
17068          */
17069         supportsRequests: true,
17070 
17071         /**
17072          * @private
17073          * Override default to indicates that the collection does not subscribe to its objects.
17074          */
17075         supportsRestItemSubscriptions: false,
17076 
17077         /**
17078          * Retrieve the Sign Out Reason Codes.
17079          *
17080          * @returns {finesse.restservices.TeamSignOutReasonCodes}
17081          *     This TeamSignOutReasonCodes object to allow cascading.
17082          */
17083         get: function () {
17084             // set loaded to false so it will rebuild the collection after the get
17085             this._loaded = false;
17086             // reset collection
17087             this._collection = {};
17088             // perform get
17089             this._synchronize();
17090             return this;
17091         },
17092 
17093         /* We only use PUT and GET on Reason Code team assignments
17094          * @param {Object} contact
17095          * @param {Object} contentBody
17096          * @param {Function} successHandler
17097          */
17098         createPutSuccessHandler: function (contact, contentBody, successHandler) {
17099             return function (rsp) {
17100                 // Update internal structure based on response. Here we
17101                 // inject the contentBody from the PUT request into the
17102                 // rsp.object element to mimic a GET as a way to take
17103                 // advantage of the existing _processResponse method.
17104                 rsp.object = contentBody;
17105                 contact._processResponse(rsp);
17106 
17107                 //Remove the injected contentBody object before cascading response
17108                 rsp.object = {};
17109 
17110                 //cascade response back to consumer's response handler
17111                 successHandler(rsp);
17112             };
17113         },
17114 
17115         /**
17116          * Update - This should be all that is needed.
17117          * @param {Object} newValues
17118          * @param {Object} handlers
17119          * @returns {finesse.restservices.TeamSignOutReasonCodes}
17120          *     This TeamSignOutReasonCodes object to allow cascading.
17121          */
17122         update: function (newValues, handlers) {
17123             this.isLoaded();
17124             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
17125 
17126             contentBody[this.getRestType()] = {
17127             };
17128 
17129             for (i in newValues) {
17130                 if (newValues.hasOwnProperty(i)) {
17131                     innerObject = {
17132                         "uri": newValues[i]
17133                     };
17134                     contentBodyInner.push(innerObject);
17135                 }
17136             }
17137 
17138             contentBody[this.getRestType()] = {
17139                 "ReasonCode" : contentBodyInner
17140             };
17141 
17142             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17143             handlers = handlers || {};
17144 
17145             this.restRequest(this.getRestUrl(), {
17146                 method: 'PUT',
17147                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17148                 error: handlers.error,
17149                 content: contentBody
17150             });
17151 
17152             return this; // Allow cascading
17153         }
17154 
17155     });
17156     
17157     window.finesse = window.finesse || {};
17158     window.finesse.restservices = window.finesse.restservices || {};
17159     window.finesse.restservices.TeamSignOutReasonCodes = TeamSignOutReasonCodes;
17160     
17161     return TeamSignOutReasonCodes;
17162 });
17163 
17164 /**
17165  * JavaScript representation of the Finesse PhoneBook Assignment object.
17166  *
17167  * @requires finesse.clientservices.ClientServices
17168  * @requires Class
17169  * @requires finesse.FinesseBase
17170  * @requires finesse.restservices.RestBase
17171  */
17172 
17173 /**
17174  * The following comment prevents JSLint errors concerning undefined global variables.
17175  * It tells JSLint that these identifiers are defined elsewhere.
17176  */
17177 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
17178 
17179 /** The following comment is to prevent jslint errors about 
17180  * using variables before they are defined.
17181  */
17182 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
17183 
17184 /** @private */
17185 define('restservices/TeamPhoneBook',['restservices/RestBase'], function (RestBase) {
17186     var TeamPhoneBook = RestBase.extend({
17187 
17188         /**
17189          * @class
17190          * JavaScript representation of a PhoneBook object. Also exposes
17191          * methods to operate on the object against the server.
17192          *
17193          * @param {Object} options
17194          *     An object with the following properties:<ul>
17195          *         <li><b>id:</b> The id of the object being constructed</li>
17196          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17197          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17198          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17199          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17200          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17201          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17202          *             <li><b>content:</b> {String} Raw string of response</li>
17203          *             <li><b>object:</b> {Object} Parsed object of response</li>
17204          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17205          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17206          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17207          *             </ul></li>
17208          *         </ul></li>
17209          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17210          * @constructs
17211          **/
17212         init: function (options) {
17213             this._super(options);
17214         },
17215 
17216         /**
17217          * @private
17218          * Gets the REST class for the current object - this is the PhoneBooks class.
17219          * @returns {Object} The PhoneBooks class.
17220          */
17221         getRestClass: function () {
17222             return TeamPhoneBook;
17223         },
17224 
17225         /**
17226          * @private
17227          * Gets the REST type for the current object - this is a "PhoneBook".
17228          * @returns {String} The PhoneBook string.
17229          */
17230         getRestType: function () {
17231             return "PhoneBook";
17232         },
17233 
17234         /**
17235          * @private
17236          * Override default to indicate that this object doesn't support making
17237          * requests.
17238          */
17239         supportsRequests: false,
17240 
17241         /**
17242          * @private
17243          * Override default to indicate that this object doesn't support subscriptions.
17244          */
17245         supportsSubscriptions: false,
17246 
17247         /**
17248          * Getter for the name.
17249          * @returns {String} The name.
17250          */
17251         getName: function () {
17252             this.isLoaded();
17253             return this.getData().name;
17254         },
17255 
17256         /**
17257          * Getter for the Uri value.
17258          * @returns {String} The Uri.
17259          */
17260         getUri: function () {
17261             this.isLoaded();
17262             return this.getData().uri;
17263         }
17264 
17265     });
17266 
17267     window.finesse = window.finesse || {};
17268     window.finesse.restservices = window.finesse.restservices || {};
17269     window.finesse.restservices.TeamPhoneBook = TeamPhoneBook;
17270     
17271     return TeamPhoneBook;
17272 });
17273 
17274 /**
17275 * JavaScript representation of the Finesse PhoneBook Assignments collection
17276 * object which contains a list of Not Ready Reason Codes objects.
17277  *
17278  * @requires finesse.clientservices.ClientServices
17279  * @requires Class
17280  * @requires finesse.FinesseBase
17281  * @requires finesse.restservices.RestBase
17282  * @requires finesse.restservices.Dialog
17283  * @requires finesse.restservices.RestCollectionBase
17284  */
17285 
17286 /**
17287  * The following comment prevents JSLint errors concerning undefined global variables.
17288  * It tells JSLint that these identifiers are defined elsewhere.
17289  */
17290 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
17291 
17292 /** The following comment is to prevent jslint errors about 
17293  * using variables before they are defined.
17294  */
17295 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
17296 
17297 /** @private */
17298 define('restservices/TeamPhoneBooks',[
17299     'restservices/RestCollectionBase',
17300     'restservices/RestBase',
17301     'restservices/TeamPhoneBook'
17302 ],
17303 function (RestCollectionBase, RestBase, TeamPhoneBook) {
17304     var TeamPhoneBooks = RestCollectionBase.extend({
17305         
17306         /**
17307          * @class
17308          * JavaScript representation of a TeamPhoneBooks collection object. Also exposes
17309          * methods to operate on the object against the server.
17310          *
17311          * @param {Object} options
17312          *     An object with the following properties:<ul>
17313          *         <li><b>id:</b> The id of the object being constructed</li>
17314          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17315          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17316          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17317          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17318          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17319          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17320          *             <li><b>content:</b> {String} Raw string of response</li>
17321          *             <li><b>object:</b> {Object} Parsed object of response</li>
17322          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17323          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17324          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17325          *             </ul></li>
17326          *         </ul></li>
17327          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17328          * @constructs
17329          **/
17330         init: function (options) {
17331             this._super(options);           
17332         },
17333 
17334         /**
17335          * @private
17336          * Gets the REST class for the current object - this is the TeamPhoneBooks class.
17337          */
17338         getRestClass: function () {
17339             return TeamPhoneBooks;
17340         },
17341 
17342         /**
17343          * @private
17344          * Gets the REST class for the objects that make up the collection. - this
17345          * is the TeamPhoneBooks class.
17346          */
17347         getRestItemClass: function () {
17348             return TeamPhoneBook;
17349         },
17350 
17351         /**
17352          * @private
17353          * Gets the REST type for the current object - this is a "ReasonCodes".
17354          */
17355         getRestType: function () {
17356             return "PhoneBooks";
17357         },
17358         
17359         /**
17360          * Overrides the parent class.  Returns the url for the PhoneBooks resource
17361          */
17362         getRestUrl: function () {
17363             // return ("/finesse/api/" + this.getRestType() + "?category=NOT_READY");
17364             var restObj = this._restObj,
17365             restUrl = "";
17366             //Prepend the base REST object if one was provided.
17367             if (restObj instanceof RestBase) {
17368                 restUrl += restObj.getRestUrl();
17369             }
17370             //Otherwise prepend with the default webapp name.
17371             else {
17372                 restUrl += "/finesse/api";
17373             }
17374             //Append the REST type.
17375             restUrl += "/PhoneBooks";
17376             //Append ID if it is not undefined, null, or empty.
17377             if (this._id) {
17378                 restUrl += "/" + this._id;
17379             }
17380             return restUrl;        
17381         },
17382         
17383         /**
17384          * @private
17385          * Gets the REST type for the objects that make up the collection - this is "ReasonCode".
17386          */
17387         getRestItemType: function () {
17388             return "PhoneBook";
17389         },
17390 
17391         /**
17392          * @private
17393          * Override default to indicates that the collection supports making
17394          * requests.
17395          */
17396         supportsRequests: true,
17397 
17398         /**
17399          * @private
17400          * Override default to indicates that the collection subscribes to its objects.
17401          */
17402         supportsRestItemSubscriptions: false,
17403         
17404         /**
17405          * Retrieve the Not Ready Reason Codes.
17406          *
17407          * @returns {finesse.restservices.TeamPhoneBooks}
17408          *     This TeamPhoneBooks object to allow cascading.
17409          */
17410         get: function () {
17411             // set loaded to false so it will rebuild the collection after the get
17412             /** @private */
17413             this._loaded = false;
17414             // reset collection
17415             /** @private */
17416             this._collection = {};
17417             // perform get
17418             this._synchronize();
17419             return this;
17420         },
17421 
17422         /* We only use PUT and GET on Reason Code team assignments 
17423          */
17424         createPutSuccessHandler: function(contact, contentBody, successHandler){
17425             return function (rsp) {
17426                 // Update internal structure based on response. Here we
17427                 // inject the contentBody from the PUT request into the
17428                 // rsp.object element to mimic a GET as a way to take
17429                 // advantage of the existing _processResponse method.
17430                 rsp.object = contentBody;
17431                 contact._processResponse(rsp);
17432 
17433                 //Remove the injected Contact object before cascading response
17434                 rsp.object = {};
17435                 
17436                 //cascade response back to consumer's response handler
17437                 successHandler(rsp);
17438             };
17439         },
17440 
17441         /**
17442          * Update - This should be all that is needed.
17443          */
17444         update: function (newValues, handlers) {
17445             this.isLoaded();
17446             var contentBody = {}, contentBodyInner = [], i, innerObject;
17447 
17448             contentBody[this.getRestType()] = {
17449             };
17450         
17451             for (i in newValues) {
17452                 if (newValues.hasOwnProperty(i)) {
17453                     innerObject = {};
17454                     innerObject = {
17455                         "uri": newValues[i]
17456                     };
17457                     contentBodyInner.push(innerObject);
17458                 }
17459             }
17460 
17461             contentBody[this.getRestType()] = {
17462                 "PhoneBook" : contentBodyInner
17463             };
17464 
17465             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17466             handlers = handlers || {};
17467 
17468             this.restRequest(this.getRestUrl(), {
17469                 method: 'PUT',
17470                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17471                 error: handlers.error,
17472                 content: contentBody
17473             });
17474 
17475             return this; // Allow cascading
17476         }       
17477         
17478     });
17479         
17480     window.finesse = window.finesse || {};
17481     window.finesse.restservices = window.finesse.restservices || {};
17482     window.finesse.restservices.TeamPhoneBooks = TeamPhoneBooks;
17483     
17484     return TeamPhoneBooks;
17485 });
17486 
17487 /**
17488  * JavaScript representation of the Finesse LayoutConfig object
17489  * @requires ClientServices
17490  * @requires finesse.FinesseBase
17491  * @requires finesse.restservices.RestBase
17492  */
17493 
17494 /** @private */
17495 define('restservices/LayoutConfig',['restservices/RestBase'], function (RestBase) {
17496     /** @private */
17497 	var LayoutConfig = RestBase.extend({
17498 
17499 		/**
17500 		 * @class
17501 		 * JavaScript representation of a LayoutConfig object. Also exposes methods to operate
17502 		 * on the object against the server.
17503 		 *
17504 		 * @param {String} id
17505 		 *     Not required...
17506 		 * @param {Object} callbacks
17507 		 *     An object containing callbacks for instantiation and runtime
17508 		 * @param {Function} callbacks.onLoad(this)
17509 		 *     Callback to invoke upon successful instantiation
17510 		 * @param {Function} callbacks.onLoadError(rsp)
17511 		 *     Callback to invoke on instantiation REST request error
17512 		 *     as passed by finesse.clientservices.ClientServices.ajax()
17513 		 *     {
17514 		 *         status: {Number} The HTTP status code returned
17515 		 *         content: {String} Raw string of response
17516 		 *         object: {Object} Parsed object of response
17517 		 *         error: {Object} Wrapped exception that was caught
17518 		 *         error.errorType: {String} Type of error that was caught
17519 		 *         error.errorMessage: {String} Message associated with error
17520 		 *     }
17521 		 * @param {Function} callbacks.onChange(this)
17522 		 *     Callback to invoke upon successful update
17523 		 * @param {Function} callbacks.onError(rsp)
17524 		 *     Callback to invoke on update error (refresh or event)
17525 		 *     as passed by finesse.clientservices.ClientServices.ajax()
17526 		 *     {
17527 		 *         status: {Number} The HTTP status code returned
17528 		 *         content: {String} Raw string of response
17529 		 *         object: {Object} Parsed object of response
17530 		 *         error: {Object} Wrapped exception that was caught
17531 		 *         error.errorType: {String} Type of error that was caught
17532 		 *         error.errorMessage: {String} Message associated with error
17533 		 *     }
17534 		 *  
17535 	     * @constructs
17536 		 */
17537 		init: function (callbacks) {
17538 			this._super("", callbacks);
17539 			//when post is performed and id is empty
17540 			/*if (id === "") {
17541 				this._loaded = true;
17542 			}*/
17543 	        this._layoutxml = {};
17544 		},
17545 	
17546 		/**
17547 		 * Returns REST class of LayoutConfig object
17548 		 */
17549 		getRestClass: function () {
17550 			return LayoutConfig;
17551 		},
17552 	
17553 		/**
17554 		 * The type of this REST object is LayoutConfig
17555 		 */
17556 		getRestType: function () {
17557 			return "LayoutConfig";
17558 		},
17559 
17560 		/**
17561 		 * Gets the REST URL of this object.
17562 		 * 
17563 		 * If the parent has an id, the id is appended.
17564 		 * On occasions of POST, it will not have an id.
17565 		 */
17566 		getRestUrl: function () {
17567 			var layoutUri = "/finesse/api/" + this.getRestType() + "/default";
17568 			/*if (this._id) {
17569 				layoutUri = layoutUri + "/" + this._id;
17570 			}*/
17571 			return layoutUri;
17572 		},
17573 	
17574 		/**
17575 		 * This API does not support subscription
17576 		 */
17577 		supportsSubscriptions: false,
17578 		
17579 		keepRestResponse: true,
17580 
17581 
17582 		/**
17583 		 * Gets finesselayout.xml retrieved from the API call
17584 		 */
17585 		getLayoutxml: function () {
17586 			this.isLoaded();
17587 			var layoutxml = this.getData().layoutxml;
17588 
17589             // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with update())
17590             layoutxml = layoutxml.replace(/&/g,"&");
17591 
17592             return layoutxml;
17593 		},
17594 	
17595 		/**
17596 		 * Gets the type of this LayoutConfig object
17597 		 */
17598 		/*
17599 		getType: function () {
17600 			this.isLoaded();
17601 			return this.getData().type;
17602 		},*/
17603 	
17604 		/**
17605 		 * Retrieve the LayoutConfig settings.
17606 		 * If the id is not provided the API call will fail.
17607 		 * @returns {LayoutConfig}
17608 		 *     This LayoutConfig object to allow cascading.
17609 		 */
17610 		get: function () {      
17611 			this._synchronize();
17612 			return this;
17613 		},
17614 
17615 		/**
17616 		 * Closure handle updating of the internal data for the LayoutConfig object
17617 		 * upon a successful update (PUT) request before calling the intended
17618 		 * success handler provided by the consumer
17619 		 * 
17620 		 * @param {Object}
17621 		 *            layoutconfig Reference to this LayoutConfig object
17622 		 * @param {Object}
17623 		 *            LayoutConfig Object that contains the  settings to be
17624 		 *            submitted in the api request
17625 		 * @param {Function}
17626 		 *            successHandler The success handler specified by the consumer
17627 		 *            of this object
17628 		 * @returns {LayoutConfig} This LayoutConfig object to allow cascading
17629 		 */
17630 	
17631 		createPutSuccessHandler: function (layoutconfig, contentBody, successHandler) {
17632 			return function (rsp) {			
17633 				// Update internal structure based on response. Here we
17634 				// inject the contentBody from the PUT request into the
17635 				// rsp.object element to mimic a GET as a way to take
17636 				// advantage of the existing _processResponse method.
17637 				rsp.content = contentBody;
17638 				rsp.object.LayoutConfig = {};
17639 				rsp.object.LayoutConfig.finesseLayout = contentBody;
17640 				layoutconfig._processResponse(rsp);
17641 	
17642 				//Remove the injected layoutConfig object before cascading response
17643 				rsp.object.LayoutConfig = {};
17644 	
17645 				//cascade response back to consumer's response handler
17646 				successHandler(rsp);
17647 			};
17648 		},
17649 	
17650 		/**
17651 		 *  Update LayoutConfig
17652 		 * @param {Object} finesselayout
17653 		 *     The XML for FinesseLayout being stored
17654 		 * 
17655 		 * @param {Object} handlers
17656 		 *     An object containing callback handlers for the request. Optional.
17657 		 * @param {Function} options.success(rsp)
17658 		 *     A callback function to be invoked for a successful request.
17659 		 *     {
17660 		 *         status: {Number} The HTTP status code returned
17661 		 *         content: {String} Raw string of response
17662 		 *         object: {Object} Parsed object of response
17663 		 *     }
17664 		 * @param {Function} options.error(rsp)
17665 		 *     A callback function to be invoked for an unsuccessful request.
17666 		 *     {
17667 		 *         status: {Number} The HTTP status code returned
17668 		 *         content: {String} Raw string of response
17669 		 *         object: {Object} Parsed object of response (HTTP errors)
17670 		 *         error: {Object} Wrapped exception that was caught
17671 		 *         error.errorType: {String} Type of error that was caught
17672 		 *         error.errorMessage: {String} Message associated with error
17673 		 *     }
17674 		 * @returns {finesse.restservices.LayoutConfig}
17675 		 *     This LayoutConfig object to allow cascading
17676 		 */
17677 	
17678 		update: function (layoutxml, handlers) {
17679 			this.isLoaded();
17680 
17681 			
17682 			var contentBody = {}, 
17683 			//Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
17684 			re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
17685 
17686 			// We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
17687 			layoutxml = layoutxml.replace(/&(?!amp;)/g, "&");
17688 
17689 			//used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
17690 			layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
17691 
17692 			contentBody[this.getRestType()] = {
17693 				"layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
17694 			};
17695 
17696 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17697 			handlers = handlers || {};
17698 
17699 			this.restRequest(this.getRestUrl(), {
17700 				method: 'PUT',
17701 				success: this.createPutSuccessHandler(this, layoutxml, handlers.success),
17702 				error: handlers.error,
17703 				content: contentBody
17704 			});
17705 
17706 			return this; // Allow cascading
17707 		}
17708 	
17709 		/**
17710 		 *TODO createPostSuccessHandler needs to be debugged to make it working
17711 		 * Closure handle creating new  LayoutConfig object
17712 		 * upon a successful create (POST) request before calling the intended
17713 		 * success handler provided by the consumer
17714 		 * 
17715 		 * @param {Object}
17716 		 *            layoutconfig Reference to this LayoutConfig object
17717 		 * @param {Object}
17718 		 *            LayoutConfig Object that contains the  settings to be
17719 		 *            submitted in the api request
17720 		 * @param {Function}
17721 		 *            successHandler The success handler specified by the consumer
17722 		 *            of this object
17723 		 * @returns {finesse.restservices.LayoutConfig} This LayoutConfig object to allow cascading
17724 		 */
17725 	/*
17726 		createPostSuccessHandler: function (layoutconfig, contentBody, successHandler) {
17727 			return function (rsp) {
17728 	
17729 				rsp.object = contentBody;
17730 				layoutconfig._processResponse(rsp);
17731 	
17732 				//Remove the injected layoutConfig object before cascading response
17733 				rsp.object = {};
17734 	
17735 				//cascade response back to consumer's response handler
17736 				successHandler(rsp);
17737 			};
17738 		}, */
17739 	
17740 		/**
17741 		 * TODO Method needs to be debugged to make POST working
17742 		 *  Add LayoutConfig
17743 		 * @param {Object} finesselayout
17744 		 *     The XML for FinesseLayout being stored
17745 		 * 
17746 		 * @param {Object} handlers
17747 		 *     An object containing callback handlers for the request. Optional.
17748 		 * @param {Function} options.success(rsp)
17749 		 *     A callback function to be invoked for a successful request.
17750 		 *     {
17751 		 *         status: {Number} The HTTP status code returned
17752 		 *         content: {String} Raw string of response
17753 		 *         object: {Object} Parsed object of response
17754 		 *     }
17755 		 * @param {Function} options.error(rsp)
17756 		 *     A callback function to be invoked for an unsuccessful request.
17757 		 *     {
17758 		 *         status: {Number} The HTTP status code returned
17759 		 *         content: {String} Raw string of response
17760 		 *         object: {Object} Parsed object of response (HTTP errors)
17761 		 *         error: {Object} Wrapped exception that was caught
17762 		 *         error.errorType: {String} Type of error that was caught
17763 		 *         error.errorMessage: {String} Message associated with error
17764 		 *     }
17765 		 * @returns {finesse.restservices.LayoutConfig}
17766 		 *     This LayoutConfig object to allow cascading
17767 		 */
17768 	/*
17769 		add: function (layoutxml, handlers) {
17770 			this.isLoaded();
17771 			var contentBody = {};
17772 	
17773 	
17774 			contentBody[this.getRestType()] = {
17775 					"layoutxml": layoutxml,
17776 					"type": "current"
17777 			    };
17778 	
17779 			// Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17780 			handlers = handlers || {};
17781 	
17782 			this.restRequest(this.getRestUrl(), {
17783 				method: 'POST',
17784 				success: this.createPostSuccessHandler(this, contentBody, handlers.success),
17785 				error: handlers.error,
17786 				content: contentBody
17787 			});
17788 	
17789 			return this; // Allow cascading
17790 		} */
17791 	});
17792 	
17793 	window.finesse = window.finesse || {};
17794     window.finesse.restservices = window.finesse.restservices || {};
17795     window.finesse.restservices.LayoutConfig = LayoutConfig;
17796     
17797 	return LayoutConfig;
17798 	
17799 });
17800 
17801 /**
17802  * JavaScript representation of the Finesse LayoutConfig object for a Team.
17803  *
17804  * @requires finesse.clientservices.ClientServices
17805  * @requires Class
17806  * @requires finesse.FinesseBase
17807  * @requires finesse.restservices.RestBase
17808  * @requires finesse.utilities.Utilities
17809  * @requires finesse.restservices.LayoutConfig
17810  */
17811 
17812 /** The following comment is to prevent jslint errors about 
17813  * using variables before they are defined.
17814  */
17815 /*global Exception */
17816 
17817 /** @private */
17818 define('restservices/TeamLayoutConfig',[
17819     'restservices/RestBase',
17820     'utilities/Utilities',
17821     'restservices/LayoutConfig'
17822 ],
17823 function (RestBase, Utilities, LayoutConfig) {
17824     
17825     var TeamLayoutConfig = RestBase.extend({
17826       // Keep the restresponse so we can parse the layoutxml out of it in getLayoutXML()
17827       keepRestResponse: true,
17828     
17829       /**
17830        * @class
17831        * JavaScript representation of a LayoutConfig object for a Team. Also exposes
17832        * methods to operate on the object against the server.
17833        *
17834        * @param {Object} options
17835        *     An object with the following properties:<ul>
17836        *         <li><b>id:</b> The id of the object being constructed</li>
17837        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
17838        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
17839        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
17840        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
17841        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
17842        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
17843        *             <li><b>content:</b> {String} Raw string of response</li>
17844        *             <li><b>object:</b> {Object} Parsed object of response</li>
17845        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
17846        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
17847        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
17848        *             </ul></li>
17849        *         </ul></li>
17850        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
17851        * @constructs
17852        **/
17853       init: function (options) {
17854           this._super(options);
17855       },
17856     
17857       /**
17858        * @private
17859        * Gets the REST class for the current object - this is the LayoutConfigs class.
17860        * @returns {Object} The LayoutConfigs class.
17861        */
17862       getRestClass: function () {
17863           return TeamLayoutConfig;
17864       },
17865     
17866       /**
17867        * @private
17868        * Gets the REST type for the current object - this is a "LayoutConfig".
17869        * @returns {String} The LayoutConfig string.
17870        */
17871       getRestType: function () {
17872           return "TeamLayoutConfig";
17873       },
17874     
17875       /**
17876        * @private
17877        * Override default to indicate that this object doesn't support making
17878        * requests.
17879        */
17880       supportsRequests: false,
17881     
17882       /**
17883        * @private
17884        * Override default to indicate that this object doesn't support subscriptions.
17885        */
17886       supportsSubscriptions: false,
17887     
17888       /**
17889        * Getter for the category.
17890        * @returns {String} The category.
17891        */
17892       getLayoutXML: function () {
17893           this.isLoaded();
17894           var layoutxml = this.getData().layoutxml;
17895 
17896           // We need to unescape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with put())
17897           layoutxml = layoutxml.replace(/&/g,"&");
17898 
17899           return layoutxml;
17900       },
17901     
17902       /**
17903        * Getter for the code.
17904        * @returns {String} The code.
17905        */
17906       getUseDefault: function () {
17907           this.isLoaded();
17908           return this.getData().useDefault;
17909       },
17910       
17911       /**
17912        * Retrieve the TeamLayoutConfig.
17913        *
17914        * @returns {finesse.restservices.TeamLayoutConfig}
17915        */
17916       get: function () {
17917           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
17918           this._id = "0";
17919           // set loaded to false so it will rebuild the collection after the get
17920           this._loaded = false;
17921           // reset collection
17922           this._collection = {};
17923           // perform get
17924           this._synchronize();
17925           return this;
17926       },
17927     
17928       createPutSuccessHandler: function(contact, contentBody, successHandler){
17929           return function (rsp) {
17930               // Update internal structure based on response. Here we
17931               // inject the contentBody from the PUT request into the
17932               // rsp.object element to mimic a GET as a way to take
17933               // advantage of the existing _processResponse method.
17934               rsp.object = contentBody;
17935               contact._processResponse(rsp);
17936     
17937               //Remove the injected Contact object before cascading response
17938               rsp.object = {};
17939               
17940               //cascade response back to consumer's response handler
17941               successHandler(rsp);
17942           };
17943       },
17944       
17945       put: function (newValues, handlers) {
17946           // this._id is needed, but is not used in this object.. we're overriding getRestUrl anyway
17947           this._id = "0";
17948           this.isLoaded();
17949 
17950           // We need to escape everything that is unallowed in xml so consumers don't have to deal with it (used in tandem with getLayoutxml())
17951           var layoutxml = newValues.layoutXML.replace(/&(?!amp;)/g, "&"),
17952               contentBody = {}, 
17953               //Created a regex (re) to scoop out just the gadget URL (without before and after whitespace characters)
17954               re = /<gadget>\s*(\S+)\s*<\/gadget>/g;
17955 
17956           //used the regex (re) to the update and save the layoutxml with the improved gadget URL (without before/after whitespace)
17957           layoutxml = layoutxml.replace(re, "<gadget>$1</gadget>");
17958           
17959           contentBody[this.getRestType()] = {
17960               "useDefault": newValues.useDefault,
17961               // The LayoutConfig restservice javascript class only translates ampersands, so we'll do that also
17962               "layoutxml": finesse.utilities.Utilities.translateHTMLEntities(layoutxml, true)
17963           };
17964     
17965           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
17966           handlers = handlers || {};
17967     
17968           this.restRequest(this.getRestUrl(), {
17969               method: 'PUT',
17970               success: this.createPutSuccessHandler(this, contentBody, handlers.success),
17971               error: handlers.error,
17972               content: contentBody
17973           });
17974     
17975           return this; // Allow cascading
17976       },
17977     
17978       getRestUrl: function(){
17979           // return team's url + /LayoutConfig
17980           // eg: /api/Team/1/LayoutConfig
17981           if(this._restObj === undefined){
17982               throw new Exception("TeamLayoutConfig instances must have a parent team object.");
17983           }
17984           return this._restObj.getRestUrl() + '/LayoutConfig';
17985       }
17986     
17987       });
17988         
17989     window.finesse = window.finesse || {};
17990     window.finesse.restservices = window.finesse.restservices || {};
17991     window.finesse.restservices.TeamLayoutConfig = TeamLayoutConfig;
17992       
17993     return TeamLayoutConfig;
17994 });
17995 
17996 /**
17997  * JavaScript representation of a TeamWorkflow.
17998  *
17999  * @requires finesse.clientservices.ClientServices
18000  * @requires Class
18001  * @requires finesse.FinesseBase
18002  * @requires finesse.restservices.RestBase
18003  */
18004 /** @private */
18005 define('restservices/TeamWorkflow',['restservices/RestBase'], function (RestBase) {
18006 
18007     var TeamWorkflow = RestBase.extend({
18008 
18009         /**
18010          * @class
18011          * JavaScript representation of a TeamWorkflow object. Also exposes
18012          * methods to operate on the object against the server.
18013          *
18014          * @param {Object} options
18015          *     An object with the following properties:<ul>
18016          *         <li><b>id:</b> The id of the object being constructed</li>
18017          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18018          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18019          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18020          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18021          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18022          *             <li><b>status:</b> {Number} The HTTP status description returned</li>
18023          *             <li><b>content:</b> {String} Raw string of response</li>
18024          *             <li><b>object:</b> {Object} Parsed object of response</li>
18025          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18026          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18027          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18028          *             </ul></li>
18029          *         </ul></li>
18030          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18031          * @constructs
18032          **/
18033         init: function (options) {
18034             this._super(options);
18035         },
18036 
18037         /**
18038          * @private
18039          * Gets the REST class for the current object - this is the TeamWorkflow class.
18040          * @returns {Object} The TeamWorkflow class.
18041          */
18042         getRestClass: function () {
18043             return TeamWorkflow;
18044         },
18045 
18046         /**
18047          * @private
18048          * Gets the REST type for the current object - this is a "Workflow".
18049          * @returns {String} The Workflow string.
18050          */
18051         getRestType: function () {
18052             return "Workflow";
18053         },
18054 
18055         /**
18056          * @private
18057          * Override default to indicate that this object doesn't support making
18058          * requests.
18059          */
18060         supportsRequests: false,
18061 
18062         /**
18063          * @private
18064          * Override default to indicate that this object doesn't support subscriptions.
18065          */
18066         supportsSubscriptions: false,
18067 
18068         /**
18069          * Getter for the name.
18070          * @returns {String} The name.
18071          */
18072         getName: function () {
18073             this.isLoaded();
18074             return this.getData().name;
18075         },
18076 
18077         /**
18078          * Getter for the description.
18079          * @returns {String} The description.
18080          */
18081         getDescription: function () {
18082             this.isLoaded();
18083             return this.getData().description;
18084         },
18085 
18086         /**
18087          * Getter for the Uri value.
18088          * @returns {String} The Uri.
18089          */
18090         getUri: function () {
18091             this.isLoaded();
18092             return this.getData().uri;
18093         }
18094 
18095     });
18096     
18097 	window.finesse = window.finesse || {};
18098     window.finesse.restservices = window.finesse.restservices || {};
18099     window.finesse.restservices.TeamWorkflow = TeamWorkflow;
18100 
18101     return TeamWorkflow;
18102 });
18103 
18104 /**
18105 * JavaScript representation of the TeamWorkflows collection
18106 * object which contains a list of TeamWorkflow objects.
18107  *
18108  * @requires finesse.clientservices.ClientServices
18109  * @requires Class
18110  * @requires finesse.FinesseBase
18111  * @requires finesse.restservices.RestBase
18112  * @requires finesse.restservices.Dialog
18113  * @requires finesse.restservices.RestCollectionBase
18114  */
18115 /** @private */
18116 define('restservices/TeamWorkflows',[
18117     'restservices/RestCollectionBase',
18118     'restservices/TeamWorkflow',
18119     'restservices/RestBase'
18120 ],
18121 function (RestCollectionBase, TeamWorkflow, RestBase) {
18122 
18123     var TeamWorkflows = RestCollectionBase.extend({
18124     
18125         /**
18126          * @class
18127          * JavaScript representation of a TeamWorkflows collection object. Also exposes
18128          * methods to operate on the object against the server.
18129          *
18130          * @param {Object} options
18131          *     An object with the following properties:<ul>
18132          *         <li><b>id:</b> The id of the object being constructed</li>
18133          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18134          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18135          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18136          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18137          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18138          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18139          *             <li><b>content:</b> {String} Raw string of response</li>
18140          *             <li><b>object:</b> {Object} Parsed object of response</li>
18141          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18142          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18143          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18144          *             </ul></li>
18145          *         </ul></li>
18146          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18147          * @constructs
18148          **/
18149         init: function (options) {
18150             this._super(options);
18151         },
18152 
18153         /**
18154          * @private
18155          * Gets the REST class for the current object - this is the TeamWorkflows class.
18156          */
18157         getRestClass: function () {
18158             return TeamWorkflows;
18159         },
18160 
18161         /**
18162          * @private
18163          * Gets the REST class for the objects that make up the collection. - this
18164          * is the TeamWorkflow class.
18165          */
18166         getRestItemClass: function () {
18167             return TeamWorkflow;
18168         },
18169 
18170         /**
18171          * @private
18172          * Gets the REST type for the current object - this is a "Workflows".
18173          */
18174         getRestType: function () {
18175             return "Workflows";
18176         },
18177 
18178         /**
18179          * Overrides the parent class.  Returns the url for the Workflows resource
18180          */
18181         getRestUrl: function () {
18182             var restObj = this._restObj, restUrl = "";
18183 
18184             //Prepend the base REST object if one was provided.
18185             //Otherwise prepend with the default webapp name.
18186             if (restObj instanceof RestBase) {
18187                 restUrl += restObj.getRestUrl();
18188             } else {
18189                 restUrl += "/finesse/api/Team";
18190             }
18191             //Append ID if it is not undefined, null, or empty.
18192             if (this._id) {
18193                 restUrl += "/" + this._id;
18194             }
18195             //Append the REST type.
18196             restUrl += "/Workflows";
18197             
18198             return restUrl;
18199         },
18200 
18201         /**
18202          * @private
18203          * Gets the REST type for the objects that make up the collection - this is "Workflow".
18204          */
18205         getRestItemType: function () {
18206             return "Workflow";
18207         },
18208 
18209         /**
18210          * @private
18211          * Override default to indicates that the collection supports making requests.
18212          */
18213         supportsRequests: true,
18214 
18215         /**
18216          * @private
18217          * Override default to indicates that the collection does not subscribe to its objects.
18218          */
18219         supportsRestItemSubscriptions: false,
18220 
18221         /**
18222          * Retrieve the Sign Out Reason Codes.
18223          *
18224          * @returns {finesse.restservices.TeamWorkflows}
18225          *     This TeamWorkflows object to allow cascading.
18226          */
18227         get: function () {
18228             // set loaded to false so it will rebuild the collection after the get
18229             this._loaded = false;
18230             // reset collection
18231             this._collection = {};
18232             // perform get
18233             this._synchronize();
18234             return this;
18235         },
18236 
18237         /* We only use PUT and GET on Reason Code team assignments
18238          * @param {Object} contact
18239          * @param {Object} contentBody
18240          * @param {Function} successHandler
18241          */
18242         createPutSuccessHandler: function (contact, contentBody, successHandler) {
18243             return function (rsp) {
18244                 // Update internal structure based on response. Here we
18245                 // inject the contentBody from the PUT request into the
18246                 // rsp.object element to mimic a GET as a way to take
18247                 // advantage of the existing _processResponse method.
18248                 rsp.object = contentBody;
18249                 contact._processResponse(rsp);
18250 
18251                 //Remove the injected contentBody object before cascading response
18252                 rsp.object = {};
18253 
18254                 //cascade response back to consumer's response handler
18255                 successHandler(rsp);
18256             };
18257         },
18258 
18259         /**
18260          * Update - This should be all that is needed.
18261          * @param {Object} newValues
18262          * @param {Object} handlers
18263          * @returns {finesse.restservices.TeamWorkflows}
18264          *     This TeamWorkflows object to allow cascading.
18265          */
18266         update: function (newValues, handlers) {
18267             this.isLoaded();
18268             var contentBody = {}, contentBodyInner = [], i, innerObject = {};
18269 
18270             contentBody[this.getRestType()] = {
18271             };
18272 
18273             for (i in newValues) {
18274                 if (newValues.hasOwnProperty(i)) {
18275                     innerObject = {
18276                         "uri": newValues[i]
18277                     };
18278                     contentBodyInner.push(innerObject);
18279                 }
18280             }
18281 
18282             contentBody[this.getRestType()] = {
18283                 "Workflow" : contentBodyInner
18284             };
18285 
18286             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
18287             handlers = handlers || {};
18288 
18289             this.restRequest(this.getRestUrl(), {
18290                 method: 'PUT',
18291                 success: this.createPutSuccessHandler(this, contentBody, handlers.success),
18292                 error: handlers.error,
18293                 content: contentBody
18294             });
18295 
18296             return this; // Allow cascading
18297         }
18298 
18299     });
18300     
18301 	window.finesse = window.finesse || {};
18302     window.finesse.restservices = window.finesse.restservices || {};
18303     window.finesse.restservices.TeamWorkflows = TeamWorkflows;
18304     
18305     return TeamWorkflows;
18306 });
18307 
18308 /**
18309  * JavaScript representation of the Finesse TeamMessage Message object.
18310  *
18311  * @requires finesse.clientservices.ClientServices
18312  * @requires Class
18313  * @requires finesse.FinesseBase
18314  * @requires finesse.restservices.RestBase
18315  */
18316 
18317 /*jslint browser: true, nomen: true, sloppy: true, forin: true */
18318 /*global define,finesse */
18319 
18320 /** @private */
18321 define('restservices/TeamMessage',[
18322         'restservices/RestBase'
18323     ],
18324     function (RestBase) {
18325 
18326         var TeamMessage = RestBase.extend({
18327 
18328             /**
18329              * @class
18330              * JavaScript representation of a TeamMessage message object. Also exposes
18331              * methods to operate on the object against the server.
18332              *
18333              * @param {Object} options
18334              *     An object with the following properties:<ul>
18335              *         <li><b>id:</b> The id of the object being constructed</li>
18336              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18337              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18338              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18339              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18340              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18341              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18342              *             <li><b>content:</b> {String} Raw string of response</li>
18343              *             <li><b>object:</b> {Object} Parsed object of response</li>
18344              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18345              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18346              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18347              *             </ul></li>
18348              *         </ul></li>
18349              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18350              * @constructs
18351              **/
18352             init: function (options) {
18353                 this._super(options);
18354             },
18355 
18356             /**
18357              * @private
18358              * Gets the REST class for the current object - this is the TeamMessage class.
18359              * @returns {Object} The TeamMessage class.
18360              */
18361             getRestClass: function () {
18362                 throw new Error("getRestClass(): Not implemented in subtype.");
18363             },
18364 
18365             /**
18366              * @private
18367              * Gets the REST type for the current object - this is a "TeamMessage".
18368              * @returns {String} The Workflow string.
18369              */
18370             getRestType: function () {
18371                 return "TeamMessage";
18372             },
18373 
18374 
18375             /**
18376              * @private
18377              * Override default to indicate that this object doesn't support making
18378              * requests.
18379              */
18380             supportsRequests: false,
18381 
18382             /**
18383              * @private
18384              * Override default to indicate that this object doesn't support subscriptions.
18385              */
18386             supportsSubscriptions: false,
18387 
18388             /**
18389              * Getter for the TeamMessageuri value.
18390              * @returns {String} uri.
18391              */
18392             getTeamMessageUri: function () {
18393                 this.isLoaded();
18394                 return this.getData().uri;
18395             },
18396 
18397             /**
18398              * Getter for the teamMessage id value.
18399              * @returns {String} id.
18400              */
18401             getId: function () {
18402                 this.isLoaded();
18403                 return this.getData().id;
18404             },
18405 
18406             /**
18407              * Getter for the teamMessage createdBy value.
18408              * @returns {String} createdBy.
18409              */
18410             getCreatedBy: function () {
18411                 this.isLoaded();
18412                 return this.getData().createdBy;
18413             },
18414             /**
18415              * Getter for the teamMessage createdAt value.
18416              * @returns {String} createdAt.
18417              */
18418             getCreatedAt: function () {
18419                 this.isLoaded();
18420                 return this.getData().createdAt;
18421             },
18422 
18423             /**
18424              * Getter for the teamMessage duration value.
18425              * @returns {String} duration.
18426              */
18427             getDuration: function () {
18428                 this.isLoaded();
18429                 return this.getData().duration;
18430             },
18431             /**
18432              * Getter for the teamMessage content value.
18433              * @returns {String} content.
18434              */
18435             getContent: function () {
18436                 this.isLoaded();
18437                 return this.getData().content;
18438 
18439             },
18440 
18441             add: function (newValues, handlers) {
18442                 // this.isLoaded();
18443                 var contentBody = {};
18444 
18445                 contentBody[this.getRestType()] = {
18446                     "duration": newValues.expires,
18447                     "content": newValues.message,
18448                     "teams": newValues.teams
18449 
18450                 };
18451 
18452                 // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
18453                 handlers = handlers || {};
18454 
18455                 this.restRequest(this.getRestUrl(), {
18456                     method: 'POST',
18457                     success: handlers.success,
18458                     error: handlers.error,
18459                     content: contentBody
18460                 });
18461 
18462                 return this; // Allow cascading
18463             }
18464 
18465         });
18466 
18467         window.finesse = window.finesse || {};
18468         window.finesse.restservices = window.finesse.restservices || {};
18469         window.finesse.restservices.TeamMessage = TeamMessage;
18470 
18471         return TeamMessage;
18472     });
18473 
18474 /**
18475  * JavaScript representation of the Finesse TeamMessages object for a Team.
18476  *
18477  * @requires Class
18478  * @requires finesse.FinesseBase
18479  * @requires finesse.restservices.RestBase
18480  * @requires finesse.utilities.Utilities
18481  * @requires finesse.restservices.TeamMessage
18482  */
18483 
18484 /** The following comment is to prevent jslint errors about 
18485  * using variables before they are defined.
18486  */
18487 /*global Exception */
18488 
18489 /** @private */
18490 define('restservices/TeamMessages',[
18491         'restservices/RestCollectionBase',
18492         'restservices/RestBase',
18493         'restservices/TeamMessage',
18494         'utilities/Utilities'
18495     ],
18496     function (RestCollectionBase, RestBase, TeamMessage, Utilities) {
18497 
18498         var TeamMessages = RestCollectionBase.extend({
18499 
18500             /**
18501              * @class
18502              * JavaScript representation of a TeamMessages object for a Team. Also exposes
18503              * methods to operate on the object against the server.
18504              *
18505              * @param {Object} options
18506              *     An object with the following properties:<ul>
18507              *         <li><b>id:</b> The id of the object being constructed</li>
18508              *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18509              *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18510              *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18511              *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18512              *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18513              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18514              *             <li><b>content:</b> {String} Raw string of response</li>
18515              *             <li><b>object:</b> {Object} Parsed object of response</li>
18516              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18517              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18518              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18519              *             </ul></li>
18520              *         </ul></li>
18521              *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18522              * @constructs
18523              **/
18524             init: function (options) {
18525                 this._super(options);
18526             },
18527 
18528             /**
18529              * Gets the REST class for the current object - this is the TeamMessages class.
18530              * @returns {Object} The TeamMessages class.
18531              */
18532             getRestClass: function () {
18533                 return TeamMessages;
18534             },
18535             /**
18536              * Gets the REST class for the rest item - this is the TeamMessages class.
18537              * @returns {Object} The TeamMessages class.
18538              */
18539             getRestItemClass: function () {
18540                 return TeamMessage;
18541             },
18542 
18543             /**
18544              * Gets the REST type for the current object - that is a "teamMessages" class.
18545              * @returns {String} The teamMessages string.
18546              */
18547             getRestType: function () {
18548                 return "teamMessages";
18549             },
18550 
18551             /**
18552              * Gets the REST type for the current object - this is a "TeamMessage" class.
18553              * @returns {String} The TeamMessage string.
18554              */
18555             getRestItemType: function () {
18556                 return "TeamMessage";
18557             },
18558 
18559 
18560             /**
18561              * @private
18562              * Override default to indicate that this object support making
18563              * requests.
18564              */
18565             supportsRequests: true,
18566 
18567             /**
18568              * @private
18569              * Override default to indicate that this object support subscriptions.
18570              */
18571             supportsSubscriptions: true,
18572             /**
18573              * @private
18574              * Override default to indicates that the collection subscribes to its objects.
18575              */
18576             supportsRestItemSubscriptions: true,
18577 
18578             /**
18579              * Getter for the teamMessages.
18580              * @returns {Object} teamMessages.
18581              */
18582             getBroadcastMessages: function () {
18583                 this.isLoaded();
18584                 return this.getData;
18585             },
18586 
18587 
18588             getRestUrl: function () {
18589                 // return team's url + /TeamMessages
18590                 // eg: /api/Team/1/TeamMessages
18591                 if (this._restObj === undefined) {
18592                     throw new Exception("Broadcast message instances must have a parent team object.");
18593                 }
18594                 return this._restObj.getRestUrl() + '/TeamMessages';
18595             },
18596 
18597             _buildCollection: function (data) {
18598                 var i, object, objectId, dataArray;
18599                 if (data && this.getProperty(data, this.getRestItemType()) !== null) {
18600                     dataArray = Utilities.getArray(this.getProperty(data, this.getRestItemType()));
18601                     for (i = 0; i < dataArray.length; i += 1) {
18602 
18603                         object = {};
18604                         object[this.getRestItemType()] = dataArray[i];
18605 
18606                         //get id from object.id instead of uri, since uri is not there for some reason
18607                         objectId = object[this.getRestItemType()].id; //this._extractId(object);
18608 
18609                         //create the Media Object
18610                         this._collection[objectId] = new(this.getRestItemClass())({
18611                             id: objectId,
18612                             data: object,
18613                             parentObj: this._restObj
18614                         });
18615                         this.length += 1;
18616                     }
18617                 }
18618             }
18619 
18620         });
18621 
18622         window.finesse = window.finesse || {};
18623         window.finesse.restservices = window.finesse.restservices || {};
18624         window.finesse.restservices.TeamMessages = TeamMessages;
18625 
18626         return TeamMessages;
18627     });
18628 
18629 /**
18630  * JavaScript representation of the Finesse Team REST object.
18631  *
18632  * @requires finesse.clientservices.ClientServices
18633  * @requires Class
18634  * @requires finesse.FinesseBase
18635  * @requires finesse.restservices.RestBase
18636  * @requires finesse.restservices.RestCollectionBase
18637  * @requires finesse.restservices.User
18638  * @requires finesse.restservices.Users
18639  */
18640 
18641 /**
18642  * The following comment prevents JSLint errors concerning undefined global variables.
18643  * It tells JSLint that these identifiers are defined elsewhere.
18644  */
18645 /*jslint bitwise:true, browser:true, nomen:true, regexp:true, sloppy:true, white:true */
18646 
18647 /** The following comment is to prevent jslint errors about 
18648  * using variables before they are defined.
18649  */
18650 /*global $, jQuery, Handlebars, dojox, dojo, finesse */
18651 
18652 /** @private */
18653 define('restservices/Team',[
18654     'restservices/RestBase',
18655     'utilities/Utilities',
18656     'restservices/Users',
18657     'restservices/TeamNotReadyReasonCodes',
18658     'restservices/TeamWrapUpReasons',
18659     'restservices/TeamSignOutReasonCodes',
18660     'restservices/TeamPhoneBooks',
18661     'restservices/TeamLayoutConfig',
18662     'restservices/TeamWorkflows',
18663     'restservices/TeamMessages'
18664 ],
18665 function (RestBase, Utilities, Users, TeamNotReadyReasonCodes, TeamWrapUpReasons, TeamSignOutReasonCodes, TeamPhoneBooks, TeamLayoutConfig, TeamWorkflows, TeamMessages) {
18666     var Team = RestBase.extend(/** @lends finesse.restservices.Team.prototype */{
18667         
18668         _teamLayoutConfig: null,
18669 
18670         /**
18671          *
18672          * @class
18673          * JavaScript representation of a Team object. Also exposes methods to operate
18674          * on the object against the server.
18675          *
18676          * @param {Object} options
18677          *     An object with the following properties:<ul>
18678          *         <li><b>id:</b> The id of the object being constructed</li>
18679          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18680          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18681          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18682          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18683          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18684          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18685          *             <li><b>content:</b> {String} Raw string of response</li>
18686          *             <li><b>object:</b> {Object} Parsed object of response</li>
18687          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18688          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18689          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18690          *             </ul></li>
18691          *         </ul></li>
18692          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18693          * @augments finesse.restservices.RestBase
18694          * @constructs
18695          * @example
18696          *      _team = new finesse.restservices.Team({
18697          *                      id: _id,
18698          *                      onLoad : _handleTeamLoad(team),
18699          *                      onChange : _handleTeamChange(team)
18700          *      });         
18701          **/
18702         init: function (options) {
18703             this._super(options);
18704         },
18705     
18706         /**
18707          * @private
18708          * Gets the REST class for the current object - this is the Team class.
18709          * @returns {Object} The Team constructor.
18710          */
18711         getRestClass: function () {
18712             return finesse.restesrvices.Team;
18713         },
18714     
18715         /**
18716          * @private
18717          * Gets the REST type for the current object - this is a "Team".
18718          * @returns {String} The Team string.
18719          */
18720         getRestType: function () {
18721             return "Team";
18722         },
18723     
18724         /**
18725          * @private
18726          * Override default to indicate that this object doesn't support making
18727          * requests.
18728          */
18729         supportsSubscriptions: false,
18730     
18731         /**
18732          * Getter for the team id.
18733          * @returns {String} The team id.
18734          */
18735         getId: function () {
18736             this.isLoaded();
18737             return this.getData().id;
18738         },
18739     
18740         /**
18741          * Getter for the team name.
18742          * @returns {String} The team name
18743          */
18744         getName: function () {
18745             this.isLoaded();
18746             return this.getData().name;
18747         },
18748     
18749         /**
18750          * @private
18751          * Getter for the team uri.
18752          * @returns {String} The team uri
18753          */
18754         getUri: function () {
18755             this.isLoaded();
18756             return this.getData().uri;        
18757         },
18758     
18759         /**
18760          * Constructs and returns a collection of Users.
18761          * @param {Object} options that sets callback handlers.
18762          * 		An object with the following properties:<ul>
18763          *         <li><b>onLoad(users): (optional)</b> when the object is successfully loaded from the server</li>
18764          *         <li><b>onError(): (optional)</b> if loading of the object fails, invoked with the error response object</li>
18765          * @returns {finesse.restservices.Users} Users collection of User objects.
18766          */
18767         getUsers: function (options) {
18768             this.isLoaded();
18769             options = options || {};
18770     
18771             options.parentObj = this;
18772             // We are using getData() instead of getData.Users because the superclass (RestCollectionBase)
18773             // for Users needs the "Users" key to validate the provided payload matches the class type.
18774             options.data = this.getData();
18775     
18776             return new Users(options);
18777         },
18778     
18779         /**
18780          * @private
18781          * Getter for a teamNotReadyReasonCodes collection object that is associated with Team.
18782          * @param callbacks
18783          * @returns {teamNotReadyReasonCodes}
18784          *     A teamNotReadyReasonCodes collection object.
18785          */
18786         getTeamNotReadyReasonCodes: function (callbacks) {
18787             var options = callbacks || {};
18788             options.parentObj = this;
18789             this.isLoaded();
18790     
18791             if (!this._teamNotReadyReasonCodes) {
18792                 this._teamNotReadyReasonCodes = new TeamNotReadyReasonCodes(options);
18793             }
18794     
18795             return this._teamNotReadyReasonCodes;
18796         },
18797     
18798         /**
18799          * @private
18800          * Getter for a teamWrapUpReasons collection object that is associated with Team.
18801          * @param callbacks
18802          * @returns {teamWrapUpReasons}
18803          *     A teamWrapUpReasons collection object.
18804          */
18805         getTeamWrapUpReasons: function (callbacks) {
18806             var options = callbacks || {};
18807             options.parentObj = this;
18808             this.isLoaded();
18809     
18810             if (!this._teamWrapUpReasons) {
18811                 this._teamWrapUpReasons = new TeamWrapUpReasons(options);
18812             }
18813     
18814             return this._teamWrapUpReasons;
18815         },
18816     
18817         /**
18818          * @private
18819          * Getter for a teamSignOutReasonCodes collection object that is associated with Team.
18820          * @param callbacks
18821          * @returns {teamSignOutReasonCodes}
18822          *     A teamSignOutReasonCodes collection object.
18823          */
18824     
18825         getTeamSignOutReasonCodes: function (callbacks) {
18826             var options = callbacks || {};
18827             options.parentObj = this;
18828             this.isLoaded();
18829     
18830             if (!this._teamSignOutReasonCodes) {
18831                 this._teamSignOutReasonCodes = new TeamSignOutReasonCodes(options);
18832             }
18833     
18834             return this._teamSignOutReasonCodes;
18835         },
18836     
18837         /**
18838          * @private
18839          * Getter for a teamPhoneBooks collection object that is associated with Team.
18840          * @param callbacks
18841          * @returns {teamPhoneBooks}
18842          *     A teamPhoneBooks collection object.
18843          */
18844         getTeamPhoneBooks: function (callbacks) {
18845             var options = callbacks || {};
18846             options.parentObj = this;
18847             this.isLoaded();
18848     
18849             if (!this._phonebooks) {
18850                 this._phonebooks = new TeamPhoneBooks(options);
18851             }
18852     
18853             return this._phonebooks;
18854         },
18855     
18856         /**
18857          * @private
18858          * Getter for a teamWorkflows collection object that is associated with Team.
18859          * @param callbacks
18860          * @returns {teamWorkflows}
18861          *     A teamWorkflows collection object.
18862          */
18863         getTeamWorkflows: function (callbacks) {
18864             var options = callbacks || {};
18865             options.parentObj = this;
18866             this.isLoaded();
18867     
18868             if (!this._workflows) {
18869                 this._workflows = new TeamWorkflows(options);
18870             }
18871     
18872             return this._workflows;
18873         },
18874     
18875         /**
18876          * @private
18877          * Getter for a teamLayoutConfig object that is associated with Team.
18878          * @param callbacks
18879          * @returns {teamLayoutConfig}
18880          */
18881         getTeamLayoutConfig: function (callbacks) {
18882             var options = callbacks || {};
18883             options.parentObj = this;
18884             this.isLoaded();
18885     
18886             if (this._teamLayoutConfig === null) {
18887                 this._teamLayoutConfig = new TeamLayoutConfig(options);
18888             }
18889     
18890             return this._teamLayoutConfig;
18891         },
18892         /**
18893          * @private
18894          * Getter for the teamMessages object that is associated with Team.
18895          * @param callbacks
18896          * @returns {teamMessages}
18897          */
18898         getTeamMessages: function (callbacks) {
18899             var options = callbacks || {};
18900             options.parentObj = this;
18901             this.isLoaded();
18902             if(!this._teamMessages) {
18903                 this._teamMessages = new TeamMessages(options);
18904             }
18905             return this._teamMessages;
18906         }
18907     
18908     });
18909     
18910     window.finesse = window.finesse || {};
18911     window.finesse.restservices = window.finesse.restservices || {};
18912     window.finesse.restservices.Team = Team;
18913     
18914     return Team;    
18915 });
18916 
18917 /**
18918  * JavaScript representation of the Finesse Teams collection.
18919  * object which contains a list of Team objects
18920  * @requires finesse.clientservices.ClientServices
18921  * @requires Class
18922  * @requires finesse.FinesseBase
18923  * @requires finesse.restservices.RestBase
18924  * @requires finesse.restservices.RestCollectionBase
18925  */
18926 
18927 /** @private */
18928 define('restservices/Teams',[
18929     'restservices/RestCollectionBase',
18930     'restservices/Team'
18931 ],
18932 function (RestCollectionBase, Team) {
18933     /** @private */
18934     var Teams = RestCollectionBase.extend({
18935 
18936         /**
18937          * @class
18938          * JavaScript representation of a Teams collection object. Also exposes methods to operate
18939          * on the object against the server.
18940          *
18941          * @param {Object} options
18942          *     An object with the following properties:<ul>
18943          *         <li><b>id:</b> The id of the object being constructed</li>
18944          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
18945          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
18946          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
18947          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
18948          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
18949          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
18950          *             <li><b>content:</b> {String} Raw string of response</li>
18951          *             <li><b>object:</b> {Object} Parsed object of response</li>
18952          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
18953          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
18954          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
18955          *             </ul></li>
18956          *         </ul></li>
18957          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
18958          * @constructs
18959          **/
18960         init: function (options) {
18961             this._super(options);
18962         },
18963 
18964         /**
18965          * @private
18966          * Gets the REST class for the current object - this is the Teams class.
18967          * @returns {Object} The Teams constructor.
18968          */
18969         getRestClass: function () {
18970             return Teams;
18971         },
18972 
18973         /**
18974          * @private
18975          * Gets the REST class for the objects that make up the collection. - this
18976          * is the Team class.
18977          */
18978         getRestItemClass: function () {
18979             return Team;
18980         },
18981 
18982         /**
18983          * @private
18984          * Gets the REST type for the current object - this is a "Teams".
18985          * @returns {String} The Teams string.
18986          */
18987         getRestType: function () {
18988             return "Teams";
18989         },
18990         
18991         /**
18992          * @private
18993          * Gets the REST type for the objects that make up the collection - this is "Team".
18994          */
18995         getRestItemType: function () {
18996             return "Team";
18997         },
18998 
18999         /**
19000          * @private
19001          * Override default to indicates that the collection supports making
19002          * requests.
19003          */
19004         supportsRequests: true,
19005 
19006         /**
19007          * @private
19008          * Override default to indicate that this object doesn't support subscriptions.
19009          */
19010         supportsRestItemSubscriptions: false,
19011         
19012         /**
19013          * @private
19014          * Retrieve the Teams.  This call will re-query the server and refresh the collection.
19015          *
19016          * @returns {finesse.restservices.Teams}
19017          *     This Teams object to allow cascading.
19018          */
19019         get: function () {
19020             // set loaded to false so it will rebuild the collection after the get
19021             this._loaded = false;
19022             // reset collection
19023             this._collection = {};
19024             // perform get
19025             this._synchronize();
19026             return this;
19027         }
19028 
19029     });
19030 
19031     window.finesse = window.finesse || {};
19032     window.finesse.restservices = window.finesse.restservices || {};
19033     window.finesse.restservices.Teams = Teams;
19034     
19035     return Teams;
19036 });
19037 
19038 /**
19039  * JavaScript representation of the Finesse SystemInfo object
19040  *
19041  * @requires finesse.clientservices.ClientServices
19042  * @requires Class
19043  * @requires finesse.FinesseBase
19044  * @requires finesse.restservices.RestBase
19045  */
19046 
19047 /** @private */
19048 define('restservices/SystemInfo',['restservices/RestBase'], function (RestBase) {
19049     
19050     var SystemInfo = RestBase.extend(/** @lends finesse.restservices.SystemInfo.prototype */{
19051         /**
19052          * @private
19053          * Returns whether this object supports subscriptions
19054          */
19055         supportsSubscriptions: false,
19056 
19057         doNotRefresh: true,
19058       
19059         /**
19060          * @class
19061          * JavaScript representation of a SystemInfo object.
19062          * 
19063          * @augments finesse.restservices.RestBase
19064          * @see finesse.restservices.SystemInfo.Statuses
19065          * @constructs
19066          */
19067         _fakeConstuctor: function () {
19068             /* This is here to hide the real init constructor from the public docs */
19069         },
19070         
19071          /**
19072          * @private
19073          * JavaScript representation of a SystemInfo object. Also exposes methods to operate
19074          * on the object against the server.
19075          *
19076          * @param {Object} options
19077          *     An object with the following properties:<ul>
19078          *         <li><b>id:</b> The id of the object being constructed</li>
19079          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
19080          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
19081          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
19082          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
19083          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
19084          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
19085          *             <li><b>content:</b> {String} Raw string of response</li>
19086          *             <li><b>object:</b> {Object} Parsed object of response</li>
19087          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
19088          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
19089          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
19090          *             </ul></li>
19091          *         </ul></li>
19092          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
19093          **/
19094         init: function (id, callbacks, restObj)
19095         {
19096             this._super(id, callbacks, restObj);
19097         },
19098 
19099         /**
19100          * @private
19101          * Gets the REST class for the current object - this is the SystemInfo object.
19102          * @returns {Object} The SystemInfo constructor.
19103          */
19104         getRestClass: function () {
19105             return SystemInfo;
19106         },
19107 
19108         /**
19109          * @private
19110          * Gets the REST type for the current object - this is a "SystemInfo".
19111          * @returns {String} The SystemInfo string.
19112          */
19113         getRestType: function ()
19114         {
19115             return "SystemInfo";
19116         },
19117         
19118         _validate: function (obj)
19119         {
19120             return true;
19121         },
19122         
19123         /**
19124          * Returns the status of the Finesse system.
19125          *   IN_SERVICE if the Finesse API reports that it is in service,
19126          *   OUT_OF_SERVICE otherwise.
19127          * @returns {finesse.restservices.SystemInfo.Statuses} System Status
19128          */
19129         getStatus: function () {
19130             this.isLoaded();
19131             return this.getData().status;
19132         },
19133         
19134         /**
19135          * Returns the lastCTIHeartbeatStatus
19136          *   success - last CTI heartbeat status was successful.
19137          *   failure - last CTI heartbeat status was unsuccessful.
19138          * @returns {finesse.restservices.SystemInfo.lastCTIHeartbeatStatus} Last Heartbeat  to CTI was successful or not.
19139          */
19140         getLastCTIHeartbeatStatus: function () {
19141             this.isLoaded();
19142             return this.getData().lastCTIHeartbeatStatus;
19143         },
19144         
19145         /**
19146          * Returns the reason due to which Finesse is OUT OF SERVICE.
19147          * It returns empty string when Finesse status is IN_SERVICE.
19148          * @returns {String} statusReason if finesse is OUT OF SERVICE , or empty string otherwise.
19149          */
19150         getStatusReason: function () {
19151             this.isLoaded();
19152             return this.getData().statusReason;
19153         },
19154         
19155         /**
19156          * Returns the current timestamp from this SystemInfo object.
19157          *   This is used to calculate time drift delta between server and client.
19158          *  @returns {String} Time (GMT): yyyy-MM-dd'T'HH:mm:ss'Z'
19159          */
19160         getCurrentTimestamp: function () {
19161             this.isLoaded();
19162             return this.getData().currentTimestamp;
19163         },
19164         
19165         /**
19166          * Getter for the xmpp domain of the system.
19167          * @returns {String} The xmpp domain corresponding to this SystemInfo object.
19168          */
19169         getXmppDomain: function () {
19170             this.isLoaded();
19171             return this.getData().xmppDomain;
19172         },
19173         
19174         /**
19175          * Getter for the xmpp pubsub domain of the system.
19176          * @returns {String} The xmpp pubsub domain corresponding to this SystemInfo object.
19177          */
19178         getXmppPubSubDomain: function () {
19179             this.isLoaded();
19180             return this.getData().xmppPubSubDomain;
19181         },
19182 
19183         /**
19184          * Getter for the deployment type (UCCE or UCCX).
19185          * @returns {String} "UCCE" or "UCCX"
19186          */ 
19187         getDeploymentType: function () {
19188             this.isLoaded();
19189             return this.getData().deploymentType;
19190         },
19191 
19192         /**
19193          * Returns whether this is a single node deployment or not by checking for the existence of the secondary node in SystemInfo.
19194          * @returns {Boolean} True for single node deployments, false otherwise.
19195          */ 
19196         isSingleNode: function () {
19197             var secondary = this.getData().secondaryNode;
19198             if (secondary && secondary.host) {
19199                 return false;
19200             }
19201             return true;
19202         },
19203 
19204         /**
19205          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the match.
19206          * This is useful for getting the FQDN of the current Finesse server.
19207          * @param {String} ...arguments[]... - any number of arguments to match against
19208          * @returns {String} FQDN (if properly configured) of the matched host of the primary or secondary node, or undefined if no match is found.
19209          */ 
19210         getThisHost: function () {
19211             var i,
19212             primary = this.getData().primaryNode,
19213             secondary = this.getData().secondaryNode;
19214 
19215             for (i = 0; (i < arguments.length); i = i + 1) {
19216                 if (primary && arguments[i] === primary.host) {
19217                     return primary.host;
19218                 } else if (secondary && arguments[i] === secondary.host) {
19219                     return secondary.host;
19220                 }
19221             }
19222         },
19223 
19224         /**
19225          * Checks all arguments against the primary and secondary hosts (FQDN) and returns the other node.
19226          * This is useful for getting the FQDN of the other Finesse server, i.e. for failover purposes.
19227          * @param {String} arguments - any number of arguments to match against
19228          * @returns {String} FQDN (if properly configured) of the alternate node, defaults to primary if no match is found, undefined for single node deployments.
19229          */ 
19230         getAlternateHost: function () {
19231             var i,
19232             isPrimary = false,
19233             primary = this.getData().primaryNode,
19234             secondary = this.getData().secondaryNode,
19235             xmppDomain = this.getData().xmppDomain,
19236             alternateHost;
19237 
19238             if (primary && primary.host) {
19239                     if (xmppDomain === primary.host) {
19240                         isPrimary = true;
19241                     }
19242                 if (secondary && secondary.host) {
19243                     if (isPrimary) {
19244                         return secondary.host;
19245                     }
19246                     return primary.host;
19247                 }
19248             }
19249         },
19250         
19251         /**
19252          * Gets the peripheral ID that Finesse is connected to. The peripheral
19253          * ID is the ID of the PG Routing Client (PIM).
19254          * 
19255          * @returns {String} The peripheral Id if UCCE, or empty string otherwise.
19256          */
19257         getPeripheralId : function () {
19258              this.isLoaded();
19259              
19260              var peripheralId = this.getData().peripheralId;
19261              if (peripheralId === null) {
19262                   return "";
19263              } else {
19264                   return this.getData().peripheralId;
19265              }
19266         },
19267 
19268          /**
19269          * Gets the license. Only apply to UCCX.
19270          * 
19271          * @returns {String} The license if UCCX, or empty string otherwise.
19272          */
19273         getlicense : function () {
19274              this.isLoaded();
19275              return this.getData().license || "";
19276         },
19277         
19278         /**
19279          * Gets the systemAuthMode for the current deployment
19280          * 
19281          * @returns {String} The System auth mode for current deployment
19282          */
19283         getSystemAuthMode : function() {
19284             this.isLoaded();
19285             return this.getData().systemAuthMode;
19286         }
19287     });
19288     
19289     SystemInfo.Statuses = /** @lends finesse.restservices.SystemInfo.Statuses.prototype */ { 
19290         /** 
19291          * Finesse is in service. 
19292          */
19293         IN_SERVICE: "IN_SERVICE",
19294         /** 
19295          * Finesse is not in service. 
19296          */
19297         OUT_OF_SERVICE: "OUT_OF_SERVICE",
19298         /**
19299          * @class SystemInfo status values.
19300          * @constructs
19301          */
19302         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
19303 
19304     };
19305     
19306     window.finesse = window.finesse || {};
19307     window.finesse.restservices = window.finesse.restservices || {};
19308     window.finesse.restservices.SystemInfo = SystemInfo;
19309     
19310     return SystemInfo;
19311 });
19312 
19313 define('restservices/DialogLogoutActions',[], function ()
19314 {
19315     var DialogLogoutActions = /** @lends finesse.restservices.DialogLogoutActions.prototype */ {
19316 
19317         /**
19318          * Set this action to close active dialogs when the agent logs out.
19319          */
19320         CLOSE: "CLOSE",
19321 
19322         /**
19323          * Set this action to transfer active dialogs when the agent logs out.
19324          */
19325         TRANSFER: "TRANSFER",
19326 
19327         /**
19328          * @class Actions used to handle tasks that are associated with a given media at logout time.
19329          *
19330          * @constructs
19331          */
19332         _fakeConstructor: function ()
19333         {
19334         }, // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
19335 
19336         /**
19337          * Is the given action a valid dialog logout action.
19338          *
19339          * @param {String} action the action to evaluate
19340          * @returns {Boolean} true if the action is valid; false otherwise
19341          */
19342         isValidAction: function(action)
19343         {
19344             if ( !action )
19345             {
19346                 return false;
19347             }
19348 
19349             return DialogLogoutActions.hasOwnProperty(action.toUpperCase());
19350         }
19351     };
19352 
19353     window.finesse = window.finesse || {};
19354     window.finesse.restservices = window.finesse.restservices || {};
19355     window.finesse.restservices.DialogLogoutActions = DialogLogoutActions;
19356 
19357     return DialogLogoutActions;
19358 });
19359 define('restservices/InterruptActions',[], function ()
19360 {
19361     var InterruptActions = /** @lends finesse.restservices.InterruptActions.prototype */
19362     {
19363         /**
19364          * The interrupt will be accepted and the agent will not work on dialogs in this media until the media is no longer interrupted.
19365          */
19366         ACCEPT: "ACCEPT",
19367 
19368         /**
19369          * the interrupt will be ignored and the agent is allowed to work on dialogs while the media is interrupted.
19370          */
19371         IGNORE: "IGNORE",
19372 
19373         /**
19374          * @class
19375          *
19376          * The action to be taken in the event this media is interrupted. The action will be one of the following:<ul>
19377          *     <li><b>ACCEPT:</b> the interrupt will be accepted and the agent will not work on dialogs in this media
19378          *     until the media is no longer interrupted.</li>
19379          *     <li><b>IGNORE:</b> the interrupt will be ignored and the agent is allowed to work on dialogs while the
19380          *     media is interrupted.</li></ul>
19381          *
19382          * @constructs
19383          */
19384         _fakeConstructor: function ()
19385         {
19386         }, // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
19387 
19388         /**
19389          * Is the given action a valid dialog logout action.
19390          *
19391          * @param {String} action the action to evaluate
19392          * @returns {Boolean} true if the action is valid; false otherwise
19393          */
19394         isValidAction: function(action)
19395         {
19396             if ( !action )
19397             {
19398                 return false;
19399             }
19400 
19401             return InterruptActions.hasOwnProperty(action.toUpperCase());
19402         }
19403     };
19404 
19405     window.finesse = window.finesse || {};
19406     window.finesse.restservices = window.finesse.restservices || {};
19407     window.finesse.restservices.InterruptActions = InterruptActions;
19408 
19409     return InterruptActions;
19410 });
19411 /**
19412  * Utility class for looking up a ReasonCode using the code and category.
19413  *
19414  */
19415 
19416 /** @private */
19417 define('restservices/ReasonCodeLookup',['restservices/RestBase', 'utilities/Utilities'], function (RestBase, Utilities) {
19418     
19419     var ReasonCodeLookup = RestBase.extend(/** @lends finesse.restservices.ReasonCodeLookup.prototype */{
19420         /**
19421          * @private
19422          * Returns whether this object supports subscriptions
19423          */
19424         supportsSubscriptions: false,
19425 
19426         doNotRefresh: true,
19427         
19428         autoSubscribe: false,
19429         
19430         supportsRequests: false,
19431       
19432         /**
19433          * @class
19434          * Utility class for looking up a ReasonCode using the code and category.
19435          * 
19436          * @constructs
19437          */
19438         _fakeConstuctor: function () {
19439             /* This is here to hide the real init constructor from the public docs */
19440         },
19441         
19442          /**
19443          * @private
19444          * JavaScript representation of a ReasonCodeLookup object. Also exposes methods to operate
19445          * on the object against the server.
19446          *
19447          * @param {Object} options
19448          *     An object with the following properties:<ul>
19449          *         <li><b>id:</b> The id of the object being constructed</li>
19450          *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
19451          *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
19452          *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
19453          *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
19454          *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
19455          *             <li><b>status:</b> {Number} The HTTP status code returned</li>
19456          *             <li><b>content:</b> {String} Raw string of response</li>
19457          *             <li><b>object:</b> {Object} Parsed object of response</li>
19458          *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
19459          *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
19460          *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
19461          *             </ul></li>
19462          *         </ul></li>
19463          *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
19464          **/
19465         init: function (options){
19466             this._super(options);
19467         },
19468         
19469         /**
19470          * @private
19471          * Do get disabled.
19472          */
19473         doGet: function(handlers) {
19474             return;
19475         },
19476 
19477         /**
19478          * @private
19479          * Gets the REST class for the current object - this is the ReasonCodeLookup object.
19480          */
19481         getRestClass: function () {
19482             return ReasonCodeLookup;
19483         },
19484 
19485         /**
19486          * @private
19487          * Gets the REST type for the current object - this is a "ReasonCodeLookup".
19488          */
19489         getRestType: function () {
19490             return "ReasonCode";
19491         },
19492         
19493         
19494         /**
19495          * @private
19496          * Parses a uriString to retrieve the id portion
19497          * @param {String} uriString
19498          * @return {String} id
19499          */
19500         _parseIdFromUriString : function (uriString) {
19501             return Utilities.getId(uriString);
19502         },
19503         
19504         /**
19505          * Performs a GET against the Finesse server, for looking up the reason code 
19506          * with its reason code value, and category.
19507          * Note that there is no return value; use the success handler to process a
19508          * valid return.
19509          * 
19510          * @param {finesse.interfaces.RequestHandlers} handlers
19511          *     An object containing the handlers for the request
19512          * @param {String} reasonCodeValue The code for the reason code to lookup
19513          * @param {String} reasonCodeCategory The category for the reason code to lookup.
19514          *                 The possible values are "NOT_READY" and "LOGOUT".
19515          *
19516          * @example
19517          *      new finesse.restservices.ReasonCodeLookup().lookupReasonCode({
19518          *                                              success: _handleReasonCodeGet,
19519          *                                              error: _handleReasonCodeGetError
19520          *                                              }, '32762', 'NOT_READY');
19521          *      _handleReasonCodeGet(_reasonCode) {
19522          *          var id = _reasonCode.id;
19523          *          var uri = _reasonCode.uri;
19524          *          var label = _reasonCode.label;
19525          *          ...
19526          *      }
19527          * 
19528          */
19529         lookupReasonCode : function (handlers, reasonCodeValue, reasonCodeCategory) {
19530             var self = this, contentBody, reasonCode, url;
19531             contentBody = {};
19532             
19533             url = this.getRestUrl();
19534             url = url + "?category=" + reasonCodeCategory + "&code=" + reasonCodeValue;
19535             this.restRequest(url, {
19536                 method: 'GET',
19537                 success: function (rsp) {
19538                     reasonCode = {
19539                         uri: rsp.object.ReasonCode.uri,
19540                         label: rsp.object.ReasonCode.label,
19541                         id: self._parseIdFromUriString(rsp.object.ReasonCode.uri)
19542                     };
19543                     handlers.success(reasonCode);
19544                 },
19545                 error: function (rsp) {
19546                     handlers.error(rsp);
19547                 },
19548                 content: contentBody
19549             });
19550         }
19551         
19552     });
19553     
19554         
19555     window.finesse = window.finesse || {};
19556     window.finesse.restservices = window.finesse.restservices || {};
19557     window.finesse.restservices.ReasonCodeLookup = ReasonCodeLookup;
19558     
19559     return ReasonCodeLookup;
19560 });
19561 
19562 /**
19563  * JavaScript representation of the Finesse ChatConfig object
19564  * @requires ClientServices
19565  * @requires finesse.FinesseBase
19566  * @requires finesse.restservices.RestBase
19567  */
19568 
19569 /** @private */
19570 define('restservices/ChatConfig',['restservices/RestBase'], function (RestBase) {
19571     
19572     var ChatConfig = RestBase.extend(/** @lends finesse.restservices.ChatConfig.prototype */{
19573 
19574 		/**
19575 		 * @class
19576 		 * JavaScript representation of a ChatConfig object. Also exposes methods to operate
19577 		 * on the object against the server.
19578 		 *
19579 		 * @constructor
19580 		 * @param {String} id
19581 		 *     Not required...
19582 		 * @param {Object} callbacks
19583 		 *     An object containing callbacks for instantiation and runtime
19584 		 * @param {Function} callbacks.onLoad(this)
19585 		 *     Callback to invoke upon successful instantiation, passes in User object
19586 		 * @param {Function} callbacks.onLoadError(rsp)
19587 		 *     Callback to invoke on instantiation REST request error
19588 		 *     as passed by finesse.clientservices.ClientServices.ajax()
19589 		 *     {
19590 		 *         status: {Number} The HTTP status code returned
19591 		 *         content: {String} Raw string of response
19592 		 *         object: {Object} Parsed object of response
19593 		 *         error: {Object} Wrapped exception that was caught
19594 		 *         error.errorType: {String} Type of error that was caught
19595 		 *         error.errorMessage: {String} Message associated with error
19596 		 *     }
19597 		 * @param {Function} callbacks.onChange(this)
19598 		 *     Callback to invoke upon successful update, passes in User object
19599 		 * @param {Function} callbacks.onError(rsp)
19600 		 *     Callback to invoke on update error (refresh or event)
19601 		 *     as passed by finesse.clientservices.ClientServices.ajax()
19602 		 *     {
19603 		 *         status: {Number} The HTTP status code returned
19604 		 *         content: {String} Raw string of response
19605 		 *         object: {Object} Parsed object of response
19606 		 *         error: {Object} Wrapped exception that was caught
19607 		 *         error.errorType: {String} Type of error that was caught
19608 		 *         error.errorMessage: {String} Message associated with error
19609 		 *     }
19610 		 * @constructs     
19611 		 */
19612 		init: function (callbacks) {
19613 		    this._super("", callbacks);
19614 		},
19615         
19616         /**
19617          * Gets the REST class for the current object - this is the ChatConfig object.
19618          */
19619         getRestClass: function () {
19620             return ChatConfig;
19621         },
19622             
19623         /**
19624          * Gets the REST type for the current object - this is a "ChatConfig".
19625          */
19626         getRestType: function () {
19627             return "ChatConfig";
19628         },
19629         
19630         /**
19631          * Overrides the parent class.  Returns the url for the ChatConfig resource
19632          */
19633         getRestUrl: function () {
19634             return ("/finesse/api/" + this.getRestType());
19635         },
19636         
19637         /**
19638          * Returns whether this object supports subscriptions
19639          */
19640         supportsSubscriptions: false,
19641         
19642         /**
19643          * Getter for the Chat primary node of the ChatConfig
19644          * @returns {String}
19645          */
19646         getPrimaryNode: function () {
19647             this.isLoaded();
19648             return this.getData().primaryNode;
19649         },
19650 
19651         /**
19652          * Getter for the Chat secondary node (if any) of the ChatConfig
19653          * @returns {String}
19654          */
19655         getSecondaryNode: function () {
19656             this.isLoaded();
19657             return this.getData().secondaryNode;
19658         },
19659 
19660         /**
19661          * Retrieve the chat config settings
19662          * @returns {finesse.restservices.ChatConfig}
19663          *     This ChatConfig object to allow cascading
19664          */
19665         get: function () {      
19666             this._synchronize();
19667             
19668             return this; // Allow cascading
19669         },
19670         
19671         /**
19672          * Closure handle updating of the internal data for the ChatConfig object
19673          * upon a successful update (PUT) request before calling the intended
19674          * success handler provided by the consumer
19675          * 
19676          * @param {Object}
19677          *            chatconfig Reference to this ChatConfig object
19678          * @param {Object}
19679          *            chatSettings Object that contains the chat server settings to be
19680          *            submitted in the api request
19681          * @param {Function}
19682          *            successHandler The success handler specified by the consumer
19683          *            of this object
19684          * @returns {finesse.restservices.ChatConfig} This ChatConfig object to allow cascading
19685          */
19686         createPutSuccessHandler: function (chatconfig, chatSettings, successHandler) {
19687             return function (rsp) {
19688                 // Update internal structure based on response. Here we
19689                 // inject the chatSettings object into the
19690                 // rsp.object.ChatConfig element as a way to take
19691                 // advantage of the existing _processResponse method.
19692                 rsp.object.ChatConfig = chatSettings;
19693                 chatconfig._processResponse(rsp);
19694 
19695                 //Remove the injected chatSettings object before cascading response
19696                 rsp.object.ChatConfig = {};
19697                 
19698                 //cascade response back to consumer's response handler
19699                 successHandler(rsp);
19700             };
19701         },
19702         
19703         /**
19704          * Update the chat config settings
19705          * @param {Object} chatSettings
19706          *     The Chat server settings you want to configure
19707          * @param {Object} handlers
19708          *     An object containing callback handlers for the request. Optional.
19709          * @param {Function} options.success(rsp)
19710          *     A callback function to be invoked for a successful request.
19711          *     {
19712          *         status: {Number} The HTTP status code returned
19713          *         content: {String} Raw string of response
19714          *         object: {Object} Parsed object of response
19715          *     }
19716          * @param {Function} options.error(rsp)
19717          *     A callback function to be invoked for an unsuccessful request.
19718          *     {
19719          *         status: {Number} The HTTP status code returned
19720          *         content: {String} Raw string of response
19721          *         object: {Object} Parsed object of response (HTTP errors)
19722          *         error: {Object} Wrapped exception that was caught
19723          *         error.errorType: {String} Type of error that was caught
19724          *         error.errorMessage: {String} Message associated with error
19725          *     }
19726          * @returns {finesse.restservices.ChatConfig}
19727          *     This ChatConfig object to allow cascading
19728          */
19729         update: function (chatSettings, handlers) {
19730             this.isLoaded();
19731             var contentBody = {};
19732             
19733             contentBody[this.getRestType()] = {
19734                 "primaryNode": chatSettings.primaryNode,
19735                 "secondaryNode": chatSettings.secondaryNode
19736             };
19737             
19738             // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
19739             handlers = handlers || {};
19740             
19741             this.restRequest(this.getRestUrl(), {
19742                 method: 'PUT',
19743                 success: this.createPutSuccessHandler(this, chatSettings, handlers.success),
19744                 error: handlers.error,
19745                 content: contentBody
19746             });
19747             
19748             return this; // Allow cascading
19749         }   
19750     });
19751     
19752     window.finesse = window.finesse || {};
19753     window.finesse.restservices = window.finesse.restservices || {};
19754     window.finesse.restservices.ChatConfig = ChatConfig;
19755     
19756     return ChatConfig;
19757 });
19758 
19759 /**
19760  * Provides standard way resolve message keys with substitution
19761  *
19762  * @requires finesse.container.I18n or gadgets.Prefs
19763  */
19764 
19765 // Add Utilities to the finesse.utilities namespace
19766 define('utilities/I18n',['utilities/Utilities'], function (Utilities) {
19767     var I18n = (function () {
19768 
19769         /**
19770          * Shortcut to finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
19771          * @private
19772          */
19773         var _getMsg;
19774 
19775         return {
19776             /**
19777              * Provides a message resolver for this utility singleton.
19778              * @param {Function} getMsg
19779              *     A function that returns a string given a message key.
19780              *     If the key is not found, this function must return 
19781              *     something that tests false (i.e. undefined or "").
19782              */
19783             setGetter : function (getMsg) {
19784                 _getMsg = getMsg;
19785             },
19786 
19787             /**
19788              * Resolves the given message key, also performing substitution.
19789              * This generic utility will use a custom function to resolve the key
19790              * provided by finesse.utilities.I18n.setGetter. Otherwise, it will 
19791              * discover either finesse.container.I18n.getMsg or gadgets.Prefs.getMsg
19792              * upon the first invocation and store that reference for efficiency.
19793              * 
19794              * Since this will construct a new gadgets.Prefs object, it is recommended
19795              * for gadgets to explicitly provide the setter to prevent duplicate
19796              * gadgets.Prefs objects. This does not apply if your gadget does not need
19797              * access to gadgets.Prefs other than getMsg. 
19798              * 
19799              * @param {String} key
19800              *     The key to lookup
19801              * @param {String} arguments
19802              *     Arguments for substitution
19803              * @returns {String/Function}
19804              *     The resolved string if successful, otherwise a function that returns
19805              *     a '???' string that can also be casted into a string.
19806              */
19807             getString : function (key) {
19808                 var prefs, i, retStr, noMsg, getFailed = "", args;
19809                 if (!_getMsg) {
19810                     if (finesse.container && finesse.container.I18n) {
19811                         _getMsg = finesse.container.I18n.getMsg;
19812                     } else if (gadgets) {
19813                         prefs = new gadgets.Prefs();
19814                         _getMsg = prefs.getMsg;
19815                     }
19816                 }
19817                 
19818                 try {
19819                     retStr = _getMsg(key);
19820                 } catch (e) {
19821                     getFailed = "finesse.utilities.I18n.getString(): invalid _getMsg";
19822                 }
19823 
19824                 if (retStr) {
19825                     // Lookup was successful, perform substitution (if any)
19826                     args = [ retStr ];
19827                     for (i = 1; i < arguments.length; i += 1) {
19828                         args.push(arguments[i]);
19829                     }
19830                     return Utilities.formatString.apply(this, args);
19831                 }
19832                 // We want a function because jQuery.html() and jQuery.text() is smart enough to invoke it.
19833                 /** @private */
19834                 noMsg = function () {
19835                     return "???" + key + "???" + getFailed;
19836                 };
19837                 // We overload the toString() of this "function" to allow JavaScript to cast it into a string
19838                 // For example, var myMsg = "something " + finesse.utilities.I18n.getMsg("unresolvable.key");
19839                 /** @private */
19840                 noMsg.toString = function () {
19841                     return "???" + key + "???" + getFailed;
19842                 };
19843                 return noMsg;
19844 
19845             }
19846         };
19847     }());
19848     
19849     window.finesse = window.finesse || {};
19850     window.finesse.utilities = window.finesse.utilities || {};
19851     window.finesse.utilities.I18n = I18n;
19852 
19853     return I18n;
19854 });
19855 
19856 /**
19857  * Logging.js: provides simple logging for clients to use and overrides synchronous native methods: alert(), confirm(), and prompt().
19858  * 
19859  * On Firefox, it will hook into console for logging.  On IE, it will log to the status bar. 
19860  */
19861 // Add Utilities to the finesse.utilities namespace
19862 define('utilities/Logger',[], function () {
19863     var Logger = (function () {
19864         
19865         var
19866         
19867         /** @private **/
19868         debugOn,
19869         
19870         /**
19871          * Pads a single digit number for display purposes (e.g. '4' shows as '04')
19872          * @param num is the number to pad to 2 digits
19873          * @returns a two digit padded string
19874          * @private
19875          */
19876         padTwoDigits = function (num) {        
19877             return (num < 10) ? '0' + num : num;  
19878         },
19879         
19880         /**
19881          * Checks to see if we have a console - this allows us to support Firefox or IE.
19882          * @returns {Boolean} True for Firefox, False for IE
19883          * @private
19884          */
19885         hasConsole = function () {
19886             var retval = false;
19887             try
19888             {
19889                 if (window.console !== undefined) 
19890                 {
19891                     retval = true;
19892                 }
19893             } 
19894             catch (err)
19895             {
19896                 retval = false;
19897             }
19898               
19899             return retval;
19900         },
19901         
19902         /**
19903          * Gets a timestamp.
19904          * @returns {String} is a timestamp in the following format: HH:MM:SS
19905          * @private
19906          */
19907         getTimeStamp = function () {
19908             var date = new Date(), timeStr;
19909             timeStr = padTwoDigits(date.getHours()) + ":" + padTwoDigits(date.getMinutes()) + ":" + padTwoDigits(date.getSeconds());
19910 
19911             return timeStr;
19912         };
19913         
19914         return {
19915             /**
19916              * Enable debug mode. Debug mode may impact performance on the UI.
19917              *
19918              * @param {Boolean} enable
19919              *      True to enable debug logging.
19920              * @private
19921              */
19922             setDebug : function (enable) {
19923                 debugOn = enable;
19924             },
19925             
19926             /**
19927              * Logs a string as DEBUG.
19928              * 
19929              * @param str is the string to log. 
19930              * @private
19931              */
19932             log : function (str) {
19933                 var timeStr = getTimeStamp();
19934                 
19935                 if (debugOn) {
19936                     if (hasConsole())
19937                     {
19938                         window.console.log(timeStr + ": " + "DEBUG" + " - " + str);
19939                     }
19940                 }
19941             },
19942             
19943             /**
19944              * Logs a string as INFO.
19945              * 
19946              * @param str is the string to log. 
19947              * @private
19948              */
19949             info : function (str) {
19950                 var timeStr = getTimeStamp();
19951                 
19952                 if (hasConsole())
19953                 {
19954                     window.console.info(timeStr + ": " + "INFO" + " - " + str);
19955                 }
19956             },
19957             
19958             /**
19959              * Logs a string as WARN.
19960              * 
19961              * @param str is the string to log. 
19962              * @private
19963              */
19964             warn : function (str) {
19965                 var timeStr = getTimeStamp();
19966                 
19967                 if (hasConsole())
19968                 {
19969                     window.console.warn(timeStr + ": " + "WARN" + " - " + str);
19970                 }
19971             },
19972             /**
19973              * Logs a string as ERROR.
19974              * 
19975              * @param str is the string to log. 
19976              * @private
19977              */
19978             error : function (str) {
19979                 var timeStr = getTimeStamp();
19980                 
19981                 if (hasConsole())
19982                 {
19983                     window.console.error(timeStr + ": " + "ERROR" + " - " + str);
19984                 }
19985             }
19986         };
19987     }());
19988     
19989     return Logger;
19990 });
19991 
19992 /**
19993  * BackSpaceHandler.js: provides functionality to prevent the page from navigating back and hence losing the unsaved data when backspace is pressed.
19994  * 
19995  */
19996 define('utilities/BackSpaceHandler',[], function () {
19997 			var eventCallback = function(event) {
19998 				var doPrevent = false, d = event.srcElement || event.target;
19999 				if (event.keyCode === 8) {
20000 					if ((d.tagName.toUpperCase() === 'INPUT' && (d.type
20001 							.toUpperCase() === 'TEXT'
20002 							|| d.type.toUpperCase() === 'PASSWORD'
20003 							|| d.type.toUpperCase() === 'FILE'
20004 							|| d.type.toUpperCase() === 'SEARCH'
20005 							|| d.type.toUpperCase() === 'EMAIL'
20006 							|| d.type.toUpperCase() === 'NUMBER' || d.type
20007 							.toUpperCase() === 'DATE'))
20008 							|| d.tagName.toUpperCase() === 'TEXTAREA') {
20009 						doPrevent = d.readOnly || d.disabled;
20010 					} else {
20011 						//if HTML content is editable doPrevent will be false and vice versa
20012 						doPrevent = (!d.isContentEditable);
20013 					}
20014 				}
20015 
20016 				if (doPrevent) {
20017 					event.preventDefault();
20018 				}
20019 			};
20020 
20021 			if (window.addEventListener) {
20022 				window.addEventListener('keydown', eventCallback);
20023 			} else if (window.attachEvent) {
20024 				window.attachEvent('onkeydown', eventCallback);
20025 			} else {
20026 				window.console.error("Unable to attach backspace handler event ");
20027 			}
20028 });
20029 !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});
20030 define('utilities/JsonValidator',[
20031 	"../../thirdparty/tv4/tv4.min.js"
20032 ],
20033 function (tv4) {
20034 
20035 	var jsonValdiator = /** @lends finesse.utilities.JsonValidator */ {
20036 		
20037         /**
20038          * @class
20039          * For JSON validation
20040          * 
20041          * @constructs
20042          */
20043         _fakeConstuctor: function () {
20044             /* This is here for jsdocs. */
20045 		},
20046 
20047 		/**
20048 		 * Validates JSON data by applying a specific schema
20049 		 * 
20050 		 * @param jsonData - JSON data
20051 		 * @param schema - JSON schema that would validate the parameter jsonData. 
20052 		 * It needs to follow the <a href="http://json-schema.org">JSON schema definition standard</a>
20053 		 * @returns - JSON Result that is of the below format
20054 		 * <pre>
20055 		 *  {
20056 		 *    "valid": [true/false], 
20057 		 *    "error": [tv4 error object if schema is not valid]
20058 		 *  }
20059 		 * </pre>
20060 		 * The error object will look something like this:
20061 		 * <pre>
20062 		 * {
20063 		 *    "code": 0,
20064 		 *    "message": "Invalid type: string",
20065 		 *    "dataPath": "/intKey",
20066 		 *    "schemaPath": "/properties/intKey/type"
20067 		 * }
20068 		 * </pre>
20069 		 */
20070 		validateJson: function (jsonData, schema) {
20071 			//window.console.info("To validate schema");
20072 			var valid = tv4.validate(jsonData, schema);
20073 			var result = {};
20074 			result.valid = valid;
20075 			if (!valid) {
20076 				result.error = tv4.error;
20077 			}
20078 			return result;
20079         }
20080 	}
20081 
20082 	
20083 
20084 	window.finesse = window.finesse || {};
20085     window.finesse.utilities = window.finesse.utilities || {};
20086     window.finesse.utilities.JsonValidator = jsonValdiator;
20087     
20088     return jsonValdiator;
20089 });
20090 /* using variables before they are defined.
20091  */
20092 /*global navigator,unescape,sessionStorage,localStorage,_initSessionList,_initSessionListComplete */
20093 
20094 /**
20095  * Allows each gadget to communicate with the server to send logs.
20096  */
20097 
20098 /**
20099  * @class
20100  * @private
20101  * Allows each product to initialize its method of storage
20102  */
20103 define('cslogger/FinesseLogger',["clientservices/ClientServices", "utilities/Utilities"], function (ClientServices, Utilities) {
20104     
20105     var FinesseLogger = (function () { 
20106 
20107         var
20108 
20109         /**
20110          * Array use to collect ongoing logs in memory
20111          * @private
20112          */
20113         _logArray = [],
20114 
20115         /**
20116          * The final data string sent to the server, =_logArray.join
20117          * @private
20118          */
20119         _logStr = "",
20120 
20121         /**
20122          * Keep track of size of log
20123          * @private
20124          */
20125         _logSize = 0,
20126 
20127         /**
20128          * Flag to keep track show/hide of send log link
20129          * @private
20130          */
20131         _sendLogShown = false,
20132 
20133         /**
20134          * Flag to keep track if local log initialized
20135          * @private
20136          */
20137         _loggingInitialized = false,
20138         
20139 
20140         /**
20141          * local log size limit
20142          * @private
20143          */
20144         _maxLocalStorageSize = 5000000,
20145 
20146         /**
20147          * half local log size limit
20148          * @private
20149          */
20150         _halfMaxLocalStorageSize = 0.5*_maxLocalStorageSize,
20151 
20152         
20153         /**
20154          * threshold for purge 
20155          * @private
20156          */
20157         _purgeStartPercent = 0.75,
20158         
20159         /**
20160          * log item prefix 
20161          * @private
20162          */
20163         _linePrefix = null,
20164         
20165         /**
20166          * locallog session 
20167          * @private
20168          */
20169         _session = null,
20170         
20171         /**
20172          * Flag to keep track show/hide of send log link
20173          * @private
20174          */
20175         _sessionKey = null,
20176         /**
20177          * Log session metadata 
20178          * @private
20179          */
20180         _logInfo = {},
20181         
20182         /**
20183          * Flag to find sessions 
20184          * @private
20185          */
20186         _findSessionsObj = null,
20187 
20188         /**
20189          * Wrap up console.log esp. for IE9 
20190          * @private
20191          */
20192         _myConsoleLog = function (str) {
20193             if (window.console !== undefined) {
20194               window.console.log(str);
20195             }
20196         },
20197         /**
20198          * Initialize the Local Logging
20199          * @private
20200          */
20201         _initLogging = function () {
20202             if (_loggingInitialized) {
20203                 return;
20204             }
20205             //Build a new store
20206             _session = sessionStorage.getItem("finSessKey");
20207             //if the _session is null or empty, skip the init
20208             if (!_session) {
20209               return;
20210             }
20211             _sessionKey = "Fi"+_session;
20212             _linePrefix = _sessionKey + "_";
20213             _logInfo = {};
20214             _logInfo.name = _session;
20215             _logInfo.size = 0;
20216             _logInfo.head = 0;
20217             _logInfo.tail = 0;
20218             _logInfo.startTime = new Date().getTime();
20219             _loggingInitialized = true;
20220             _initSessionList();
20221         },
20222         
20223         /**
20224          * get total data size 
20225          *
20226          * @return {Integer} which is the amount of data stored in local storage.
20227          * @private
20228          */
20229         _getTotalData = function ()
20230         {
20231             var sessName, sessLogInfoStr,sessLogInfoObj, sessionsInfoObj, totalData = 0,
20232             sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
20233             if (!sessionsInfoStr) {
20234                  return 0;
20235             }
20236             sessionsInfoObj = JSON.parse(sessionsInfoStr);
20237 
20238             for (sessName in sessionsInfoObj.sessions)
20239             {
20240                 if (sessionsInfoObj.sessions.hasOwnProperty(sessName)) {
20241                     sessLogInfoStr = localStorage.getItem("Fi" + sessName);
20242                     if (!sessLogInfoStr) {
20243                         _myConsoleLog("_getTotalData failed to get log info for "+sessName);
20244                     }
20245                     else {
20246                        sessLogInfoObj = JSON.parse(sessLogInfoStr);
20247                        totalData = totalData + sessLogInfoObj.size;
20248                     }
20249                 }
20250             }
20251 
20252               return totalData;
20253         },
20254         
20255         /**
20256          * Remove lines from tail up until store size decreases to half of max size limit.
20257          *
20258          * @private
20259          */
20260         _purgeCurrentSession = function() {
20261             var curStoreSize, purgedSize=0, line, tailKey, secLogInfoStr, logInfoStr, theLogInfo;
20262             curStoreSize = _getTotalData();
20263             if (curStoreSize < _halfMaxLocalStorageSize) {
20264                return;
20265             }
20266             logInfoStr = localStorage.getItem(_sessionKey);
20267             if (!logInfoStr) {
20268                return;
20269             }
20270             theLogInfo = JSON.parse(logInfoStr);
20271             //_myConsoleLog("Starting _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
20272             while(curStoreSize > _halfMaxLocalStorageSize) {
20273                try {
20274                    tailKey = _sessionKey+"_"+theLogInfo.tail;
20275                    line = localStorage.getItem(tailKey);
20276                    if (line) {
20277                        purgedSize = purgedSize +line.length;
20278                        localStorage.removeItem(tailKey);
20279                        curStoreSize = curStoreSize - line.length;
20280                        theLogInfo.size = theLogInfo.size - line.length;
20281                    }
20282                }
20283                catch (err) {
20284                    _myConsoleLog("purgeCurrentSession encountered err="+err);
20285                }
20286                if (theLogInfo.tail < theLogInfo.head) {
20287                    theLogInfo.tail = theLogInfo.tail  + 1;
20288                }
20289                else {
20290                    break;
20291                }
20292             }
20293             //purge stops here, we need to update session's meta data in storage
20294             secLogInfoStr = localStorage.getItem(_sessionKey);
20295             if (!secLogInfoStr) {
20296                 //somebody cleared the localStorage
20297                 return;
20298             }
20299             
20300             //_myConsoleLog("In _purgeCurrentSession() - after purging current session, currentStoreSize=" + curStoreSize);
20301             //_myConsoleLog("In _purgeCurrentSession() - after purging purgedSize=" + purgedSize);
20302             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.size=" + theLogInfo.size);
20303             //_myConsoleLog("In _purgeCurrentSession() - after purging logInfo.tail=" + theLogInfo.tail);
20304             localStorage.setItem(_sessionKey, JSON.stringify(theLogInfo));
20305             _myConsoleLog("Done _purgeCurrentSession() - currentStoreSize=" + curStoreSize);
20306         },
20307        
20308         /**
20309          * Purge a session 
20310          *
20311          * @param sessionName is the name of the session
20312          * @return {Integer} which is the current amount of data purged
20313          * @private
20314          */
20315         _purgeSession = function (sessionName) {
20316               var theLogInfo, logInfoStr, sessionsInfoStr, sessionsInfoObj;
20317               //Get the session logInfo
20318               logInfoStr = localStorage.getItem("Fi" + sessionName);
20319               if (!logInfoStr) {
20320                  _myConsoleLog("_purgeSession failed to get logInfo for "+sessionName);
20321                  return 0;
20322               }
20323               theLogInfo = JSON.parse(logInfoStr);
20324               
20325               //Note: This assumes that we don't crash in the middle of purging
20326               //=> if we do then it should get deleted next time
20327               //Purge tail->head
20328               while (theLogInfo.tail <= theLogInfo.head)
20329               {
20330                   try {
20331                       localStorage.removeItem("Fi" + sessionName + "_" + theLogInfo.tail);
20332                       theLogInfo.tail = theLogInfo.tail + 1;
20333                   }
20334                   catch (err) {
20335                       _myConsoleLog("In _purgeSession err="+err);
20336                       break;
20337                   }
20338               }
20339 
20340               //Remove the entire session
20341               localStorage.removeItem("Fi" + sessionName);
20342 
20343               //Update FinesseSessionsInfo
20344               sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
20345               if (!sessionsInfoStr) {
20346                  _myConsoleLog("_purgeSession could not get sessions Info, it was cleared?");
20347                  return 0;
20348               }
20349               sessionsInfoObj = JSON.parse(sessionsInfoStr);
20350               if (sessionsInfoObj.sessions !== null)
20351               {
20352                  delete sessionsInfoObj.sessions[sessionName];
20353               
20354                  sessionsInfoObj.total = sessionsInfoObj.total - 1;
20355                  sessionsInfoObj.lastWrittenBy = _session;
20356                  localStorage.setItem("FinesseSessionsInfo", JSON.stringify(sessionsInfoObj));
20357               }
20358               
20359               return theLogInfo.size;
20360         },
20361         
20362          /**
20363           * purge old sessions
20364           * 
20365           * @param storeSize
20366 	  * @return {Boolean} whether purging reaches its target
20367           * @private
20368          */
20369          _purgeOldSessions = function (storeSize) {
20370              var sessionsInfoStr, purgedSize = 0, sessName, sessions, curStoreSize, activeSession, sessionsInfoObj;
20371              sessionsInfoStr = localStorage.getItem("FinesseSessionsInfo");
20372              if (!sessionsInfoStr) {
20373                 _myConsoleLog("Could not get FinesseSessionsInfo");
20374                 return true;
20375              }
20376              sessionsInfoObj = JSON.parse(sessionsInfoStr);
20377              curStoreSize = _getTotalData();
20378              
20379              activeSession = _session;
20380              sessions = sessionsInfoObj.sessions;
20381              for (sessName in sessions) {
20382                 if (sessions.hasOwnProperty(sessName)) {
20383                     if (sessName !== activeSession) {
20384                         purgedSize = purgedSize + _purgeSession(sessName);
20385                         if ((curStoreSize-purgedSize) < _halfMaxLocalStorageSize) {
20386                             return true;
20387                         }
20388                     }
20389                 }
20390              }
20391             //purge is not done, so return false
20392             return false;
20393          },
20394          
20395        /**
20396         * handle insert error
20397         *
20398         * @param error
20399         * @private
20400         */
20401         _insertLineHandleError = function (error) {
20402             _myConsoleLog(error);
20403         },
20404 
20405         /**
20406          * check storage data size and if need purge
20407          * @private
20408          */
20409         _checkSizeAndPurge = function () {
20410             var purgeIsDone=false, totalSize = _getTotalData();
20411             if (totalSize > 0.75*_maxLocalStorageSize) {
20412                _myConsoleLog("in _checkSizeAndPurge, totalSize ("+totalSize+") exceeds limit");
20413                purgeIsDone = _purgeOldSessions(totalSize);
20414                if (purgeIsDone) {
20415                   _myConsoleLog("in _checkSizeAndPurge after purging old session, purge is done");
20416                }
20417                else {
20418                   //after all old sessions purged, still need purge
20419                   totalSize = _getTotalData();
20420                   if (totalSize > 0.75*_maxLocalStorageSize) {
20421                       _myConsoleLog("in _checkSizeAndPurge after purging old session,still needs purging, now storeSize ("+totalSize+")");
20422                      _purgeCurrentSession();
20423                      _myConsoleLog("in _checkSizeAndPurge done purging current session.");
20424                   }
20425                }
20426             }
20427         },
20428         
20429         /**
20430          * check if the session is already in meta data  
20431          * 
20432          * @param metaData
20433          * @param sessionName
20434          * @return {Boolean} true if session has metaData (false otherwise)
20435          * @private
20436          */
20437         _sessionsInfoContains = function (metaData, sessionName) {
20438            if (metaData && metaData.sessions && metaData.sessions.hasOwnProperty(sessionName)) {
20439               return true;
20440            }
20441            return false;
20442         },
20443         
20444         
20445         /**
20446          * setup sessions in local storage 
20447          * 
20448          * @param logInfo
20449          * @private
20450          */
20451         _getAndSetNumberOfSessions = function (logInfo) {
20452             var numOfSessionsPass1, numOfSessionsPass2, l;
20453             numOfSessionsPass1 = localStorage.getItem("FinesseSessionsInfo");
20454             if (numOfSessionsPass1 === null) {
20455                 //Init first time
20456                 numOfSessionsPass1 = {};
20457                 numOfSessionsPass1.total = 1;
20458                 numOfSessionsPass1.sessions = {};
20459                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
20460                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
20461                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
20462             }
20463             else {
20464                 numOfSessionsPass1 = JSON.parse(numOfSessionsPass1);
20465                 //check if the session is already in the FinesseSessionSInfo
20466                 if (_sessionsInfoContains(numOfSessionsPass1, logInfo.name)) {
20467                     return;
20468                 }             
20469                 //Save numOfSessionsPass1
20470                 numOfSessionsPass1.total = parseInt(numOfSessionsPass1.total, 10) + 1;
20471                 numOfSessionsPass1.sessions[logInfo.name] = logInfo.startTime;
20472                 numOfSessionsPass1.lastWrittenBy = logInfo.name;
20473                 localStorage.setItem("FinesseSessionsInfo", JSON.stringify(numOfSessionsPass1));
20474                 numOfSessionsPass2 = localStorage.getItem("FinesseSessionsInfo");
20475                 if (!numOfSessionsPass2) {
20476                    _myConsoleLog("Could not get FinesseSessionsInfo");
20477                    return;
20478                 }
20479                 numOfSessionsPass2 = JSON.parse(numOfSessionsPass2);
20480                 //in future we need to confirm the numOfSessionsPass2 is the same as numOfSessionsPass1
20481                 ////if (numOfSessionsPass1.lastWrittenBy !== numOfSessionsPass2.lastWrittenBy) {
20482                 ////    _myConsoleLog("Rebuild sessions");
20483                 ////    _sessionTimerId = setTimeout(_initSessionList, 10000);
20484                 ////}
20485                 ////else {
20486                 ////    _sessionTimerId = null;
20487                 ////callback(numOfSessionsPass2.sessions);
20488                 ////}
20489             }
20490             if (!localStorage.getItem(_sessionKey)) {
20491                 localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
20492             }
20493         },
20494         
20495         
20496         /**
20497          * init session list 
20498          * @private
20499          */
20500         _initSessionList = function () {
20501             _getAndSetNumberOfSessions(_logInfo);
20502         },
20503         
20504        /**
20505         * do the real store of log line
20506         * 
20507         * @param line
20508         * @private
20509         */
20510         _persistLine = function (line) {
20511             var key, logInfoStr;
20512             logInfoStr = localStorage.getItem(_sessionKey);
20513             if (logInfoStr === null) {
20514                return;
20515             }
20516             _logInfo = JSON.parse(logInfoStr);
20517             _logInfo.head = _logInfo.head + 1;
20518             key = _linePrefix + _logInfo.head;
20519             localStorage.setItem(key, line);
20520             //Save the size
20521             _logInfo.size = _logInfo.size + line.length;
20522             if (_logInfo.tail === 0) {
20523                 _logInfo.tail = _logInfo.head;
20524             }
20525         
20526             localStorage.setItem(_sessionKey, JSON.stringify(_logInfo));
20527             _checkSizeAndPurge();
20528         },
20529         
20530         /**
20531          * Insert a line into the localStorage.
20532          *
20533          * @param line line to be inserted 
20534          * @private
20535         */
20536         _insertLine = function (line) {
20537             //_myConsoleLog("_insertLine: [" + line + "]");
20538             //Write the next line to localStorage
20539             try {
20540                //Persist the line 
20541                _persistLine(line);
20542             }
20543             catch (err) {
20544                _myConsoleLog("error in _insertLine(), err="+err);
20545                //_insertLineHandleError(err);
20546             }
20547         },
20548          
20549         
20550         /**
20551          * Clear the local storage
20552          * @private
20553          */
20554         _clearLocalStorage = function() {
20555             localStorage.clear();
20556 
20557         },
20558 
20559         /**
20560          * Collect logs when onCollect called
20561          *
20562          * @param data
20563          * @private
20564          */
20565         _collectMethod = function(data) {
20566           //Size of log should not exceed 1.5MB
20567           var info, maxLength = 1572864;
20568           
20569           //add size buffer equal to the size of info to be added when publish
20570           info = Utilities.getSanitizedUserAgentString() + "
";
20571           info = escape(info);
20572 
20573             //If log was empty previously, fade in buttons
20574             if (!_sendLogShown) {
20575                 _sendLogShown = true;
20576                 _logSize = info.length;
20577             }
20578             
20579             //if local storage logging is enabled, then insert the log into local storage
20580             if (window.sessionStorage.getItem('enableLocalLog')==='true') {
20581                 if (data) {
20582                    if (data.length>0 && data.substring(0,1) === '\n') {
20583                       _insertLine(data.substring(1));
20584                    }
20585                    else {
20586                       _insertLine(data);
20587                    }
20588                 }
20589             }
20590               
20591             //escape all data to get accurate size (shindig will escape when it builds request)
20592             //escape 6 special chars for XML: &<>"'\n
20593             data = data.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/>/g, ">").replace(/</g, "<").replace(/\n/g, "
");
20594             //Send Error Report crashes with Control characters. Replacing the control characters with empty string
20595             data = data.replace(/[\x00-\x1F\x7F-\x9F]/g, "");
20596             data = escape(data+"\n");
20597 
20598             if (data.length < maxLength){
20599                 //make room for new data if log is exceeding max length
20600                 while (_logSize + data.length > maxLength) {
20601                     _logSize -= (_logArray.shift()).length;
20602                 }
20603             }
20604 
20605             //Else push the log into memory, increment the log size
20606             _logArray.push(data);
20607 
20608             //inc the size accordingly
20609             _logSize+=data.length;
20610 
20611         };
20612 
20613         return {
20614 
20615             /**
20616              * @private
20617              * Initiate FinesseLogger.
20618              */
20619             init: function () {
20620                 ClientServices.subscribe("finesse.clientLogging.*", _collectMethod);
20621                 _initLogging();
20622             },
20623 
20624             /**
20625              * @private
20626              * Clear all items stored in localStorage.
20627             */
20628             clear : function () {
20629                _clearLocalStorage();
20630             },
20631 
20632             /**
20633              * @private
20634              * Initialize the local storage logging.
20635             */
20636             initLocalLog: function () {
20637                _initLogging();
20638             },
20639 
20640             /**
20641              * @private
20642              * Inserts a line into the localStorage.
20643              * @param line to insert
20644             */
20645             localLog : function (line) {
20646                _insertLine(line);
20647             },
20648 
20649            /**
20650             * @ignore
20651             * Publish logs to server and clear the memory
20652             *
20653             * @param userObj
20654             * @param options
20655             * @param callBack
20656             */
20657             publish: function(userObj, options, callBack) {
20658                 // Avoid null references.
20659                 options = options || {};
20660                 callBack = callBack || {};
20661 
20662                 if (callBack.sending === "function") {
20663                     callBack.sending();
20664                 }
20665 
20666                 //logs the basic version and machine info and escaped new line
20667                 _logStr = Utilities.getSanitizedUserAgentString() + "
";
20668                 
20669                 //join the logs to correct string format
20670                 _logStr += unescape(_logArray.join(""));
20671 
20672                 //turning log string to JSON obj
20673                 var logObj = {
20674                         ClientLog: {
20675                         logData : _logStr //_logStr
20676                     }
20677                 },
20678                 tmpOnAdd = (options.onAdd && typeof options.onAdd === "function")? options.onAdd : function(){};
20679                 /** @private */
20680                 options.onAdd = function(){
20681                     tmpOnAdd();
20682                     _logArray.length = 0; _logSize =0;
20683                     _sendLogShown = false;
20684                     };
20685                 //adding onLoad to the callbacks, this is the subscribe success case for the first time user subscribe to the client log node
20686                 /** @private */
20687                 options.onLoad = function (clientLogObj) {
20688                     clientLogObj.sendLogs(logObj,{
20689                             error: callBack.error
20690                         });
20691                     };
20692 
20693                 userObj.getClientLog(options);
20694             }
20695         };
20696     }());
20697 
20698     window.finesse = window.finesse || {};
20699     window.finesse.cslogger = window.finesse.cslogger || {};
20700     /** @private */
20701     window.finesse.cslogger.FinesseLogger = FinesseLogger;
20702 
20703     return FinesseLogger;
20704 });
20705 
20706 /**
20707  *  Contains a list of topics used for containerservices pubsub.
20708  *
20709  */
20710 
20711 /**
20712  * @class
20713  * Contains a list of topics with some utility functions.
20714  */
20715 /** @private */
20716 define('containerservices/Topics',[], function () {
20717 
20718     var Topics = (function () {
20719 
20720     /**
20721      * The namespace prepended to all Finesse topics.
20722      */
20723     this.namespace = "finesse.containerservices";
20724 
20725     /**
20726      * @private
20727      * Gets the full topic name with the ContainerServices namespace prepended.
20728      * @param {String} topic
20729      *     The topic category.
20730      * @returns {String}
20731      *     The full topic name with prepended namespace.
20732      */
20733     var _getNSTopic = function (topic) {
20734         return this.namespace + "." + topic;
20735     };
20736 
20737 
20738 
20739     /** @scope finesse.containerservices.Topics */
20740     return {
20741         /** 
20742          * @private
20743          * request channel. */
20744         REQUESTS: _getNSTopic("requests"),
20745 
20746         /** 
20747          * @private
20748          * reload gadget channel. */
20749         RELOAD_GADGET: _getNSTopic("reloadGadget"),
20750 
20751         /**
20752          * @private
20753          * Convert a Finesse REST URI to a OpenAjax compatible topic name.
20754          */
20755         getTopic: function (restUri) {
20756             //The topic should not start with '/' else it will get replaced with
20757             //'.' which is invalid.
20758             //Thus, remove '/' if it is at the beginning of the string
20759             if (restUri.indexOf('/') === 0) {
20760                 restUri = restUri.substr(1);
20761             }
20762 
20763             //Replace every instance of "/" with ".". This is done to follow the
20764             //OpenAjaxHub topic name convention.
20765             return restUri.replace(/\//g, ".");
20766         }
20767     };
20768 	}());
20769 	
20770 	window.finesse = window.finesse || {};
20771     window.finesse.containerservices = window.finesse.containerservices || {};
20772     window.finesse.containerservices.Topics = Topics;
20773     
20774     /** @namespace JavaScript class objects and methods to handle gadget container services.*/
20775     finesse.containerservices = finesse.containerservices || {};
20776 
20777     return Topics;
20778  });
20779 
20780 /** The following comment is to prevent jslint errors about 
20781  * using variables before they are defined.
20782  */
20783 /*global finesse*/
20784 
20785 /**
20786  * Per containerservices request, publish to the OpenAjax gadget pubsub infrastructure.
20787  *
20788  * @requires OpenAjax, finesse.containerservices.Topics
20789  */
20790 
20791 /** @private */
20792 define('containerservices/MasterPublisher',[
20793     "utilities/Utilities",
20794     "containerservices/Topics"
20795 ],
20796 function (Utilities, Topics) {
20797 
20798     var MasterPublisher = function () {
20799 
20800     var
20801     
20802     /**
20803      * Reference to the gadget pubsub Hub instance.
20804      * @private
20805      */
20806     _hub = gadgets.Hub,
20807 
20808     /**
20809      * Reference to the Topics class.
20810      * @private
20811      */
20812     _topics = Topics,
20813     
20814     /**
20815      * Reference to conversion utilities class.
20816      * @private
20817      */
20818     _utils = Utilities,
20819     
20820     /**
20821      * References to ClientServices logger methods
20822      * @private
20823      */
20824     _logger = {
20825         log: finesse.clientservices.ClientServices.log
20826     },
20827     
20828    /**
20829      * The types of possible request types supported when listening to the
20830      * requests channel. Each request type could result in different operations.
20831      * @private
20832      */
20833     _REQTYPES = {
20834 		ACTIVETAB: "ActiveTabReq",
20835 		SET_ACTIVETAB: "SetActiveTabReq",
20836         RELOAD_GADGET: "ReloadGadgetReq"
20837     },
20838 
20839     /**
20840      * Handles client requests made to the request topic. The type of the
20841      * request is described in the "type" property within the data payload. Each
20842      * type can result in a different operation.
20843      * @param {String} topic
20844      *     The topic which data was published to.
20845      * @param {Object} data
20846      *     The data containing requests information published by clients.
20847      * @param {String} data.type
20848      *     The type of the request. Supported: "ActiveTabReq", "SetActiveTabReq", "ReloadGadgetReq"
20849      * @param {Object} data.data
20850      *     May contain data relevant for the particular requests.
20851      * @param {String} [data.invokeID]
20852      *     The ID used to identify the request with the response. The invoke ID
20853      *     will be included in the data in the publish to the topic. It is the
20854      *     responsibility of the client to correlate the published data to the
20855      *     request made by using the invoke ID.
20856      * @private
20857      */
20858     _clientRequestHandler = function (topic, data) {
20859     
20860         //Ensure a valid data object with "type" and "data" properties.
20861         if (typeof data === "object" &&
20862                 typeof data.type === "string" &&
20863                 typeof data.data === "object") {
20864 			switch (data.type) {
20865 			case _REQTYPES.ACTIVETAB:
20866                 _hub.publish("finesse.containerservices.activeTab", finesse.container.Tabs.getActiveTab());
20867                 break;
20868             case _REQTYPES.SET_ACTIVETAB:
20869                 if (typeof data.data.id === "string") {
20870                     _logger.log("Handling request to activate tab: " + data.data.id);
20871                     if (!finesse.container.Tabs.activateTab(data.data.id)) {
20872                         _logger.log("No tab found with id: " + data.data.id);
20873                     }
20874                 }
20875                 break;
20876             case _REQTYPES.RELOAD_GADGET:
20877                 _hub.publish("finesse.containerservices.reloadGadget", data.data);
20878                 break;
20879 			default:
20880 				break;
20881 			}
20882         }
20883     };
20884 
20885     (function () {
20886 
20887         //Listen to a request channel to respond to any requests made by other
20888         //clients because the Master may have access to useful information.
20889         _hub.subscribe(_topics.REQUESTS, _clientRequestHandler);
20890     }());
20891 
20892     //BEGIN TEST CODE//
20893     /**
20894      * Test code added to expose private functions that are used by unit test
20895      * framework. This section of code is removed during the build process
20896      * before packaging production code. The [begin|end]TestSection are used
20897      * by the build to identify the section to strip.
20898      * @ignore
20899      */
20900     this.beginTestSection = 0;
20901 
20902     /**
20903      * @ignore
20904      */
20905     this.getTestObject = function () {
20906         //Load mock dependencies.
20907         var _mock = new MockControl();
20908         _hub = _mock.createMock(gadgets.Hub);
20909 
20910         return {
20911             //Expose mock dependencies
20912             mock: _mock,
20913             hub: _hub,
20914 			
20915             //Expose internal private functions
20916             reqtypes: _REQTYPES,
20917             
20918             clientRequestHandler: _clientRequestHandler
20919 
20920         };
20921     };
20922 
20923 
20924     /**
20925      * @ignore
20926      */
20927     this.endTestSection = 0;
20928     //END TEST CODE//
20929 	};
20930 	
20931 	window.finesse = window.finesse || {};
20932     window.finesse.containerservices = window.finesse.containerservices || {};
20933     window.finesse.containerservices.MasterPublisher = MasterPublisher;
20934 	
20935     return MasterPublisher;
20936 });
20937 
20938 /**
20939  * JavaScript representation of the Finesse WorkflowActionEvent object.
20940  *
20941  * @requires finesse.FinesseBase
20942  */
20943 
20944 /** The following comment is to prevent jslint errors about 
20945  * using variables before they are defined.
20946  */
20947 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
20948 /** @private */
20949 define('containerservices/WorkflowActionEvent', ["FinesseBase"], function (FinesseBase) {
20950     var WorkflowActionEvent = FinesseBase.extend(/** @lends finesse.containerservices.WorkflowActionEvent.prototype */{
20951         /**
20952          * Reference to the WorkflowActionEvent name
20953          * This will be set by setWorkflowActionEvent
20954          * @private
20955          */
20956         _name: null,
20957 
20958         /**
20959          * Reference to the WorkflowActionEvent type
20960          * This will be set by setWorkflowActionEvent
20961          * @private
20962          */
20963         _type: null,
20964 
20965         /**
20966          * Reference to the WorkflowActionEvent handledBy value
20967          * This will be set by setWorkflowActionEvent
20968          * @private
20969          */
20970         _handledBy: null,
20971 
20972         /**
20973          * Reference to the WorkflowActionEvent params array
20974          * This will be set by setWorkflowActionEvent
20975          * @private
20976          */
20977         _params: [],
20978 
20979         /**
20980          * Reference to the WorkflowActionEvent actionVariables array
20981          * This will be set by setWorkflowActionEvent
20982          * @private
20983          */            
20984         _actionVariables: [], 
20985         
20986         /**
20987          * @class
20988          * JavaScript representation of a WorkflowActionEvent object.
20989          * The WorkflowActionEvent object is delivered as the payload of
20990          * a WorkflowAction callback.  This can be subscribed to by using
20991          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
20992          * topic of {@link finesse.containerservices.ContainerServices.Topics#WORKFLOW_ACTION_EVENT}. 
20993          * Gadgets should key on events with a handleBy value of "OTHER".
20994          * 
20995          * @constructs
20996          **/
20997         init: function () {
20998             this._super();
20999         },        
21000 
21001         /**
21002 	     * Validate that the passed in object is a WorkflowActionEvent object
21003 	     * and sets the variables if it is
21004 	     * @param maybeWorkflowActionEvent A possible WorkflowActionEvent object to be evaluated and set if 
21005 	     *                                 it validates successfully.
21006 	     * @returns {Boolean} Whether it is valid or not.
21007          * @private
21008 	     */
21009 	    setWorkflowActionEvent: function(maybeWorkflowActionEvent) {
21010 	        var returnValue;
21011 	
21012 	        if (maybeWorkflowActionEvent.hasOwnProperty("name") === true &&
21013 	                maybeWorkflowActionEvent.hasOwnProperty("type") === true &&
21014                     maybeWorkflowActionEvent.hasOwnProperty("handledBy") === true &&
21015 	                maybeWorkflowActionEvent.hasOwnProperty("params") === true &&
21016 	                maybeWorkflowActionEvent.hasOwnProperty("actionVariables") === true) {
21017 	            this._name = maybeWorkflowActionEvent.name;
21018 	            this._type = maybeWorkflowActionEvent.type;
21019                 this._handledBy = maybeWorkflowActionEvent.handledBy;
21020 	            this._params = maybeWorkflowActionEvent.params;
21021 	            this._actionVariables = maybeWorkflowActionEvent.actionVariables;
21022 	            returnValue = true;
21023 	        } else {
21024 	            returnValue = false;
21025 	        }
21026 	
21027 	        return returnValue;
21028 	    },
21029 	
21030 	    /**
21031 	     * Getter for the WorkflowActionEvent name.
21032 	     * @returns {String} The name of the WorkflowAction.
21033 	     */
21034 	    getName: function () {
21035 	        // escape nulls to empty string
21036 	        return this._name || "";
21037 	    },
21038 	
21039 	    /**
21040 	     * Getter for the WorkflowActionEvent type.
21041 	     * @returns {String} The type of the WorkflowAction (BROWSER_POP, HTTP_REQUEST).
21042 	     */
21043 	    getType: function () {
21044 	        // escape nulls to empty string
21045 	        return this._type || "";
21046 	    },
21047 	
21048         /**
21049          * Getter for the WorkflowActionEvent handledBy value. Gadgets should look for
21050          * events with a handleBy of "OTHER".
21051          * @see finesse.containerservices.WorkflowActionEvent.HandledBy
21052          * @returns {String} The handledBy value of the WorkflowAction that is a value of {@link finesse.containerservices.WorkflowActionEvent.HandledBy}.
21053          */
21054         getHandledBy: function () {
21055             // escape nulls to empty string
21056             return this._handledBy || "";
21057         },
21058 
21059 
21060 	    /**
21061 	     * Getter for the WorkflowActionEvent Params map.
21062 	     * @returns {Object} key = param name, value = Object{name, value, expandedValue}
21063 	     * BROWSER_POP<ul>
21064 	     * <li>windowName : Name of window to pop into, or blank to always open new window.
21065 	     * <li>path : URL to open.</ul>
21066 	     * HTTP_REQUEST<ul>
21067 	     * <li>method : "PUT" or "POST".
21068 	     * <li>location : "FINESSE" or "OTHER".
21069 	     * <li>contentType : MIME type of request body, if applicable, e.g. "text/plain".
21070 	     * <li>path : Request URL.
21071 	     * <li>body : Request content for POST requests.</ul>
21072 	     */
21073 	    getParams: function () {
21074 	        var map = {},
21075 	            params = this._params,
21076 	            i,
21077 	            param;
21078 	
21079 	        if (params === null || params.length === 0) {
21080 	            return map;
21081 	        }
21082 	
21083 	        for (i = 0; i < params.length; i += 1) {
21084 	            param = params[i];
21085 	            // escape nulls to empty string
21086 	            param.name = param.name || "";
21087 	            param.value = param.value || "";
21088 	            param.expandedValue = param.expandedValue || "";
21089 	            map[param.name] = param;
21090 	        }
21091 	
21092 	        return map;
21093 	    },
21094 	    
21095 	    /**
21096 	     * Getter for the WorkflowActionEvent ActionVariables map
21097 	     * @returns {Object} key = action variable name, value = Object{name, type, node, testValue, actualValue}
21098 	     */
21099 	    getActionVariables: function() {
21100 	        var map = {},
21101 	            actionVariables = this._actionVariables,
21102 	            i,
21103 	            actionVariable;
21104 	
21105 	        if (actionVariables === null || actionVariables.length === 0) {
21106 	            return map;
21107 	        }
21108 	
21109 	        for (i = 0; i < actionVariables.length; i += 1) {
21110 	            actionVariable = actionVariables[i];
21111 	            // escape nulls to empty string
21112 	            actionVariable.name = actionVariable.name || "";
21113 	            actionVariable.type = actionVariable.type || "";
21114 	            actionVariable.node = actionVariable.node || "";
21115 	            actionVariable.testValue = actionVariable.testValue || "";
21116 	            actionVariable.actualValue = actionVariable.actualValue || "";
21117 	            map[actionVariable.name] = actionVariable;
21118 	        }
21119 	
21120 	        return map;
21121 	    }
21122     }); 
21123     
21124     
21125     WorkflowActionEvent.HandledBy = /** @lends finesse.containerservices.WorkflowActionEvent.HandledBy.prototype */ {
21126         /**
21127          * This specifies that Finesse will handle this WorkflowActionEvent.  A 3rd Party can do additional processing
21128          * with the action, but first and foremost Finesse will handle this WorkflowAction.
21129          */
21130         FINESSE: "FINESSE",
21131 
21132         /**
21133          * This specifies that a 3rd Party will handle this WorkflowActionEvent.  Finesse's Workflow Engine Executor will 
21134          * ignore this action and expects Gadget Developers to take action.
21135          */
21136         OTHER: "OTHER",
21137         
21138         /**
21139          * @class This is the set of possible HandledBy values used for WorkflowActionEvent from ContainerServices.  This
21140          * is provided from the {@link finesse.containerservices.WorkflowActionEvent#getHandledBy} method.
21141          * @constructs
21142          */
21143         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
21144     };    
21145     
21146     window.finesse = window.finesse || {};
21147     window.finesse.containerservices = window.finesse.containerservices || {};
21148     window.finesse.containerservices.WorkflowActionEvent = WorkflowActionEvent;
21149     
21150     return WorkflowActionEvent;
21151 });
21152 
21153 /**
21154  * JavaScript representation of the Finesse TimerTickEvent
21155  *
21156  * @requires finesse.FinesseBase
21157  */
21158 
21159 /** The following comment is to prevent jslint errors about 
21160  * using variables before they are defined.
21161  */
21162 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
21163 /** @private */
21164 define('containerservices/TimerTickEvent',[
21165     "FinesseBase"
21166 ],
21167 function (FinesseBase) {
21168     var TimerTickEvent = FinesseBase.extend(/** @lends finesse.containerservices.TimerTickEvent.prototype */{
21169         /**
21170          * date the TimerTickEvent was queued 
21171          * @private
21172          */
21173         _dateQueued: null,
21174 
21175         /**
21176          * the frequency of the timer tick (in miiliseconds)
21177          * @private
21178          */
21179         _tickFrequency: 1000,
21180 
21181         /**
21182          * @class
21183          * JavaScript representation of a TimerTickEvent object.
21184          * The TimerTickEvent object is delivered as the payload of
21185          * a TimerTickEvent callback.  This can be subscribed to by using
21186          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
21187          * topic of {@link finesse.containerservices.ContainerServices.Topics#TIMER_TICK_EVENT}. 
21188          * 
21189          * @constructs
21190          **/
21191         init: function (tickFrequency, dateQueued) {
21192             this._super();
21193             
21194             this._tickFrequency = tickFrequency;
21195             this._dateQueued = dateQueued;
21196         },
21197 
21198        /**
21199          * Get the "tickFrequency" field
21200          * @param {int} which is the "TickFrequency" field
21201          * @private
21202          */
21203         getTickFrequency: function () {
21204             return this._tickFrequency;
21205         },
21206 
21207         /**
21208          * Getter for the TimerTickEvent "DateQueued" field. 
21209          * @returns {Date} which is a Date object when the TimerTickEvent was queued
21210          */
21211         getDateQueued: function () {
21212             return this._dateQueued;
21213         }
21214 
21215     });
21216     
21217     window.finesse = window.finesse || {};
21218     window.finesse.containerservices = window.finesse.containerservices || {};
21219     window.finesse.containerservices.TimerTickEvent = TimerTickEvent;
21220     
21221     return TimerTickEvent;
21222 });
21223 
21224 /**
21225  * JavaScript representation of the Finesse GadgetViewChangedEvent object.
21226  *
21227  * @requires finesse.FinesseBase
21228  */
21229 
21230 /** The following comment is to prevent jslint errors about 
21231  * using variables before they are defined.
21232  */
21233 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
21234 /** @private */
21235 define('containerservices/GadgetViewChangedEvent',[
21236     "FinesseBase"
21237 ],
21238 function (FinesseBase) {
21239     var GadgetViewChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.GadgetViewChangedEvent.prototype */{
21240         /**
21241          * Reference to the gadget id
21242          * @private
21243          */
21244         _gadgetId: null,
21245 
21246         /**
21247          * Reference to the tab id
21248          * @private
21249          */
21250         _tabId: null,
21251 
21252         /**
21253          * Reference to the maxAvailableHeight
21254          * @private
21255          */
21256         _maxAvailableHeight: null,
21257 
21258         /**
21259          * Reference to the view
21260          * E.g. 'default' or 'canvas'
21261          * @private
21262          */
21263         _view: null,
21264         
21265         /**
21266          * @class
21267          * JavaScript representation of a GadgetViewChangedEvent object.
21268          * The GadgetViewChangedEvent object is delivered as the payload of
21269          * a GadgetViewChangedEvent callback.  This can be subscribed to by using
21270          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
21271          * topic of {@link finesse.containerservices.ContainerServices.Topics#GADGET_VIEW_CHANGED_EVENT}. 
21272          * 
21273          * @constructs
21274          **/
21275         init: function (gadgetId, tabId, maxAvailableHeight, view) {
21276             this._super();
21277 
21278             this._gadgetId = gadgetId;
21279             this._tabId = tabId;
21280             this._maxAvailableHeight = maxAvailableHeight;
21281             this._view = view;
21282         },
21283     
21284         /**
21285          * Getter for the gadget id.
21286          * @returns {String} The identifier for the gadget changing view.
21287          */
21288         getGadgetId: function () {
21289             // escape nulls to empty string
21290             return this._gadgetId || "";
21291         },
21292     
21293         /**
21294          * Getter for the maximum available height.
21295          * @returns {String} The maximum available height for the gadget's view.
21296          */
21297         getMaxAvailableHeight: function () {
21298             // escape nulls to empty string
21299             return this._maxAvailableHeight || "";
21300         },
21301 
21302         /**
21303          * Getter for the tab id.
21304          * @returns {String} The identifier for the tab where the gadget changing view resides.
21305          */
21306         getTabId: function () {
21307             // escape nulls to empty string
21308             return this._tabId || "";
21309         },
21310 
21311         /**
21312          * Getter for the view.
21313          * @returns {String} The view type the gadget is changing to.
21314          */
21315         getView: function () {
21316             // escape nulls to empty string
21317             return this._view || "";
21318         }
21319     });
21320     
21321     window.finesse = window.finesse || {};
21322     window.finesse.containerservices = window.finesse.containerservices || {};
21323     window.finesse.containerservices.GadgetViewChangedEvent = GadgetViewChangedEvent;
21324     
21325     return GadgetViewChangedEvent;
21326 });
21327 
21328 /**
21329  * JavaScript representation of the Finesse MaxAvailableHeightChangedEvent object.
21330  *
21331  * @requires finesse.FinesseBase
21332  */
21333 
21334 /** The following comment is to prevent jslint errors about 
21335  * using variables before they are defined.
21336  */
21337 /*global FinesseBase: true, publisher:true, define:true, finesse:true, window:true */
21338 /** @private */
21339 define('containerservices/MaxAvailableHeightChangedEvent',[
21340     "FinesseBase"
21341 ],
21342 function (FinesseBase) {
21343     var MaxAvailableHeightChangedEvent = FinesseBase.extend(/** @lends finesse.containerservices.MaxAvailableHeightChangedEvent.prototype */{
21344 
21345         /**
21346          * Reference to the maxAvailableHeight
21347          * @private
21348          */
21349         _maxAvailableHeight: null,
21350         
21351         /**
21352          * @class
21353          * JavaScript representation of a MaxAvailableHeightChangedEvent object.
21354          * The MaxAvailableHeightChangedEvent object is delivered as the payload of
21355          * a MaxAvailableHeightChangedEvent callback.  This can be subscribed to by using
21356          * {@link finesse.containerservices.ContainerServices#addHandler} with a 
21357          * topic of {@link finesse.containerservices.ContainerServices.Topics#MAX_AVAILABLE_HEIGHT_CHANGED_EVENT}. 
21358          * 
21359          * @constructs
21360          **/
21361         init: function (maxAvailableHeight) {
21362             this._super();
21363 
21364             this._maxAvailableHeight = maxAvailableHeight;
21365         },
21366     
21367         /**
21368          * Getter for the maximum available height.
21369          * @returns {String} The maximum available height for a gadget in canvas view
21370          */
21371         getMaxAvailableHeight: function () {
21372             // escape nulls to empty string
21373             return this._maxAvailableHeight || "";
21374         }
21375     });
21376     
21377     window.finesse = window.finesse || {};
21378     window.finesse.containerservices = window.finesse.containerservices || {};
21379     window.finesse.containerservices.MaxAvailableHeightChangedEvent = MaxAvailableHeightChangedEvent;
21380     
21381     return MaxAvailableHeightChangedEvent;
21382 });
21383 
21384 /**
21385  * Exposes a set of API wrappers that will hide the dirty work of
21386  *     constructing Finesse API requests and consuming Finesse events.
21387  *
21388  * @requires OpenAjax, jQuery 1.5, finesse.utilities.Utilities
21389  */
21390 
21391 /** The following comment is to prevent jslint errors about using variables before they are defined. */
21392 /*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 */
21393 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
21394 /** @private */
21395 define('containerservices/ContainerServices',[
21396     "utilities/Utilities",
21397     "restservices/Notifier",
21398     "containerservices/Topics",
21399     "containerservices/MasterPublisher",
21400     "containerservices/WorkflowActionEvent",
21401     "containerservices/TimerTickEvent",
21402     "containerservices/GadgetViewChangedEvent",
21403     "containerservices/MaxAvailableHeightChangedEvent"
21404 ],
21405 function (Utilities, Notifier, Topics, MasterPublisher, WorkflowActionEvent) {
21406 
21407     var ContainerServices = ( function () { /** @lends finesse.containerservices.ContainerServices.prototype */
21408 
21409     var
21410 
21411     /**
21412      * Shortcut reference to the Utilities singleton
21413      * This will be set by init()
21414      * @private
21415      */
21416     _util,
21417 
21418     /**
21419      * Shortcut reference to the gadget pubsub Hub instance.
21420      * This will be set by init()
21421      * @private
21422      */
21423     _hub,
21424 
21425     /**
21426      * Boolean whether this instance is master or not
21427      * @private
21428      */
21429     _master = false,
21430 
21431     /**
21432      * Whether the Client Services have been initiated yet.
21433      * @private
21434      */
21435     _inited = false,
21436     
21437     /**
21438      * References to ClientServices logger methods
21439      * @private
21440      */
21441     _logger = {
21442         log: finesse.clientservices.ClientServices.log
21443     },
21444     
21445      /**
21446      * Stores the list of subscription IDs for all subscriptions so that it
21447      * could be retrieve for unsubscriptions.
21448      * @private
21449      */
21450     _subscriptionID = {},
21451     
21452     /**
21453      * Reference to the gadget's parent container
21454      * @private
21455      */
21456     _container,
21457 
21458     /**
21459      * Reference to the MasterPublisher
21460      * @private
21461      */
21462     _publisher,
21463     
21464     /**
21465      * Object that will contain the Notifiers
21466      * @private
21467      */
21468     _notifiers = {},
21469 
21470     /**
21471      * Reference to the tabId that is associated with the gadget
21472      * @private
21473      */
21474     _myTab = null,
21475     
21476     /**
21477      * Reference to the visibility of current gadget
21478      * @private
21479      */
21480     _visible = false,
21481     
21482     /**
21483      * Reference for auth modes constants.
21484      * @private
21485      */
21486     _authModes,
21487     
21488     /**
21489      * Shortcut reference to the Topics class.
21490      * This will be set by init()
21491      * @private
21492      */
21493     _topics,
21494     
21495     /**
21496      * Check whether the common desktop apis are available for finext.
21497      * In case it not available it will use the existing finesse Tab logic
21498      * @private
21499      */
21500     _commonDesktop,
21501 
21502     /**
21503      * Associates a topic name with the private handler function.
21504      * Adding a new topic requires that you add this association here 
21505      *  in to keep addHandler generic.
21506      * @param {String} topic : Specifies the callback to retrieve
21507      * @return {Function} The callback function associated with the topic param.
21508      * @private
21509      */
21510     _topicCallback = function (topic) {
21511         var callback, notifier;
21512         switch (topic)
21513         {
21514             case finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB:
21515                 callback = _tabTracker;
21516                 break;
21517             case finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT:
21518                 callback = _workflowActionEventTracker;
21519                 break;
21520             case finesse.containerservices.ContainerServices.Topics.RELOAD_GADGET_EVENT:
21521                 callback = _masterReloader;
21522                 break;
21523             case finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT:
21524                 callback = _gadgetViewChanged;
21525                 break;
21526             case finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT:
21527                 callback = _maxAvailableHeightChanged;
21528                 break;
21529             case finesse.containerservices.ContainerServices.Topics.ACCESS_TOKEN_REFRESHED_EVENT:
21530                 callback = _accessTokenRefreshed;
21531                 break;
21532             default:
21533                 callback = function (param) {
21534                      var data = null;
21535                      
21536                      notifier = _getNotifierReference(topic);
21537                      
21538                      if (arguments.length === 1) {
21539                         data = param;
21540                      } else {
21541                         data = arguments;
21542                      }
21543                      notifier.notifyListeners(data);
21544                 };
21545         }
21546         return callback;
21547     },
21548 
21549     /**
21550      * Ensure that ClientServices have been inited.
21551      * @private
21552      */
21553     _isInited = function () {
21554         if (!_inited) {
21555             throw new Error("ContainerServices needs to be inited.");
21556         }
21557         return _inited;
21558     },
21559 
21560     /**
21561      * Retrieves a Notifier reference to a particular topic, and creates one if it doesn't exist.
21562      * @param {String} topic : Specifies the notifier to retrieve
21563      * @return {Notifier} The notifier object.
21564      * @private
21565      */
21566     _getNotifierReference = function (topic) {
21567         if (!_notifiers.hasOwnProperty(topic))
21568         {
21569             _notifiers[topic] = new Notifier();
21570         }
21571 
21572         return _notifiers[topic];
21573     },
21574 
21575     /**
21576      * Utility function to make a subscription to a particular topic. Only one
21577      * callback function is registered to a particular topic at any time.
21578      * @param {String} topic
21579      *     The full topic name. The topic name should follow the OpenAjax
21580      *     convention using dot notation (ex: finesse.api.User.1000).
21581      * @param {Function} callback
21582      *     The function that should be invoked with the data when an event
21583      *     is delivered to the specific topic.
21584      * @returns {Boolean}
21585      *     True if the subscription was made successfully and the callback was
21586      *     been registered. False if the subscription already exist, the
21587      *     callback was not overwritten.
21588      * @private
21589      */
21590     _subscribe = function (topic, callback) {
21591         _isInited();
21592 
21593         //Ensure that the same subscription isn't made twice.
21594         if (!_subscriptionID[topic]) {
21595             //Store the subscription ID using the topic name as the key.
21596             _subscriptionID[topic] = _hub.subscribe(topic,
21597                 //Invoke the callback just with the data object.
21598                 function (topic, data) {
21599                     callback(data);
21600                 });
21601             return true;
21602         }
21603         return false;
21604     },
21605 
21606     /**
21607      * Unsubscribe from a particular topic.
21608      * @param {String} topic : The full topic name.
21609      * @private
21610      */
21611     _unsubscribe = function (topic) {
21612         _isInited();
21613 
21614         //Unsubscribe from the topic using the subscription ID recorded when
21615         //the subscription was made, then delete the ID from data structure.
21616         _hub.unsubscribe(_subscriptionID[topic]);
21617         delete _subscriptionID[topic];
21618     },
21619 
21620     /**
21621      * Get my tab id.
21622      * @returns {String} tabid : The tabid of this container/gadget.
21623      * @private
21624      */
21625     _getMyTab = function () {
21626     	
21627     	// Adding startsWith to the string prototype for IE browser
21628     	// See defect CSCvj93044
21629     	
21630     	if (!String.prototype.startsWith) {
21631       	  String.prototype.startsWith = function(searchString, position) {
21632       	    position = position || 0;
21633       	    return this.indexOf(searchString, position) === position;
21634       	  };
21635       	}
21636     	
21637 	if(_commonDesktop){
21638         /**
21639          *  This change is done for SPOG. SPOG container will set routNmae(i.e. current nav item)
21640          *  as user preference
21641          */
21642         var prefs,routeName;
21643         if (gadgets && gadgets.Prefs) {
21644         	prefs = gadgets.Prefs();
21645         	routeName = prefs.getString('routeName');
21646         }
21647 
21648         if (routeName) {
21649             _myTab = routeName;
21650         } else {	
21651             //This will return the nav name of the currently selected iframe.This selection is similar to the existing finesse desktop.
21652             //This is not tested with the page level gadget
21653             _myTab = _commonDesktop.route.getAllRoute()[$(frameElement).closest('div[data-group-id]').attr('data-group-id')-1];
21654             if(_myTab){
21655                 _myTab = _myTab.startsWith('#/') ? _myTab.slice(2) : _myTab;
21656             }
21657         }		
21658 	}else{
21659 		if (_myTab === null){
21660 			try {
21661 				_myTab = $(frameElement).closest("div.tab-panel").attr("id").replace("panel_", "");
21662 			}catch (err) {
21663 				_logger.log("Error accessing current tab: " + err.message);
21664 				_myTab = null;
21665 			}
21666 		}	
21667 	}  
21668 	return _myTab;
21669     },
21670     
21671     /**
21672      * Callback function that is called when an activeTab message is posted to the Hub.
21673      * Notifies listener functions if this tab is the one that was just made active.
21674      * @param {String} tabId : The tabId which was just made visible.
21675      * @private
21676      */
21677     _tabTracker = function(tabId) {
21678         if (tabId === _getMyTab()) {
21679             if(!_visible) {
21680                 _visible = true;
21681                 _notifiers[finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB].notifyListeners(this);
21682             }
21683         } else {
21684             _visible = false;
21685         }
21686     },
21687     
21688     /**
21689      * Make a request to set a particular tab active. This
21690      * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
21691      * to ensure the gadget gets properly initialized.
21692      * @param {String} tabId
21693      *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
21694      * @private
21695      */
21696     _activateTab = function ( tabId ) {
21697         _logger.log("Sending request to activate tab: " + tabId);
21698         if(_hub){
21699             var data = {
21700                 type: "SetActiveTabReq",
21701                 data: { id: tabId },
21702                 invokeID: (new Date()).getTime()          
21703             };
21704             _hub.publish(_topics.REQUESTS, data);
21705         } else {
21706             throw new Error("Hub is not defined.");
21707         }
21708         
21709     },
21710 
21711     /**
21712      * Callback function that is called when a gadget view changed message is posted to the Hub.
21713      * @private
21714      */
21715     _gadgetViewChanged = function (data) {
21716         if (data) {
21717             var gadgetViewChangedEvent = new finesse.containerservices.GadgetViewChangedEvent(
21718                 data.gadgetId,
21719                 data.tabId,
21720                 data.maxAvailableHeight,
21721                 data.view);
21722 
21723             _notifiers[finesse.containerservices.ContainerServices.Topics.GADGET_VIEW_CHANGED_EVENT].notifyListeners(gadgetViewChangedEvent);
21724         }
21725     },
21726 
21727     /**
21728      * Callback function that is called when a max available height changed message is posted to the Hub.
21729      * @private
21730      */
21731     _maxAvailableHeightChanged = function (data) {
21732         if (data) {
21733             var maxAvailableHeightChangedEvent = new finesse.containerservices.MaxAvailableHeightChangedEvent(
21734                 data.maxAvailableHeight);
21735 
21736             _notifiers[finesse.containerservices.ContainerServices.Topics.MAX_AVAILABLE_HEIGHT_CHANGED_EVENT].notifyListeners(maxAvailableHeightChangedEvent);
21737         }
21738     },
21739 
21740     /**
21741      * Callback function that is called when a workflowActionEvent message is posted to the Hub.
21742      * Notifies listener functions if the posted object can be converted to a proper WorkflowActionEvent object.
21743      * @param {String} workflowActionEvent : The workflowActionEvent that was posted to the Hub
21744      * @private
21745      */
21746     _workflowActionEventTracker = function(workflowActionEvent) {
21747         var vWorkflowActionEvent = new finesse.containerservices.WorkflowActionEvent();
21748                 
21749         if (vWorkflowActionEvent.setWorkflowActionEvent(workflowActionEvent)) {
21750             _notifiers[finesse.containerservices.ContainerServices.Topics.WORKFLOW_ACTION_EVENT].notifyListeners(vWorkflowActionEvent);
21751         }
21752         // else
21753         // {
21754             //?console.log("Error in ContainerServices : _workflowActionEventTracker - could not map published HUB object to WorkflowActionEvent");
21755         // }
21756 
21757     },
21758 
21759     /**
21760      * Callback function that is called when a reloadGadget event message is posted to the Hub.
21761      *
21762      * Grabs the id of the gadget we want to reload from the data and reload it!
21763      *
21764      * @param {String} topic
21765      *      which topic the event came on (unused)
21766      * @param {Object} data
21767      *      the data published with the event
21768      * @private
21769      */
21770     _masterReloader = function (topic, data) {
21771         var gadgetId = data.gadgetId;
21772         if (gadgetId) {
21773             _container.reloadGadget(gadgetId);
21774         }
21775     },
21776     
21777     /**
21778      * Pulls the gadget id from the url parameters
21779      * @return {String} id of the gadget
21780      * @private
21781      */
21782     _findMyGadgetId = function () {
21783         if (gadgets && gadgets.util && gadgets.util.getUrlParameters()) {
21784             return gadgets.util.getUrlParameters().mid;
21785         }
21786     };
21787 
21788     return {
21789         /**
21790          * @class
21791          * This class provides container-level services for gadget developers, exposing container events by
21792          * calling a set of exposed functions. Gadgets can utilize the container dialogs and 
21793          * event handling (add/remove).
21794          * @example
21795          *    containerServices = finesse.containerservices.ContainerServices.init();
21796          *    containerServices.addHandler(
21797          *      finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB, 
21798          *      function() {
21799          *          clientLogs.log("Gadget is now visible");  // log to Finesse logger
21800          *          // automatically adjust the height of the gadget to show the html
21801          *          gadgets.window.adjustHeight();
21802          *      });
21803          *    containerServices.makeActiveTabReq();
21804          *    
21805          * @constructs
21806          */
21807         _fakeConstuctor: function () {
21808             /* This is here so we can document init() as a method rather than as a constructor. */
21809         },
21810         
21811         /**
21812          * Initialize ContainerServices for use in gadget.
21813          * @param {Boolean} [master=false] Do not use this parameter from your gadget.
21814          * @returns ContainerServices instance.
21815          */
21816         init: function (master) {
21817             if (!_inited) {
21818                 _inited = true;
21819                 // Set shortcuts
21820                 _util = Utilities;
21821                 _authModes = _util.getAuthModes();
21822                 try {
21823                 	_commonDesktop = window.top.cd;
21824                 } catch(err) {
21825                     _logger.log("Error accessing common desktop: " + err.message);
21826                 }
21827                 
21828                 //init the hub only when it's available
21829                 if(gadgets.Hub) {
21830                     _hub = gadgets.Hub;
21831                 }
21832 
21833                 if(Topics) {
21834                     _topics = Topics;
21835                 }
21836 
21837                 if (master) {
21838                     _master = true;
21839                     _container = finesse.container.Container;
21840                     _publisher = new MasterPublisher();
21841 
21842                     // subscribe for reloading gadget events
21843                     // we only want the master ContainerServices handling these events
21844                     _hub.subscribe(_topics.RELOAD_GADGET, _topicCallback(_topics.RELOAD_GADGET));
21845                 } else {
21846                      // For SPOG like containers where parent.finesse is undefined.
21847                      if(parent.finesse){
21848                         _container = parent.finesse.container.Container;
21849                      }
21850                 }
21851             }
21852             
21853             this.makeActiveTabReq();
21854 
21855             if(finesse.modules && finesse.modules.ToastPopover){
21856                 finesse.ToastPopoverInstance = new finesse.modules.ToastPopover();
21857             }
21858 
21859             /* initialize popOverService */
21860             if(window.finesse.containerservices.PopoverService){
21861                 window.finesse.containerservices.PopoverService.init(this);   
21862             }
21863             
21864             //Return the CS object for object chaining.
21865             return this;
21866         },
21867 
21868         /**
21869          * Shows the jQuery UI Dialog with the specified parameters. The following are the
21870          * default parameters: <ul>
21871          *     <li> Title of "Cisco Finesse".</li>
21872          *     <li>Message of "A generic error has occured".</li>
21873          *     <li>The only button, "Ok", closes the dialog.</li>
21874          *     <li>Modal (blocks other dialogs).</li>
21875          *     <li>Not draggable.</li>
21876          *     <li>Fixed size.</li></ul>
21877          * @param {Object} options
21878          *  An object containing additional options for the dialog.
21879          * @param {String/Boolean} options.title
21880          *  Title to use. undefined defaults to "Cisco Finesse". false to hide
21881          * @param {Function} options.close
21882          *  A function to invoke when the dialog is closed.
21883          * @param {String} options.message
21884          *  The message to display in the dialog.
21885          *  Defaults to "A generic error has occurred."
21886          * @param {Boolean} options.isBlocking
21887          *  Flag indicating whether this dialog will block other dialogs from being shown (Modal).
21888          * @returns {jQuery} JQuery wrapped object of the dialog DOM element.
21889          * @see finesse.containerservices.ContainerServices#hideDialog
21890          */
21891         showDialog: function(options) {
21892             if ((_container.showDialog !== undefined) && (_container.showDialog !== this.showDialog)) {
21893                 return _container.showDialog(options);
21894             }
21895         },
21896         
21897         /**
21898          * Hides the jQuery UI Dialog.
21899          * @returns {jQuery} jQuery wrapped object of the dialog DOM element
21900          * @see finesse.containerservices.ContainerServices#showDialog
21901          */
21902         hideDialog: function() {
21903             if ((_container.hideDialog !== undefined) && (_container.hideDialog !== this.hideDialog)) {
21904                 return _container.hideDialog();
21905             }
21906         },
21907         /**
21908          * Shows the Certificate Banner with the specified parameters
21909          * @param  {Function} callback (optional)
21910          *  Callback to be called when user closes the banner
21911          * Id is fetched using window.finesse.containerservices.ContainerServices.getMyGadgetId()
21912          * @returns {String} id gadgetId or UniqueId if gadget is not present
21913          * 
21914          */
21915         showCertificateBanner: function(callback) {
21916             if ((_container.showCertificateBanner !== undefined) && (_container.showCertificateBanner !== this.showCertificateBanner)) {
21917             		var options = {};
21918             		/**
21919             		 * If getMyGadgetId is undefined , i.e. for components, a unique id will be returned,
21920             		 * which should be sent while calling hideCertificateBanner
21921             		 */ 
21922             		options.id = window.finesse.containerservices.ContainerServices.getMyGadgetId();
21923             		options.callback = callback;
21924                 return _container.showCertificateBanner(options);
21925             }
21926         },
21927         
21928         /**
21929          *  Requests for hiding the Certificate Banner
21930          *  Banner will only be hidden when all gadget's which called showCertificateBanner before
21931          *  have called hideCertificateBanner 
21932          *  @param {String} id -> unique id returned while calling showCertificateBanner
21933          */
21934         hideCertificateBanner: function(id) {
21935         		if(id === undefined && window.finesse.containerservices.ContainerServices.getMyGadgetId() === undefined) {
21936         			throw new Error('ID returned when showCertificateBanner was called need to be sent as params');
21937         		}
21938             if ((_container.hideCertificateBanner !== undefined) && (_container.hideCertificateBanner !== this.hideCertificateBanner)) {
21939                 return _container.hideCertificateBanner(id || window.finesse.containerservices.ContainerServices.getMyGadgetId());
21940             }
21941         },
21942 
21943         /**
21944          *  Reloads the current gadget. 
21945          *  For use from within a gadget only.
21946          */
21947         reloadMyGadget: function () {
21948             var topic, gadgetId, data;
21949 
21950             if (!_master) {
21951                 // first unsubscribe this gadget from all topics on the hub
21952                 for (topic in _notifiers) {
21953                     if (_notifiers.hasOwnProperty(topic)) {
21954                         _unsubscribe(topic);
21955                         delete _notifiers[topic];
21956                     }
21957                 }
21958 
21959                 // send an asynch request to the hub to tell the master container
21960                 // services that we want to refresh this gadget
21961                 gadgetId = _findMyGadgetId();
21962                 data = {
21963                     type: "ReloadGadgetReq",
21964                     data: {gadgetId: gadgetId},
21965                     invokeID: (new Date()).getTime()          
21966                 };
21967                 _hub.publish(_topics.REQUESTS, data);
21968             }            
21969         },
21970 
21971         /**
21972          * Updates the url for this gadget and then reload it.
21973          * 
21974          * This allows the gadget to be reloaded from a different location
21975          * than what is uploaded to the current server. For example, this
21976          * would be useful for 3rd party gadgets to implement their own failover
21977          * mechanisms.
21978          *
21979          * For use from within a gadget only.
21980          *
21981          * @param {String} url
21982          *      url from which to reload gadget
21983          */
21984         reloadMyGadgetFromUrl: function (url) {
21985             if (!_master) {
21986                 var gadgetId = _findMyGadgetId();
21987 
21988                 // update the url in the container
21989                 _container.modifyGadgetUrl(gadgetId, url);
21990 
21991                 // reload it
21992                 this.reloadMyGadget();
21993             }
21994         },
21995         
21996         /**
21997          * Adds a handler for one of the supported topics provided by ContainerServices.  The callbacks provided
21998          * will be invoked when that topic is notified.  
21999          * @param {String} topic
22000          *  The Hub topic to which we are listening.
22001          * @param {Function} callback
22002          *  The callback function to invoke.
22003          * @see finesse.containerservices.ContainerServices.Topics
22004          * @see finesse.containerservices.ContainerServices#removeHandler
22005          */
22006         addHandler: function (topic, callback) {
22007             _isInited();
22008             var notifier = null;
22009             
22010             try {    
22011                 // For backwards compatibility...
22012                 if (topic === "tabVisible") {
22013                     if (window.console && typeof window.console.log === "function") {
22014                         window.console.log("WARNING - Using tabVisible as topic.  This is deprecated.  Use finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB now!");
22015                     }
22016                     
22017                     topic = finesse.containerservices.ContainerServices.Topics.ACTIVE_TAB;
22018                 }
22019                 
22020                 // Add the callback to the notifier.
22021                 _util.validateHandler(callback);
22022             
22023                 notifier = _getNotifierReference(topic);
22024             
22025                 notifier.addListener(callback);
22026             
22027                 // Subscribe to the topic. _subscribe ensures that a topic is only subscribed to once,
22028                 // so attempt to subscribe each time a handler is added. This ensures that a topic is subscribed
22029                 // to only when necessary.
22030                 _subscribe(topic, _topicCallback(topic));
22031             
22032             } catch (err) {
22033                 throw new Error("addHandler(): " + err);
22034             }
22035         }, 
22036         
22037         /**
22038          * Removes a previously-added handler for one of the supported topics.
22039          * @param {String} topic
22040          *  The Hub topic from which we are removing the callback.
22041          * @param {Function} callback
22042          *  The name of the callback function to remove.
22043          * @see finesse.containerservices.ContainerServices.Topics
22044          * @see finesse.containerservices.ContainerServices#addHandler
22045          */
22046         removeHandler: function(topic, callback) {
22047             var notifier = null;
22048             
22049             try {
22050                 _util.validateHandler(callback);
22051     
22052                 notifier = _getNotifierReference(topic);
22053     
22054                 notifier.removeListener(callback);
22055             } catch (err) {
22056                 throw new Error("removeHandler(): " + err);
22057             }
22058         },
22059         
22060         /**
22061          * Wrapper API for publishing data on the Openajax hub
22062          * @param {String} topic
22063          *  The Hub topic to which we are publishing.
22064          * @param {Object} data
22065          *  The data to be published on the hub.
22066          */
22067         publish : function(topic , data){
22068             if(_hub){
22069                 _hub.publish(topic, data);
22070             } else {
22071                 throw new Error("Hub is not defined.");
22072             }
22073         },
22074 
22075         /**
22076          * Returns the visibility of current gadget.  Note that this 
22077          * will not be set until after the initialization of the gadget.
22078          * @return {Boolean} The visibility of current gadget.
22079          */
22080         tabVisible: function(){
22081             return _visible;
22082         },
22083         
22084         /**
22085          * Make a request to check the current tab.  The 
22086          * activeTab event will be invoked if on the active tab.  This
22087          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
22088          * to ensure the gadget gets properly initialized.
22089          */
22090         makeActiveTabReq : function () {
22091             if(_hub){
22092                 var data = {
22093                     type: "ActiveTabReq",
22094                     data: {},
22095                     invokeID: (new Date()).getTime()          
22096                 };
22097                 _hub.publish(_topics.REQUESTS, data);
22098             } else {
22099                 throw new Error("Hub is not defined.");
22100             }
22101             
22102         },
22103 
22104         /**
22105          * Make a request to set a particular tab active. This
22106          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
22107          * to ensure the gadget gets properly initialized.
22108          * @param {String} tabId
22109          *    The tabId (not the label text) of the tab to make active.  If the id is invalid, no action will occur.
22110          */
22111         activateTab : function (tabId) {
22112             _activateTab(tabId);
22113         },
22114         
22115         /**
22116          * Make a request to set this container's tab active. This
22117          * method should be called after {@link finesse.containerservices.ContainerServices#addHandler}
22118          * to ensure the gadget gets properly initialized.
22119          */
22120         activateMyTab : function () {
22121             _activateTab( _getMyTab() );
22122         },
22123         
22124         /**
22125          * Get the tabId of my container/gadget.
22126          * @returns {String} tabid : The tabid of this container/gadget.
22127          */
22128         getMyTabId : function () {
22129 		return _getMyTab();
22130 	},
22131 
22132         /**
22133          * Gets the id of the gadget.
22134          * @returns {number} the id of the gadget
22135          */
22136         getMyGadgetId : function () {
22137             return _findMyGadgetId();
22138         },
22139 
22140         //BEGIN TEST CODE//
22141         /**
22142          * Test code added to expose private functions that are used by unit test
22143          * framework. This section of code is removed during the build process
22144          * before packaging production code. The [begin|end]TestSection are used
22145          * by the build to identify the section to strip.
22146          * @ignore
22147          */
22148         beginTestSection : 0,
22149 
22150         /**
22151          * @ignore
22152          */
22153         getTestObject: function () {
22154             //Load mock dependencies.
22155             var _mock = new MockControl();
22156             _util = _mock.createMock(Utilities);
22157             _hub = _mock.createMock(gadgets.Hub);
22158             _inited = true;
22159             return {
22160                 //Expose mock dependencies
22161                 mock: _mock,
22162                 hub: _hub,
22163                 util: _util,
22164                 addHandler: this.addHandler,
22165                 removeHandler: this.removeHandler
22166             };
22167         },
22168 
22169         /**
22170          * @ignore
22171          */
22172        endTestSection: 0
22173         //END TEST CODE//
22174     };
22175     }());
22176     
22177     ContainerServices.Topics = /** @lends finesse.containerservices.ContainerServices.Topics.prototype */ {
22178         /**
22179          * Topic for subscribing to be notified when the active tab changes.
22180          * The provided callback will be invoked when the tab that the gadget 
22181          * that subscribes with this becomes active.  To ensure code is called
22182          * when the gadget is already on the active tab use the 
22183          * {@link finesse.containerservices.ContainerServices#makeActiveTabReq}
22184          * method.
22185          */
22186         ACTIVE_TAB: "finesse.containerservices.activeTab",
22187 
22188         /**
22189          * Topic for WorkflowAction events traffic.
22190          * The provided callback will be invoked when a WorkflowAction needs
22191          * to be handled.  The callback will be passed a {@link finesse.containerservices.WorkflowActionEvent}
22192          * that can be used to interrogate the WorkflowAction and determine to use or not.
22193          */
22194         WORKFLOW_ACTION_EVENT: "finesse.containerservices.workflowActionEvent",
22195         
22196         /**
22197          * Topic for Timer Tick event.
22198          * The provided callback will be invoked when this event is fired.
22199          * The callback will be passed a {@link finesse.containerservices.TimerTickEvent}.
22200          */
22201         TIMER_TICK_EVENT : "finesse.containerservices.timerTickEvent",
22202 
22203         /**
22204          * Topic for Non Voice Gadgets to communicate with Finext Container
22205          * Finext container will handle this event
22206          */
22207         FINEXT_NON_VOICE_GADGET_EVENT : "finext.nv",
22208 
22209         /**
22210          * Topic for listening to the active call event. Can a trigger a callback
22211          * when the agent voice state changes from READY/NOT_READY to any other
22212          * non-callable state and vice versa.
22213          */
22214         ACTIVE_CALL_STATUS_EVENT : "finesse.containerservices.activeCallStatusEvent",
22215 
22216         /**
22217          * Topic for Gadgets to communicate with Finext Popover Container.
22218          */
22219         FINEXT_POPOVER_EVENT : "finext.popover",
22220 
22221         /**
22222          * Topic for Reload Gadget events traffic.
22223          * Only the master ContainerServices instance will handle this event.
22224          */
22225         RELOAD_GADGET_EVENT: "finesse.containerservices.reloadGadget",
22226         
22227         /**
22228          * Topic for listening to gadget view changed events.
22229          * The provided callback will be invoked when a gadget changes view.
22230          * The callback will be passed a {@link finesse.containerservices.GadgetViewChangedEvent}.
22231          */
22232         GADGET_VIEW_CHANGED_EVENT: "finesse.containerservices.gadgetViewChangedEvent",
22233 
22234         /**
22235          * Topic for listening to max available height changed events.
22236          * The provided callback will be invoked when the maximum height available to a maximized gadget changes.
22237          * This event is only meant for maximized gadgets and will not be published unless a maximized gadget exists.
22238          * The callback will be passed a {@link finesse.containerservices.MaxAvailableHeightChangedEvent}.
22239          */
22240         MAX_AVAILABLE_HEIGHT_CHANGED_EVENT: "finesse.containerservices.maxAvailableHeightChangedEvent",
22241         
22242         /**
22243          * @class This is the set of Topics used for subscribing for events from ContainerServices.
22244          * Use {@link finesse.containerservices.ContainerServices#addHandler} to subscribe to the topic.
22245          * 
22246          * @constructs
22247          */
22248         _fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
22249     };
22250     
22251     window.finesse = window.finesse || {};
22252     window.finesse.containerservices = window.finesse.containerservices || {};
22253     window.finesse.containerservices.ContainerServices = ContainerServices;
22254     
22255     return ContainerServices;
22256  });
22257 
22258 /**
22259  * FinesseToaster is a utility class to show toaster notification in Finesse.
22260  * FinesseToaster leverages HTML5 Notification API to display Toaster
22261  * Notification.
22262  * 
22263  */
22264 
22265 define('containerservices/FinesseToaster',[],function() {
22266 
22267 	var FinesseToaster = (function() {
22268 		/** @lends finesse.containerservices.FinesseToaster.prototype */
22269 
22270 		var
22271 
22272 		/** How long the toaster will be displayed by default. Default timeout is 8 seconds */
22273 		AUTO_CLOSE_TIME = 8000,
22274 
22275 		/** PERMISSION_GRANTED constant for granted string */
22276 		PERMISSION_GRANTED = 'granted',
22277 
22278 		/** PERMISSION_DEFAULT constant for default string */
22279 		PERMISSION_DEFAULT = 'default',
22280 
22281 		/** PERMISSION_DENIED constant for denied string */
22282 		PERMISSION_DENIED = 'denied',
22283 
22284 		/** ICON_PATH constant for holding path icon images */
22285 		ICON_PATH = '/desktop/theme/finesse/images/modules/',
22286 
22287 		/**
22288 		 * Shortcut reference to finesse.cslogger.ClientLogger singleton This
22289 		 * will be set by init(), it should already be initialized by
22290 		 * PageServices
22291 		 *
22292 		 * @private
22293 		 */
22294 		_logger,
22295 		
22296 		/**
22297 		 * Boolean variable to determine if finesse toaster is enabled 
22298 		 * @private
22299 		 */
22300 		_isToasterDisabled = false,
22301 		
22302 		/**
22303 		 * Boolean variable to determine if finesse toaster is initialized 
22304 		 * @private
22305 		 */
22306 		_isToasterInitialized,
22307 		
22308 		/**
22309 		 * this function check of provided parameter is a javascript function.
22310 		 * 
22311 		 * @private
22312 		 */
22313 		isFunction = function(param) {
22314 			if (typeof param === "function") {
22315 				return true;
22316 			}
22317 			return false;
22318 		},
22319 
22320 		/**
22321 		 * _createNotification creates Notification instance
22322 		 *
22323 		 * @param {String}
22324 		 *            title title string should be displayed in the Toaster
22325 		 * @param {Object}
22326 		 *            options JSON object for notification options.
22327 		 */
22328 		_createNotification = function(title, options) {
22329 			var notification = new window.Notification(title, options);
22330 			return notification;
22331 		},
22332 
22333 		/**
22334 		 * _setAutoClose set the auto close time for toaster, it checks if
22335 		 * client code passed custom time out for the toaster, otherwise it set
22336 		 * the AUTO_CLOSE_TIME
22337 		 *
22338 		 * @param {Object}
22339 		 *            notification window.Notification that creates Html5
22340 		 *            Notification.
22341 		 * @param {String}
22342 		 *            autoClose autoClose time of the Toaster
22343 		 * @return toasterTimeout Toaster Timeout
22344 		 */
22345 		_setAutoClose = function(notification, autoClose) {
22346 
22347 			// check if custom close time passed other wise set
22348 			// DEFAULT_AUTO_CLOSE
22349 			var autoCloseTime = (autoClose && !isNaN(autoClose)) ? autoClose
22350 					: AUTO_CLOSE_TIME,
22351 			// set the time out for notification toaster
22352 			toasterTimer = setTimeout(function() {
22353 				notification.close();
22354 			}, autoCloseTime);
22355 
22356 			return toasterTimer;
22357 
22358 		},
22359 
22360 		/** This method will request permission to display Toaster. */
22361 		_requestPermission = function() {
22362 			// If they are not denied (i.e. default)
22363 			if (window.Notification
22364 					&& window.Notification.permission !== PERMISSION_DENIED) {
22365 				// Request permission
22366 				window.Notification
22367 						.requestPermission(function(status) {
22368 
22369 							// Change based on user's decision
22370 							if (window.Notification.permission !== status) {
22371 								window.Notification.permission = status;
22372 							}
22373 							_logger
22374 									.log("FinesseToaster.requestPermission(): request permission status "
22375 											+ status);
22376 
22377 						});
22378 
22379 			} else {
22380 				_logger
22381 						.log("FinesseToaster.requestPermission(): Notification not supported or permission denied.");
22382 			}
22383 
22384 		},
22385 
22386 		/**
22387 		 * This method will add onclick and onerror listener to Notification.
22388 		 * on click of toaster the gadget which originally had focus may loose
22389 		 * the focus. To get the back the focus on any element inside the gadget
22390 		 * use the on click callback handler.
22391 		 * 
22392 		 * @param {Object}
22393 		 *            notification window.Notification that creates Html5
22394 		 *            Notification.
22395 		 * @param {Object}
22396 		 *            options JSON object for notification options.
22397 		 */
22398 		_addToasterListeners = function(notification, options, toasterTimer) {
22399 			// this is onlcik handler of toaster. this handler will be invoked
22400 			// on click of toaster
22401 			notification.onclick = function() {
22402 				// in case of manually closed toaster, stop the notification
22403 				// auto close method to be invoked
22404 				clearTimeout(toasterTimer);
22405 				// This will maximize/activate chrome browser on click of
22406 				// toaster. this handling required only in case of Chrome
22407 				if (window.chrome) {
22408 					parent.focus();
22409 				}
22410 
22411 				if (options && options.onclick) {
22412 					if (isFunction(options.onclick)) {
22413 						options.onclick();
22414 					} else {
22415 						throw new Error("onclick callback must be a function");
22416 					}
22417 				}
22418 
22419 				//close toaster upon click
22420 				this.close();
22421 
22422 			};
22423 
22424 			// this is onerror handler of toaster, if there is any error while
22425 			// loading toaster this hadnler will be invoked
22426 			notification.onerror = function() {
22427 				if (options && options.onerror) {
22428 					if (isFunction(options.onerror)) {
22429 						options.onerror();
22430 					} else {
22431 						throw new Error("onerror callback must be a function");
22432 					}
22433 				}
22434 			};
22435 		};
22436 		
22437 		return {
22438 
22439 			/**
22440 			 * @class
22441 			 *  FinesseToaster is a utility class to show toaster
22442 			 *        notification in Finesse. FinesseToaster leverages <a
22443 			 *        href="https://www.w3.org/TR/notifications/">HTML5
22444 			 *        Notification</a> API to display Toaster Notification. 
22445 			 *       <p> <a
22446 			 *        href="https://developer.mozilla.org/en/docs/Web/API/notification#Browser_compatibility">For
22447 			 *        HTML5 Notification API and browser compatibility, please click
22448 			 *        here.</a></p>
22449 			 * 
22450 			 * @constructs
22451 			 */
22452 			_fakeConstuctor : function() {
22453 
22454 			},
22455 			/**
22456 			 * TOASTER_DEFAULT_ICONS constants has list of predefined icons (e.g INCOMING_CALL_ICON).
22457 			 * <p><b>Constant list</b></p>
22458 			 * <ul>
22459 			 * <li>TOASTER_DEFAULT_ICONS.INCOMING_CALL_ICON</li>
22460 			 * </ul>
22461 			 */
22462 			TOASTER_DEFAULT_ICONS : {
22463 				INCOMING_CALL_ICON : ICON_PATH + "incoming_call.png",
22464 				INCOMING_CHAT_ICON : ICON_PATH + "incoming_chat.png",
22465 				INCOMING_TEAM_MESSAGE : ICON_PATH + "incoming_team_message.png"
22466 			},
22467 
22468 			/**
22469 			 * <b>showToaster </b>: shows Toaster Notification.
22470 			 *
22471 			 * @param {String}
22472 			 *            <b>title</b> : title string should be displayed in the Toaster
22473 			 * @param {Object}
22474 			 *            options is JSON object for notification options. 
22475 			 *            <ul>
22476 			 *            <li><b>options</b> = { </li>
22477 			 *            <li><b>body</b> : The body string of the notification as
22478 			 *            specified in the options parameter of the constructor.</li>
22479 			 *            <li><b>icon</b>: The URL of the image used as an icon of the
22480 			 *            notification as specified in the options parameter of
22481 			 *            the constructor.</li>
22482 			 *            <li><b>autoClose</b> : custom auto close time of the toaster</li>
22483 			 *            <li><b>showWhenVisible</b> : 'true' toaster shows up even when page is 
22484 			 *            visible,'false' toaster  shows up only when page is invisible </li>
22485 			 *           <li> }</li>
22486 			 *            </ul>
22487 			 *
22488 			 */
22489 			showToaster : function(title, options) {
22490 				
22491 				if(!_isToasterInitialized){
22492 					throw new Error("FinesseToaster.showToaster() : Finesse toaster is not initialized");
22493 				}
22494 				
22495 				if(_isToasterDisabled){
22496 					_logger.log("FinesseToaster.showToaster() : FinesseToaster is disabled");
22497 					return;
22498 				}
22499 
22500 				var notification, toasterTimer;
22501 
22502 				// If notifications are granted show the notification
22503 				if (window.Notification
22504 						&& window.Notification.permission === PERMISSION_GRANTED) {
22505 
22506 					// document.hasFocus() used over document.hidden to keep the consistent behavior across mozilla/chrome
22507 					if (document.hasFocus() === false
22508 							|| options.showWhenVisible) {
22509 						if (_logger && AUTO_CLOSE_TIME > -1) {
22510 							notification = _createNotification(title, options);
22511 
22512 							// set the auto close time out of the toaster
22513 							toasterTimer = _setAutoClose(notification,
22514 									options.autoClose);
22515 
22516 							// and Toaster Event listeners. eg. onclick , onerror.
22517 							_addToasterListeners(notification, options,
22518 									toasterTimer);
22519 						} 
22520 					} else {
22521 						_logger
22522 								.log("FinesseToaster supressed : Page is visible and  FineeseToaster.options.showWhenVisible is false");
22523 					}
22524 
22525 				}
22526 
22527 				return notification;
22528 			},
22529 
22530 			/**
22531 			 * initialize FininseToaster and inject dependencies. this method
22532 			 * will also request permission in browser from user to display
22533 			 * Toaster Notification.
22534 			 *
22535 			 *@param {Object}
22536 			 *          Could be finesse.container.Config or finesse.gadget.Config based on where it is getting initialized from.
22537 			 *            
22538 			 * @param {Object}
22539 			 *            finesse.cslogger.ClientLogger
22540 			 * @return finesse.containerservices.FinesseToaster
22541 			 */
22542 			init : function(config , logger) {
22543 				
22544 				_isToasterInitialized = true;
22545 				
22546 				// This is for injecting mocked logger.
22547 				if (logger) {
22548 					_logger = logger;
22549 				} else {
22550 					_logger = finesse.cslogger.ClientLogger;
22551 				}
22552 				
22553 				//set default toaster notification timeout
22554 				if (config && config.toasterNotificationTimeout !== undefined) {
22555 					AUTO_CLOSE_TIME = Number(config.toasterNotificationTimeout) * 1000;
22556 					
22557 					if(AUTO_CLOSE_TIME === 0){
22558 						//Finesse toaster has been disabled
22559 						_isToasterDisabled = true;
22560 					}
22561 				} 
22562 
22563 				// Request permission
22564 				_requestPermission();
22565 				return finesse.containerservices.FinesseToaster;
22566 			}
22567 		};
22568 
22569 	}());
22570 
22571 	window.finesse = window.finesse || {};
22572 	window.finesse.containerservices = window.finesse.containerservices || {};
22573 	window.finesse.containerservices.FinesseToaster = FinesseToaster;
22574 
22575 	return FinesseToaster;
22576 });
22577 
22578 /**
22579  * This "interface" is just a way to easily jsdoc the Object callback handlers.
22580  *
22581  * @requires finesse.clientservices.ClientServices
22582  * @requires Class
22583  */
22584 /** @private */
22585 define('interfaces/RestObjectHandlers',[
22586     "FinesseBase",
22587      "utilities/Utilities",
22588      "restservices/Notifier",
22589      "clientservices/ClientServices",
22590      "clientservices/Topics"
22591 ],
22592 function () {
22593 
22594     var RestObjectHandlers = ( function () { /** @lends finesse.interfaces.RestObjectHandlers.prototype */
22595         
22596         return {
22597 
22598             /**
22599              * @class
22600              * This "interface" defines REST Object callback handlers, passed as an argument to
22601              * Object getter methods in cases where the Object is going to be created.
22602              * 
22603              * @param {Object} [handlers]
22604              *     An object containing callback handlers for instantiation and runtime
22605              *     Callback to invoke upon successful instantiation, passes in REST object.
22606              * @param {Function} [handlers.onLoad(this)]
22607              *     Callback to invoke upon loading the data for the first time.
22608              * @param {Function} [handlers.onChange(this)]
22609              *     Callback to invoke upon successful update object (PUT)
22610              * @param {Function} [handlers.onAdd(this)]
22611              *     Callback to invoke upon successful update to add object (POST)
22612              * @param {Function} [handlers.onDelete(this)]
22613              *     Callback to invoke upon successful update to delete object (DELETE)
22614              * @param {Function} [handlers.onError(rsp)]
22615              *     Callback to invoke on update error (refresh or event)
22616              *     as passed by finesse.restservices.RestBase.restRequest()<br>
22617              *     {<br>
22618              *         status: {Number} The HTTP status code returned<br>
22619              *         content: {String} Raw string of response<br>
22620              *         object: {Object} Parsed object of response<br>
22621              *         error: {Object} Wrapped exception that was caught<br>
22622              *         error.errorType: {String} Type of error that was caught<br>
22623              *         error.errorMessage: {String} Message associated with error<br>
22624              *     }<br>
22625              *     <br>
22626              * Note that RestCollections have two additional callback handlers:<br>
22627              * <br>
22628              * @param {Function} [handlers.onCollectionAdd(this)]: when an object is added to this collection
22629              * @param {Function} [handlers.onCollectionDelete(this)]: when an object is removed from this collection
22630 
22631              * @constructs
22632              */
22633             _fakeConstuctor: function () {
22634                 /* This is here to enable jsdoc to document this as a class. */
22635             }
22636         };
22637     }());
22638 
22639 window.finesse = window.finesse || {};
22640 window.finesse.interfaces = window.finesse.interfaces || {};
22641 window.finesse.interfaces.RestObjectHandlers = RestObjectHandlers;
22642 
22643 return RestObjectHandlers;
22644 
22645 });
22646 
22647 
22648 /**
22649  * This "interface" is just a way to easily jsdoc the REST request handlers.
22650  *
22651  * @requires finesse.clientservices.ClientServices
22652  * @requires Class
22653  */
22654 /** @private */
22655 define('interfaces/RequestHandlers',[
22656     "FinesseBase",
22657      "utilities/Utilities",
22658      "restservices/Notifier",
22659      "clientservices/ClientServices",
22660      "clientservices/Topics"
22661 ],
22662 function () {
22663 
22664     var RequestHandlers = ( function () { /** @lends finesse.interfaces.RequestHandlers.prototype */
22665         
22666         return {
22667 
22668             /**
22669              * @class
22670              * This "interface" defines REST Object callback handlers, passed as an argument to
22671              * Object getter methods in cases where the Object is going to be created.
22672              * 
22673              * @param {Object} handlers
22674              *     An object containing the following (optional) handlers for the request:<ul>
22675              *         <li><b>success(rsp):</b> A callback function for a successful request to be invoked with the following
22676              *         response object as its only parameter:<ul>
22677              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
22678              *             <li><b>content:</b> {String} Raw string of response</li>
22679              *             <li><b>object:</b> {Object} Parsed object of response</li></ul>
22680              *         <li><b>error(rsp):</b> An error callback function for an unsuccessful request to be invoked with the
22681              *         error response object as its only parameter:<ul>
22682              *             <li><b>status:</b> {Number} The HTTP status code returned</li>
22683              *             <li><b>content:</b> {String} Raw string of response</li>
22684              *             <li><b>object:</b> {Object} Parsed object of response (HTTP errors)</li>
22685              *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
22686              *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
22687              *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
22688              *             </ul></li>
22689              *         </ul>
22690 
22691              * @constructs 
22692              */
22693             _fakeConstuctor: function () {
22694                 /* This is here to enable jsdoc to document this as a class. */
22695             }
22696         };
22697     }());
22698 
22699 window.finesse = window.finesse || {};
22700 window.finesse.interfaces = window.finesse.interfaces || {};
22701 window.finesse.interfaces.RequestHandlers = RequestHandlers;
22702 
22703 finesse = finesse || {};
22704 /** @namespace These interfaces are just a convenience for documenting common parameter structures. */
22705 finesse.interfaces = finesse.interfaces || {};
22706 
22707 return RequestHandlers;
22708 
22709 });
22710 
22711 
22712 
22713 define('gadget/Config',[
22714 	"utilities/Utilities"
22715 ], function (Utilities) {  
22716 	var Config = (function () { /** @lends finesse.gadget.Config.prototype */
22717 		
22718 		if (gadgets && gadgets.Prefs) {
22719 		
22720 			var _prefs = new gadgets.Prefs();
22721 		
22722 			return {
22723 				/**
22724 				 * The base64 encoded "id:password" string used for authentication.
22725 				 * In case of SPOG container, the credentials will not be there in browser session storage so it will be taken from the gadget prefs.
22726 				 */
22727 				authorization: Utilities.getUserAuthString() || _prefs.getString("authorization"),
22728 				
22729 				/**
22730 				 * The  auth token string used for authentication in SSO deployments.
22731 				 */
22732 				authToken: Utilities.getToken(),
22733 				
22734 				/**
22735 				 * The country code of the client (derived from locale).
22736 				 */
22737 				country: _prefs.getString("country"),
22738 				
22739 				/**
22740 				 * The language code of the client (derived from locale).
22741 				 */
22742 				language: _prefs.getString("language"),
22743 				
22744 				/**
22745 				 * The locale of the client.
22746 				 */
22747 				locale: _prefs.getString("locale"),
22748 				
22749 				/**
22750 				 * The Finesse server IP/host as reachable from the browser.
22751 				 */
22752 				host: _prefs.getString("host"),
22753 				
22754 				/**
22755 				 * The Finesse server host's port reachable from the browser.
22756 				 */
22757 				hostPort: _prefs.getString("hostPort"),
22758 				
22759 				/**
22760 				 * The extension of the user.
22761 				 */
22762 				extension: _prefs.getString("extension"),
22763 				
22764 				/**
22765 				 * One of the work modes found in {@link finesse.restservices.User.WorkMode}, or something false (undefined) for a normal login.
22766 				 */
22767 				mobileAgentMode: _prefs.getString("mobileAgentMode"),
22768 				
22769 				/**
22770 				 * The dial number to use for mobile agent, or something false (undefined) for a normal login.
22771 				 */
22772 				mobileAgentDialNumber: _prefs.getString("mobileAgentDialNumber"),
22773 				
22774 				/**
22775 				 * The domain of the XMPP server.
22776 				 */
22777 				xmppDomain: _prefs.getString("xmppDomain"),
22778 				
22779 				/**
22780 				 * The pub sub domain where the pub sub service is running.
22781 				 */
22782 				pubsubDomain: _prefs.getString("pubsubDomain"),
22783 				
22784 				/**
22785 				 * The Finesse API IP/host as reachable from the gadget container.
22786 				 */
22787 				restHost: _prefs.getString("restHost"),
22788 				
22789 				/**
22790 				 * The type of HTTP protocol (http or https).
22791 				 */
22792 				scheme: _prefs.getString("scheme"),
22793 				
22794 				/**
22795 				 * The localhost fully qualified domain name.
22796 				 */
22797 				localhostFQDN: _prefs.getString("localhostFQDN"),
22798 				
22799 				/**
22800 				 * The localhost port.
22801 				 */
22802 				localhostPort: _prefs.getString("localhostPort"),
22803 				
22804 				/**
22805 				 * The id of the team the user belongs to.
22806 				 */
22807 				teamId: _prefs.getString("teamId"),
22808 				
22809 				/**
22810 				 * The name of the team the user belongs to.
22811 				 */
22812 				teamName: _prefs.getString("teamName"),
22813 				
22814 				/**
22815 				 * The drift time between the client and the server in milliseconds.
22816 				 */
22817 				clientDriftInMillis: _prefs.getInt("clientDriftInMillis"),
22818 				
22819 				/**
22820 				 * The client compatibility mode configuration (true if it is or false otherwise).
22821 				 */
22822 				compatibilityMode: _prefs.getString("compatibilityMode"),
22823 				
22824 				/**
22825 				 * The peripheral Id that Finesse is connected to.
22826 				 */
22827 				peripheralId: _prefs.getString("peripheralId"),
22828 				
22829 				/**
22830 				 * The auth mode of the finesse deployment.
22831 				 */
22832 				systemAuthMode: _prefs.getString("systemAuthMode"),
22833 				
22834 				
22835 				/**
22836 				 * The time for which fineese toaster stay on the browser.
22837 				 */
22838 				toasterNotificationTimeout: _prefs.getString("toasterNotificationTimeout"),
22839 				
22840 				/**
22841 				* @class
22842 				* The Config object for gadgets within the Finesse desktop container which
22843 				* contains configuration data provided by the container page.
22844 				* @constructs
22845 				*/
22846 				_fakeConstructor : function () {} // For JS Doc to work need a constructor so that the lends/constructs build the doc properly
22847 				
22848 			};
22849 		} else {
22850 			return {};
22851 		}
22852 	}());
22853 	
22854 	/** Assign to container and gadget namespace to have config available in both  */
22855 	window.finesse = window.finesse || {};
22856 	window.finesse.container = window.finesse.container || {};
22857 	window.finesse.container.Config = window.finesse.container.Config || Config;
22858 
22859 	window.finesse.gadget = window.finesse.gadget || {};
22860 	window.finesse.gadget.Config = Config;
22861 
22862 	return Config;
22863 });
22864 /**
22865  * Digital Channel uses a JSON payload for communication with DigitalChannelManager.
22866  * That payload has to conform to this schema.
22867  * This schema has been defined as per http://json-schema.org/
22868  *
22869  * @see utilities.JsonValidator
22870  */
22871 /** The following comment is to prevent jslint errors about using variables before they are defined. */
22872 /*global window:true, define:true*/
22873 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
22874 define('digital/ChannelSchema',['require','exports','module'],function (require, exports, module) {
22875 
22876     var ChannelSchema = (function () {  /** @lends finesse.digital.ChannelSchema.prototype */
22877 
22878         var _menuConfigSchema = {
22879             "$schema": "http://json-schema.org/draft-04/schema#",
22880             "type": "object",
22881             "properties": {
22882                 "label": {
22883                     "type": "string"
22884                 },
22885                 "menuItems": {
22886                     "type": "array",
22887                     "uniqueItems": true,
22888                     "items": [{
22889                         "type": "object",
22890                         "properties": {
22891                             "id": {
22892                                 "type": "string"
22893                             },
22894                             "label": {
22895                                 "type": "string"
22896                             },
22897                             "iconColor": {
22898                                 "type": "string",
22899                                 "enum": ["available", "unavailable", "busy"]
22900                             }
22901                         },
22902                         "required": ["id", "label", "iconColor"],
22903                         "additionalProperties": false
22904                     }]
22905                 }
22906             },
22907             "required": ["label", "menuItems"],
22908             "additionalProperties": false
22909         },
22910 
22911             _channelConfigSchema = {
22912                 "$schema": "http://json-schema.org/draft-04/schema#",
22913                 "type": "object",
22914                 "properties": {
22915                     "actionTimeoutInSec": {
22916                         "type": "integer",
22917                         "minimum": 1,
22918                         "maximum": 30
22919                     },
22920                     "icons": {
22921                         "type": "array",
22922                         "minItems": 1,
22923                         "items": [
22924                             {
22925                                 "type": "object",
22926                                 "properties": {
22927                                     "type": {
22928                                         "type": "string",
22929                                         "enum": ["collab-icon", "url"]
22930                                     },
22931                                     "value": {
22932                                         "type": "string"
22933                                     }
22934                                 },
22935                                 "required": [
22936                                     "type",
22937                                     "value"
22938                                 ],
22939                                 "additionalProperties": false
22940                             }
22941                         ]
22942                     }
22943                 },
22944                 "required": [
22945                     "actionTimeoutInSec",
22946                     "icons"
22947                 ],
22948                 "additionalProperties": false
22949             },
22950 
22951             _channelStateSchema = {
22952                 "$schema": "http://json-schema.org/draft-04/schema#",
22953                 "type": "object",
22954                 "properties": {
22955                     "label": {
22956                         "type": "string"
22957                     },
22958                     "currentState": {
22959                         "type": "string"
22960                     },
22961                     "iconColor": {
22962                         "type": "string"
22963                     },
22964                     "enable": {
22965                         "type": "boolean"
22966                     },
22967                     "logoutDisabled": {
22968                         "type": "boolean"
22969                     },
22970                     "logoutDisabledText": {
22971                         "type": "string"
22972                     },
22973                     "iconBadge": {
22974                         "type": "string",
22975                         "enum": [
22976                             "error",
22977                             "info",
22978                             "warning",
22979                             "none"
22980                         ]
22981                     },
22982                     "hoverText": {
22983                         "type": "string"
22984                     }
22985                 },
22986                 "required": [
22987                     "label",
22988                     "currentState",
22989                     "iconColor",
22990                     "enable",
22991                     "logoutDisabled",
22992                     "iconBadge"
22993                 ],
22994                 "additionalProperties": false
22995             };
22996 
22997         return {
22998 
22999             /**
23000              * @class
23001 			 * <b>F</b>i<b>n</b>esse digital <b>c</b>hannel state control (referred to as FNC elsewhere in this document) 
23002              * is a programmable desktop component that was introduced in Finesse 12.0.
23003              * This API provides the schema that is used in {@link finesse.digital.ChannelService} for various channel operations.
23004              * 
23005              * This schema has been defined as per http://json-schema.org/
23006              * <style>
23007              *   .schemaTable tr:nth-child(even) { background-color:#EEEEEE; }
23008              *   .schemaTable th { background-color: #999999; }
23009              *   .schemaTable th, td { border: none; }
23010              *   .pad30 {padding-left: 30px;}
23011              *   .inset {
23012              *          padding: 5px;
23013              *          border-style: inset; 
23014              *          background-color: #DDDDDD; 
23015              *          width: auto; 
23016              *          text-shadow: 2px 2px 3px rgba(255,255,255,0.5); 
23017              *          font: 12px arial, sans-serif; 
23018              *          color:rebeccapurple;
23019              *    }
23020              * 
23021              * </style>
23022              * 
23023              * @example
23024              * <h3 id='cdIcons'>Cisco Common Desktop Stock Icon names with image</h3>
23025              *
23026              * The channel configuration schema has options to take 
23027              * Cisco Common Desktop icon (CD-icon) name as value.
23028              * 
23029              * To get to know the list of CD-UI icon names and its visual design,
23030              * paste the below JavaScript code in javascript editor part of your 
23031              * browser developer console after Finesse login. This script will clear off the 
23032              * Finesse web-page and will display icon-name and its rendering in a HTML table.
23033              * To get back to the page, just refresh the browser.
23034              * Note: You can also set this up in a gadget for reference.
23035              *  <pre class='inset'>
23036 var showIcons = function () {
23037     $('body').html('');
23038 
23039     $('body').append("<table border='1' background-color:#a0c0a0;'>"
23040         + "<thead style='display: none;'><th>Icon Name</th>"
23041         + "<th>Icon</th></thead><tbody  "
23042         + "style='display: block;  overflow-y: auto; height: 600px'>"
23043         + "</tbody></table>");
23044 
23045     var icons = window.top.cd.core.cdIcon;
23046 
23047     var addIcon = function (name, iconJson) {
23048 
23049         var width = (iconJson.width) ? iconJson.width : 1000;
23050         var height = (iconJson.height) ? iconJson.height : 1000;
23051 
23052         var iconBuilt = "<tr><td>" + name 
23053             + "</td><td><svg width='" + width 
23054             + "' height='" + height 
23055             + "' style='height: 30px; width: 30px;'  viewBox='" 
23056             + iconJson.viewBox + "'>" 
23057             + iconJson.value + "</svg></td></tr>";
23058             
23059 
23060         try {
23061             $('tbody').append(iconBuilt);
23062         } catch (e) {
23063             console.error("Error when adding " + name, e);
23064         }
23065     }
23066 
23067     for (var icon in icons) {
23068         if (icons[icon].viewBox) addIcon(icon, icons[icon])
23069     }
23070 }
23071 
23072 showIcons();
23073              * </pre>
23074              * 
23075              * @constructs
23076              */
23077             _fakeConstuctor: function () {
23078                 /* This is here so we can document init() as a method rather than as a constructor. */
23079             },
23080 
23081             /**
23082              * @example
23083              * <BR><BR><b>Example JSON for <i>MenuConfig</i> data:</b>
23084              * <pre class='inset'>
23085 {
23086   "label" : "Chat", 
23087   "menuItems" :     
23088           [     
23089             {
23090                 "id": "ready-menu-item", 
23091                 "label": "Ready",         
23092                 "iconColor": "available" 
23093             },
23094             {
23095                 "id": "not-ready-menu-item",
23096                 "label": "Not Ready",
23097                 "iconColor": "unavailable"
23098             }
23099          ]
23100 }
23101              * </pre>
23102              * @returns
23103              * Schema for validation of the below JSON definition:
23104              * <table class="schemaTable">
23105              * <thead>
23106              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
23107              * </thead>
23108              * <tbody>
23109              * <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>
23110              * <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>
23111              * <tr><td class='pad30'>id</td><td>String</td><td>ready-menu-item</td><td>This id needs to be unique for a channel. 
23112              *      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>
23113              * <tr><td class='pad30'>label</td><td>String</td><td>Ready</td><td>The text of menu item</td></tr>
23114              * <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>
23115              * </tbody>
23116              * </table>
23117              * 
23118              * 
23119              */
23120             getMenuConfigSchema: function () {
23121                 return _menuConfigSchema;
23122             },
23123 
23124             /**
23125              * @example
23126              * <BR><BR><b>Example JSON for <i>ChannelConfig</i> data:</b>
23127              * <pre class='inset'>
23128 {
23129     "actionTimeoutInSec": 5, 
23130     "icons"             :  [
23131                                 {
23132                                     "type": "collab-icon",
23133                                     "value": "Chat"         
23134                                 },
23135                                 {
23136                                     "type": "url",
23137                                     "value": "../../thirdparty/gadget3/channel-icon.png"
23138                                 }
23139                             ]
23140 }            
23141              * </pre>
23142              * @returns
23143              * Schema for validation of the below JSON definition:
23144              * <table class="schemaTable">
23145              * <thead>
23146              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
23147              * </thead>
23148              * <tbody>
23149              * <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 
23150              *              selection request to the gadget.
23151              *              Upper limit is 30 seconds. It is recommended that this limit be kept as 
23152              *              low as possible, since no other operation can be performed 
23153              *              on FNC during this period</td></tr>
23154              * <tr><td>icons</td><td colspan='2'>Array</td><td>JSON array of icons to be composed and displayed in the Header 
23155              *              to visually represent a channel. This should ideally never change. 
23156              *              A single item is defined below</td></tr>
23157              * <tr><td class='pad30'>type</td><td>Enum</td><td>collab-icon</td>
23158              * <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>
23159              * <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>
23160              * <tr><td class='pad30'>value</td><td>String</td><td>Chat</td><td>The <a href="#cdIcons">stock icon name</a>
23161              * or the url of the custom icon</td></tr>
23162              * </tbody>
23163              * </table>
23164              */
23165             getChannelConfigSchema: function () {
23166                 return _channelConfigSchema;
23167             },
23168 
23169 
23170             /**
23171              * @example
23172              * <BR><BR><b>Example JSON for <i>ChannelState</i> data:</b>
23173              * <pre class='inset'>
23174 {
23175     "label"          : "Chat & Email", 
23176     "currentState"   : "ready", 
23177     "iconColor"      : "available",
23178     "enable"         : true,  
23179     "logoutDisabled" : true,  
23180     "logoutDisabledText" : "Please go unavailable on chat before logout", 
23181     "iconBadge"      :  "none"  
23182     "hoverText"      : "Tooltip text"
23183 }
23184              * </pre>
23185              * 
23186              * @returns
23187              * Schema for validation of the below JSON definition:
23188              * 
23189              * <table class="schemaTable">
23190              * <thead>
23191              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
23192              * </thead>
23193              * <tbody>
23194              * <tr><td>label</td><td>String</td><td>Chat & Email</td><td>The name used for the channel on hover</td></tr>
23195              * <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>
23196              * <tr><td>iconColor</td><td>Enum</td><td>available</td>
23197              *      <td>Takes one of these values; available - shows up as green; 
23198              *          unavailable - shows up as red; busy - shows up as orange. 
23199              *          The icon colors will be used by the FNC in composing the final icon for the channel, 
23200              *          that indicates the current state of the channel.</td></tr>
23201              * <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>
23202              * <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.
23203              * 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.
23204              * <BR>Note: The true setting in any of the Channel across all Channels will cause the Logout menu to be disabled.</td></tr>
23205              * <tr><td>logoutDisabledText</td><td>String</td><td>Please go unavailable on chat before logout</td>
23206              *      <td>The text which will be shown to the user if the logout is disabled</td></tr>
23207              * <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>
23208              * <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>
23209              * </tbody>
23210              * </table>
23211              */
23212             getChannelStateSchema: function () {
23213                 return _channelStateSchema;
23214             }
23215         };
23216     }());
23217 
23218     window.finesse = window.finesse || {};
23219     window.finesse.digital = window.finesse.digital || {};
23220     window.finesse.digital.ChannelSchema = ChannelSchema;
23221 
23222     // CommonJS
23223     if (typeof module === "object" && module.exports) {
23224         module.exports = ChannelSchema;
23225     }
23226 
23227     return ChannelSchema;
23228 });
23229 
23230 /**
23231  * API for managing Finesse Digital Channels.
23232  */
23233 /** @private */
23234 define('digital/ChannelService',['require','exports','module','./ChannelSchema','../utilities/JsonValidator','../utilities/Utilities','../clientservices/ClientServices'],function (require, exports, module) {
23235 	
23236     var ChannelService = (function () { /** @lends finesse.digital.ChannelService.prototype */
23237     	
23238         var SchemaMgr = require('./ChannelSchema'),
23239             SchemaValidator = require('../utilities/JsonValidator'),
23240             Utilities = require('../utilities/Utilities'),
23241             ClientService = require('../clientservices/ClientServices'),
23242             /**
23243              * References to ContainerService
23244              * @private
23245              */
23246             _containerService,
23247 
23248             /**
23249              * Whether the ChannelService have been initialized or not.
23250              * @private
23251              */
23252             _inited = false,
23253 
23254             /**
23255              * References to ClientServices logger
23256              * @private
23257              */
23258             _logger = {
23259                 log: ClientService.log
23260             },
23261 
23262             /**
23263              * Open Ajax topic for the API to publish requests for register, de-register and update on the
23264              * NonVoice Channels. The Finesse NonVoice Component (FNC) will listen to this topic and process
23265              * the request.
23266              * @private
23267              */
23268             _fncTopic,
23269 
23270             /**
23271              * Lists the various channel actions taken by gadget.
23272              * @private
23273              */
23274             ACTIONS = Object.freeze({
23275                 ADD: 'ADD',
23276                 REMOVE: 'REMOVE',
23277                 UPDATE: 'UPDATE',
23278                 UPDATE_MENU: 'UPDATE_MENU',
23279                 UPDATE_CHANNEL_STATE: 'UPDATE_CHANNEL_STATE'
23280             }),
23281 
23282             /**
23283              * Operation status enum
23284              * @private
23285              */
23286             STATUS = Object.freeze({
23287                 SUCCESS: 'success',
23288                 FAILURE: 'failure'
23289             }),
23290 
23291             /**
23292              * State Status enum
23293              * @private
23294              */
23295             STATE_STATUS = Object.freeze({
23296                 AVAILABLE: 'available',
23297                 UNAVAILABLE: 'unavailable',
23298                 BUSY: 'busy'
23299             }),
23300 
23301             /**
23302              * Icon Color mapping for state status
23303              */
23304             ICON_COLOR = Object.freeze({
23305                 available: '#2CB14C',
23306                 unavailable: '#CF4237',
23307                 busy: '#D79208'
23308             }),
23309 
23310             /**
23311              * Channel Icon Type
23312              * @private
23313              */
23314             ICON_TYPE = Object.freeze({
23315                 COLLAB_ICON: "collab-icon",
23316                 URL: "url"
23317             }),
23318 
23319             /**
23320              * Icon Badge Type
23321              * @private
23322              */
23323             BADGE_TYPE = Object.freeze({
23324                 NONE: "none",
23325                 INFO: "info",
23326                 WARNING: "warning",
23327                 ERROR: "error"
23328             }),
23329 
23330             /*
23331              * Dynamic registry object, which keeps a map of success and error callbacks for requests whose
23332              * responses are expected asynchronously. Error callback will be invoked on operation timeout.
23333              * @private
23334              */
23335             _requestCallBackRegistry = (function () {
23336 
23337                 var OPERATION_TIMEOUT = 60000,
23338                     _map = {},
23339 
23340                     _clear = function (_uid) {
23341                         if (_map[_uid]) {
23342                             if (_map[_uid].pendingTimer) {
23343                                 clearTimeout(_map[_uid].pendingTimer);
23344                             }
23345                             delete _map[_uid];
23346                         }
23347                     },
23348 
23349                     _requestTimedOutHandler = function (_uid) {
23350                         var err = {
23351                             status: 'failure',
23352                             error: {
23353                                 errorCode: '408',
23354                                 errorDesc: 'Request Timed Out'
23355                             }
23356                         };
23357                         if (_map[_uid] && typeof _map[_uid].error === 'function') {
23358                             _logger.log("ChannelService: Request Timeout. Request Id : " + _uid);
23359                             _map[_uid].error(err);
23360                         }
23361                         _clear(_uid);
23362                     },
23363 
23364                     _initTimerForTimeout = function (_uid) {
23365                         _map[_uid].pendingTimer = setTimeout(function () {
23366                             _requestTimedOutHandler(_uid);
23367                         }, OPERATION_TIMEOUT);
23368                     },
23369 
23370                     _add = function (_uid, _chId, _success, _error) {
23371                         _map[_uid] = {
23372                             channelId: _chId,
23373                             success: _success,
23374                             error: _error
23375                         };
23376                         _initTimerForTimeout(_uid);
23377                     };
23378 
23379                 return {
23380                     register: _add,
23381                     clear: _clear,
23382                     success: function (uid, data) {
23383                         if (_map[uid] && typeof _map[uid].success === 'function') {
23384                             data.channelId = _map[uid].channelId;
23385                             var response = _map[uid].success(data);
23386                             _clear(uid);
23387                             return response;
23388                         }
23389                     },
23390                     error: function (uid, data) {
23391                         if (_map[uid] && typeof _map[uid].error === 'function') {
23392                             data.channelId = _map[uid].channelId;
23393                             var response = _map[uid].error(data);
23394                             _clear(uid);
23395                             return response;
23396                         }
23397                     }
23398                 };
23399             }()),
23400 
23401             /*
23402              * Dynamic registry object, which keeps a map of channel id and its menu handler function reference.
23403              * This handler will be invoked on user interaction with its channel menu.
23404              * @private
23405              */
23406             _menuHandlerRegistry = (function () {
23407                 var _map = {},
23408 
23409                     _add = function (_id, _chId, _menuHandler) {
23410                         _map[_id] = {
23411                             channelId: _chId,
23412                             menuHandler: _menuHandler
23413                         };
23414                     },
23415 
23416                     _clear = function (_id) {
23417                         if (_map[_id]) {
23418                             delete _map[_id];
23419                         }
23420                     };
23421 
23422                 return {
23423                     register: _add,
23424                     clear: _clear,
23425                     menuSelection: function (_id, _menuItemId, _success, _error) {
23426                         if (_map[_id] && _map[_id].menuHandler) {
23427                             return _map[_id].menuHandler(_map[_id].channelId, _menuItemId,
23428                                 _success, _error);
23429                         }
23430                     }
23431                 };
23432             }()),
23433 
23434             /**
23435              * Ensure that ChannelService have been inited.
23436              * @private
23437              */
23438             _isInited = function () {
23439                 if (!_inited) {
23440                     throw new Error("ChannelService needs to be inited.");
23441                 }
23442             },
23443 
23444             /**
23445              * Gets the id of the gadget.
23446              * @returns {number} the id of the gadget
23447              * @private
23448              */
23449             _getMyGadgetId = function () {
23450                 var gadgetId = _containerService.getMyGadgetId();
23451                 return gadgetId || '';
23452             },
23453 
23454             /**
23455              * Validate the menuConfig structure in channelData payload.
23456              *
23457              * @param {Object} data
23458              *     menuConfig structure.
23459              * @throws {Error} if the data is not in menuConfig defined format.
23460              * @private
23461              */
23462             _validateMenuConfig = function (data) {
23463                 var result = SchemaValidator.validateJson(data, SchemaMgr.getMenuConfigSchema());
23464                 /* if result.valid is false, then additional details about failure are contained in
23465                    result.error which contains json like below:
23466 
23467                     {
23468                         "code": 0,
23469                         "message": "Invalid type: string",
23470                         "dataPath": "/intKey",
23471                         "schemaPath": "/properties/intKey/type"
23472                     }
23473                 */
23474                 if (!result.valid) {
23475                     _logger.log("ChannelService: Finesse Nonvoice API Validation result : " + JSON.stringify(result));
23476                     throw new Error("menuConfig structure is not in expected format. Refer finesse client logs for more details.");
23477                 }
23478             },
23479 
23480             /**
23481              * Validate the channelConfig structure in channelData payload.
23482              *
23483              * @param {Object} data
23484              *     channelConfig structure.
23485              * @throws {Error} if the data is not in channelConfig defined format.
23486              * @private
23487              */
23488             _validateChannelConfig = function (data) {
23489                 var result = SchemaValidator.validateJson(data, SchemaMgr.getChannelConfigSchema());
23490                 if (!result.valid) {
23491                     _logger.log("ChannelService: Finesse Nonvoice API Validation result : " + JSON.stringify(result));
23492                     throw new Error("channelConfig structure is not in expected format. Refer finesse client logs for more details.");
23493                 }
23494             },
23495 
23496             /**
23497              * Validate the channelState structure in channelData payload.
23498              *
23499              * @param {Object} data
23500              *     channelState structure.
23501              * @throws {Error} if the data is not in channelState defined format.
23502              * @private
23503              */
23504             _validateChannelState = function (data) {
23505                 var result = SchemaValidator.validateJson(data, SchemaMgr.getChannelStateSchema());
23506                 if (!result.valid) {
23507                     _logger.log("ChannelService: Finesse Nonvoice API Validation result : " + JSON.stringify(result));
23508                     throw new Error("channelState structure is not in expected format. Refer finesse client logs for more details.");
23509                 }
23510             },
23511 
23512             /**
23513              * Validate the entire channelData structure in payload.
23514              *
23515              * @param {Object} data
23516              *     channelData structure.
23517              * @throws {Error} if the data is not in channelData defined format.
23518              * @private
23519              */
23520             _validateAddChannelPayload = function (data) {
23521                 var err = "";
23522                 if (!data.hasOwnProperty("menuConfig")) {
23523                     err = "menuConfig property missing in Channel Data";
23524                 } else if (!data.hasOwnProperty("channelConfig")) {
23525                     err = "channelConfig property missing in Channel Data";
23526                 } else if (!data.hasOwnProperty("channelState")) {
23527                     err = "channelState property missing in Channel Data";
23528                 }
23529 
23530                 if (err) {
23531                     throw new Error(err);
23532                 }
23533                 _validateMenuConfig(data.menuConfig);
23534                 _validateChannelConfig(data.channelConfig);
23535                 _validateChannelState(data.channelState);
23536             },
23537 
23538             /**
23539              * Validate the available structure in payload.
23540              *
23541              * @param {Object} data
23542              *     channelData structure.
23543              * @throws {Error} if the data is not in channelData defined format.
23544              * @private
23545              */
23546             _validateUpdateChannelPayload = function (data) {
23547                 if (data.hasOwnProperty("menuConfig")) {
23548                     _validateMenuConfig(data.menuConfig);
23549                 }
23550                 if (data.hasOwnProperty("channelConfig")) {
23551                     _validateChannelConfig(data.channelConfig);
23552                 }
23553                 if (data.hasOwnProperty("channelState")) {
23554                     _validateChannelState(data.channelState);
23555                 }
23556             },
23557 
23558             /**
23559              * Validate the gadget passed JSON structure against the schema definition.
23560              *
23561              * @param {Object} data
23562              *     channelData structure.
23563              * @throws {Error} if the data is not in channelData defined format.
23564              * @private
23565              */
23566             _validateData = function (data, action) {
23567                 switch (action) {
23568                     case ACTIONS.ADD:
23569                         _validateAddChannelPayload(data);
23570                         break;
23571                     case ACTIONS.UPDATE:
23572                         _validateUpdateChannelPayload(data);
23573                         break;
23574                     case ACTIONS.UPDATE_CHANNEL_STATE:
23575                         _validateChannelState(data);
23576                         break;
23577                     case ACTIONS.UPDATE_MENU:
23578                         _validateMenuConfig(data);
23579                         break;
23580                 }
23581             },
23582 
23583             /**
23584              * Prepares the FNC required payload based on the action and the supplied JSON data.
23585              *
23586              * @param {Object} data
23587              *     json structure used for channel request.
23588              * @returns {Object} data in FNC required payload format.
23589              * @private
23590              */
23591             _prepareFNCChannelData = function (data, action) {
23592                 switch (action) {
23593                     case ACTIONS.ADD:
23594                     	if (data.hasOwnProperty("channelState")) {
23595                     		data.channelState.iconColor = ICON_COLOR[data.channelState.iconColor];
23596                     	}
23597                         data.menuConfig.menuItems.forEach(function (item) {
23598                             item.iconColor = ICON_COLOR[item.iconColor];
23599                         });
23600                         break;
23601                     case ACTIONS.UPDATE:
23602                         if (data.hasOwnProperty("channelState")) {
23603                             data.channelState.iconColor = ICON_COLOR[data.channelState.iconColor];
23604                         }
23605                         if (data.hasOwnProperty("menuConfig")) {
23606                             data.menuConfig.menuItems.forEach(function (item) {
23607                                 item.iconColor = ICON_COLOR[item.iconColor];
23608                             });
23609                         }
23610                         break;
23611                     case ACTIONS.UPDATE_CHANNEL_STATE:
23612                         data.iconColor = ICON_COLOR[data.iconColor];
23613                         data = {
23614                             channelState: data
23615                         };
23616                         break;
23617                     case ACTIONS.UPDATE_MENU:
23618                         data.menuItems.forEach(function (item) {
23619                             item.iconColor = ICON_COLOR[item.iconColor];
23620                         });
23621                         data = {
23622                             menuConfig: data
23623                         };
23624                         break;
23625                 }
23626                 if(data.channelState){
23627                 	data.channelState.isAgentReady = data.channelState.iconColor === ICON_COLOR.available;
23628                 }
23629                 
23630                 return data;
23631             },
23632 
23633             /**
23634              * Utility function to make a subscription to a particular topic. Only one
23635              * callback function is registered to a particular topic at any time.
23636              *
23637              * @param {String} topic
23638              *     The full topic name. The topic name should follow the OpenAjax
23639              *     convention using dot notation (ex: finesse.api.User.1000).
23640              * @param {Function} handler
23641              *     The function that should be invoked with the data when an event
23642              *     is delivered to the specific topic.
23643              * @returns {Boolean}
23644              *     True if the subscription was made successfully and the callback was
23645              *     been registered. False if the subscription already exist, the
23646              *     callback was not overwritten.
23647              * @private
23648              */
23649             _subscribe = function (topic, handler) {
23650                 try {
23651                     return _containerService.addHandler(topic, handler);
23652                 } catch (error) {
23653                     _logger.log('ChannelService: Error while subscribing to open ajax topic : ' + topic + ', Exception:' + error.toString());
23654                     throw new Error('ChannelService: Unable to subscribe the channel topic with Open Ajax Hub : ' + error);
23655                 }
23656             },
23657 
23658             /**
23659              * Function which processes the menu selection request from FNC.
23660              *
23661              * @param {Object} payload
23662              *     The payload containing the menu selection request details.
23663              * @private
23664              */
23665             _processMenuChangeRequest = function (payload) {
23666                 _menuHandlerRegistry.menuSelection(payload.id, payload.selection, function (successPayload) {
23667                     var response = {
23668                         id: payload.id,
23669                         requestId: payload.requestId,
23670                         status: successPayload.hasOwnProperty('status') ? successPayload.status : STATUS.SUCCESS
23671                     };
23672                     _containerService.publish(_fncTopic, response);
23673                 }, function (failurePayload) {
23674                     var response = {
23675                         id: payload.id,
23676                         requestId: payload.requestId,
23677                         status: failurePayload.hasOwnProperty('status') ? failurePayload.status : STATUS.FAILURE
23678                     };
23679                     if (failurePayload.hasOwnProperty('error')) {
23680                         response.error = failurePayload.error;
23681                     }
23682                     _containerService.publish(_fncTopic, response);
23683                 });
23684             },
23685 
23686             _processChannelOperationResult = function (payload) {
23687                 var response = {
23688                     status: payload.status
23689                 };
23690                 if (payload.status === STATUS.FAILURE) {
23691                     // error response
23692                     if (payload.hasOwnProperty('error')) {
23693                         response.error = payload.error;
23694                     }
23695                     _logger.log('ChannelService: Failure response for requestId: ' + payload.requestId);
23696                     _requestCallBackRegistry.error(payload.requestId, response);
23697                 } else {
23698                     // success response
23699                     _requestCallBackRegistry.success(payload.requestId, response);
23700                 }
23701             },
23702 
23703             /**
23704              * Function which processes the messages from Finesse NonVoice Component.
23705              * The messages received mainly for below cases,
23706              * <ul>
23707              *  <li> Operation responses for ADD, REMOVE and UPDATE.
23708              *  <li> User menu selection request.
23709              * <ul>
23710              * The registered callbacks are invoked based on the data.
23711              *
23712              * @param {String} payload
23713              *     The actual message sent by FNC via open ajax hub.
23714              * @private
23715              */
23716             _processFNCMessage = function (payload) {
23717                 _logger.log('ChannelService: Received Message from FNC: ' + JSON.stringify(payload));
23718                 try {
23719                     // if the received data from topic is in text format, then parse it to JSON format
23720                     payload = typeof payload === 'string' ? JSON.parse(payload) : payload;
23721                 } catch (error) {
23722                     _logger.log('ChannelService: Error while parsing the FNC message' + payload);
23723                     return;
23724                 }
23725 
23726                 if (payload.hasOwnProperty('selection')) {
23727                     // menu selection request from FNC
23728                     _processMenuChangeRequest(payload);
23729                 } else {
23730                     // ADD or REMOVE or UPDATE operation status from FNC
23731                     _processChannelOperationResult(payload);
23732                 }
23733             },
23734 
23735             /*
23736              * Validate the data and process the channel API request based on the action.
23737              *
23738              * @param {String} channelId
23739              *     Digtial channel id, should be unique within the gadget.
23740              * @param {Actions Enum} action
23741              *     Any one of the channel action defined in ACTIONS enum.
23742              * @param {Function} success
23743              *     Callback function invoked upon request successful.
23744              * @param {Function} error
23745              *     Callback function invoked upon request errored.
23746              * @param {JSON} data
23747              *     ADD or UPDATE operation data object as per the defined format.
23748              * @param {Function} menuHandler
23749              *     Callback function invoked when the user interacts with the registered channel menu.
23750              * @throws Error
23751              *     Throws error when the passed in data is not in defined format.
23752              * @private
23753              */
23754             _processChannelRequest = function (channelId, action, success, error, data, menuHandler) {
23755                 _logger.log('ChannelService: Received digital channel request. ChannelId ' + channelId + ', Action ' + action + ', Data ' + JSON.stringify(data));
23756 
23757                 _isInited();
23758 
23759                 var id = _getMyGadgetId() + '/' + channelId,
23760                     topic = _fncTopic + '.' + id,
23761                     requestUID, payload;
23762 
23763                 if (action === ACTIONS.REMOVE) {
23764                     // clear the menuHandler for this channel from menu registry
23765                     _menuHandlerRegistry.clear(id);
23766 
23767                     // remove the hub listener for the channel topic
23768                     _containerService.removeHandler(topic, _processFNCMessage);
23769                 } else {
23770                     if (!data) {
23771                         throw new Error("ChannelService: Channel Data cannot be empty.");
23772                     }
23773                     try {
23774                         data = typeof data === 'string' ? JSON.parse(data) : data;
23775                     } catch (err) {
23776                         throw new Error("ChannelService: Data Parsing Failed. ADD or UPDTAE payload not in expected format. Error: " + err.toString);
23777                     }
23778 
23779                     // validate the data
23780                     _validateData(data, action);
23781 
23782                     // prepare FNC payload
23783                     data = _prepareFNCChannelData(data, action);
23784 
23785                     if (action === ACTIONS.ADD) {
23786                         // onMenuClick must be supplied and of type function
23787                         if (!menuHandler || typeof menuHandler !== 'function') {
23788                             throw new Error("ChannelService: onMenuClick parameter in addChannel() must be a function.");
23789                         }
23790 
23791                         // register the menuHandler for this channel with menu registry for later use
23792                         _menuHandlerRegistry.register(id, channelId, menuHandler);
23793 
23794                         // subscribe this channel topic for messages from Finesse NV Header Component
23795                         _subscribe(topic, _processFNCMessage);
23796                     }
23797 
23798                     // derive the FNC action
23799                     action = action !== ACTIONS.ADD ? ACTIONS.UPDATE : action;
23800                 }
23801 
23802 
23803                 requestUID = Utilities.generateUUID();
23804                 payload = {
23805                     id: id,
23806                     topic: topic,
23807                     requestId: requestUID,
23808                     action: action,
23809                     data: data
23810                 };
23811 
23812                 /*
23813                  * register the request with request registry for asynchronously
23814                  * invoking success and failure callbacks about the operation
23815                  * status.
23816                  */
23817                 _requestCallBackRegistry.register(requestUID, channelId, success, error);
23818 
23819                 _logger.log('ChannelService: Sending channel request to FNC: ' + JSON.stringify(payload));
23820 
23821                 _containerService.publish(_fncTopic, payload);
23822             };
23823 
23824         return {
23825             
23826         	
23827 
23828         	
23829         	/**
23830              * @class
23831 			 * <b>F</b>i<b>n</b>esse digital <b>c</b>hannel state control (referred to as FNC elsewhere in this document) 
23832              * is a programmable desktop component that was introduced in Finesse 12.0.
23833              * The ChannelService API provides hooks to FNC that can be leveraged by gadget hosting channels.
23834              * 
23835              * <h3> FNC API </h3>
23836              * 
23837              * The FNC API provides methods that can be leveraged by gadgets serving channels to 
23838              * register, update or modify channel specific display information and corresponding menu action behavior
23839              *  in Agent State Control Menu (referred to as FNC Menu component).
23840              * 
23841              * <BR>
23842              * These APIs are available natively to the gadget through the finesse.js import. Examples of how to write a sample gadget
23843              * can be found <a href="https://github.com/CiscoDevNet/finesse-sample-code/tree/master/LearningSampleGadget">here</a>.
23844              * <BR>
23845              * The FNC API is encapsulated in a module named ChannelService within finesse.js and it can be accessed 
23846              * via the namespace <i><b>finesse.digital.ChannelService</b></i>.
23847 			 *
23848              * <style>
23849              *   .channelTable tr:nth-child(even) { background-color:#EEEEEE; }
23850              *   .channelTable th { background-color: #999999; }
23851              *   .channelTable th, td { border: none; }
23852              *   .pad30 {padding-left: 30px;}
23853              *   .inset {
23854              *          padding: 5px;
23855              *          border-style: inset; 
23856              *          background-color: #DDDDDD; 
23857              *          width: auto; 
23858              *          text-shadow: 2px 2px 3px rgba(255,255,255,0.5); 
23859              *          font: 12px arial, sans-serif; 
23860              *          color:rebeccapurple;
23861              *    } 
23862              * 
23863              * </style>
23864              * 
23865 			 * @example
23866 			 * 
23867 			 * <h3> Example demonstrating initialization </h3>
23868              * 
23869              * <pre class='inset'>
23870 containerServices = finesse.containerservices.ContainerServices.init();
23871 channelService    = finesse.digital.ChannelService.init(containerServices);
23872 channelService.addChannel(channelId, channelData, onMenuClick, onSuccess, onError);
23873              * </pre>
23874 
23875              * @constructs
23876              */
23877             _fakeConstuctor: function () {
23878                 /* This is here so we can document init() as a method rather than as a constructor. */
23879             },
23880 
23881             /**
23882              * Initialize ChannelService for use in gadget.
23883              *
23884              * @param {finesse.containerservices.ContainerServices} ContainerServices instance.
23885              * @returns {finesse.digital.ChannelService} instance.
23886              */
23887             init: function (containerServices) {
23888                 if (!_inited) {
23889                     if (!containerServices) {
23890                         throw new Error("ChannelService: Invalid ContainerService reference.");
23891                     }
23892                     _inited = true;
23893                     _containerService = containerServices;
23894                     window.finesse.clientservices.ClientServices.setLogger(window.finesse.cslogger.ClientLogger);
23895                     _fncTopic = containerServices.Topics.FINEXT_NON_VOICE_GADGET_EVENT;
23896                 }
23897                 return this;
23898             },
23899 
23900             /**
23901              * 
23902              * This API starts the gadget interaction with FNC by adding the channel to the FNC Menu component. 
23903              * The complete channel state is required in the form of JSON payload as part of this API.
23904              * It is advised developers pre-validate the JSON that is passed as parameter against its corresponding schema 
23905              * by testing it through {@link finesse.utilities.JsonValidator.validateJson}.
23906              * The result of the add operation will be returned via the given success or error callback.
23907              *
23908              * @param {String} channelId
23909              *     Digital channel id, should be unique within the gadget.
23910              *     The API developer can supply any string that uniquely identifies the Digital Channel for registration with FNC.
23911              *     This id would be returned in callbacks by FNC when there is a user action on menus that belongs to a channel.
23912              * @param {Object} channelData
23913              *     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.
23914              *     <table class="channelTable">
23915              *     <thead><tr><th>key</th><th>Value</th></tr><thead>
23916              *     <tbody>
23917              *     <tr><td>menuconfig</td><td>Refer {@link finesse.digital.ChannelSchema#getMenuConfigSchema} for description about this JSON payload</td></tr>
23918              *     <tr><td>channelConfig</td><td>Refer {@link finesse.digital.ChannelSchema#getChannelConfigSchema} for description about this JSON payload</td></tr>
23919              *     <tr><td>channelState</td><td>Refer {@link finesse.digital.ChannelSchema#getChannelStateSchema} for description about this JSON payload</td></tr>
23920              *     </tbody>
23921              *     </table>
23922              * 
23923              * @param onMenuClick
23924              *     Handler that is provided by the Gadget to the API during channel addition. 
23925              *     It is invoked whenever the user clicks a menu item on the FNC control.
23926              *     <table class="channelTable">
23927              *     <thead><tr><th>Parameter</th><th>Description</th></tr></thead>
23928              *     <tbody>
23929              *     <tr><td><h4 id="selectedMenuItemId">selectedMenuItemId</h4></td><td>The selectedMenuItemId will contain the menuId as defined in 
23930              *      menuConfig {@link finesse.digital.ChannelSchema#getMenuConfigSchema} payload. 
23931              *      The gadget has to invoke onSuccess callback, if the state change is success or 
23932              *      onError callback if there is a failure performing state change operation 
23933              *      (refer actionTimeoutInSec in JSON payload for timeout) on the underlying channel service.</td></tr>
23934              *     <tr><td><h5 id="success">onSuccess<h5></td><td>The success payload would be of the following format:
23935              *      <pre>
23936              *      {
23937              *          "channelId" : "[ID of the Digital channel]",
23938              *          "status"    : "success"
23939              *      }
23940              *      </pre></td></tr>
23941              *     <tr><td><h5 id="failure">onError<h5></td><td>The error payload would be of the following format:
23942              *      <pre>
23943              *       { 
23944              *       "channelId" : "[ID of the Digital channel]",
23945              *       "status"    : "failure",  
23946              *       "error"     : { 
23947              *                       "errorCode": "[Channel supplied error code that will be logged in Finesse client logs]",
23948              *                       "errorDesc": "An error occurred while processing request"  
23949              *                      }
23950              *       }
23951              *      </pre></td></tr>
23952              *      </tbody></table>
23953              * @param onSuccess
23954              *      Callback function invoked upon add operation successful. Refer above for the returned JSON payload.
23955              * @param onError
23956              *      Callback function invoked upon add operation is unsuccessful. Refer above for the returned JSON payload.
23957              * @throws
23958              *     Throws error when the passed in channelData is not as per schema.
23959              */
23960             addChannel: function (channelId, channelData, onMenuClick, onSuccess, onError) {
23961                 _processChannelRequest(channelId, ACTIONS.ADD, onSuccess, onError, channelData, onMenuClick);
23962             },
23963 
23964             /**
23965              * Removes a channel representation from the FNC Menu component. 
23966              * The result of the remove operation will be intimated via the given success and error callbacks.
23967              *
23968              * @param {String} channelId
23969              *     Digtial channel id, should be unique within the gadget.
23970              *     This id would be returned in the callbacks.
23971              * @param {Function} onSuccess
23972              *     <a href="#success">onSuccess</a> function that is invoked for successful operation.
23973              * @param {Function} onError
23974              *     <a href="#failure">onError</a> function that is invoked for failed operation.
23975              */
23976             removeChannel: function (channelId, onSuccess, onError) {
23977                 _processChannelRequest(channelId, ACTIONS.REMOVE, onSuccess, onError);
23978             },
23979 
23980             /**
23981              * Updates the channels representation in FNC Menu component. 
23982              * None of the data passed within the data payload <i>channelData</i> is mandatory. 
23983              * This API provides an easy way to update the complete channel configuration together in one go or partially if required. 
23984              * The result of the update operation will be intimated via the given success and error callbacks.
23985              *
23986              * @param {String} channelId
23987              *     Digtial channel id, should be unique within the gadget
23988              *     This id would be returned in the callbacks.
23989              * @param {Object} channelData
23990              *     Channel data JSON object as per the spec. Refer {@link #addChannel} for this object description. Partial data sections allowed.
23991              * @param {Function} onSuccess
23992              *     <a href="#success">onSuccess</a> function that is invoked for successful operation.
23993              * @param {Function} onError
23994              *     <a href="#failure">onError</a> function that is invoked for failed operation.
23995              */
23996             updateChannel: function (channelId, channelData, onSuccess, onError) {
23997                 _processChannelRequest(channelId, ACTIONS.UPDATE, onSuccess, onError, channelData);
23998             },
23999 
24000             /**
24001              * Updates the menu displayed against a channel.
24002              * The result of the update operation will be intimated via the given success and error callbacks.
24003              *
24004              * @param {String} channelId
24005              *     Digtial channel id, should be unique within the gadget.
24006              *     This id would be returned in the callbacks.
24007              * @param {menuItem[]} menuItems
24008              *     Refer {@link finesse.digital.ChannelSchema#getMenuConfigSchema} for menuItem definition.
24009              * @param {Function} onSuccess
24010              *     <a href="#success">onSuccess</a> function that is invoked for successful operation.
24011              * @param {Function} onError
24012              *     <a href="#failure">onError</a> function that is invoked for failed operation.
24013              */
24014             updateChannelMenu: function (channelId, menuConfig, onSuccess, onError) {
24015                 _processChannelRequest(channelId, ACTIONS.UPDATE_MENU, onSuccess, onError, menuConfig);
24016             },
24017 
24018             /**
24019              * Updates the channels current state.  The result of
24020              * the update operation will be intimated via the given success and error callbacks.
24021              *
24022              * @param {String} channelId
24023              *     Digtial channel id, should be unique within the gadget.
24024              *     This id would be returned in the callbacks.
24025              * @param {Object} channelState
24026              *     Refer {@link finesse.digital.ChannelSchema#getChannelStateSchema} for channel state definition.
24027              * @param {Function} onSuccess
24028              *     <a href="#success">onSuccess</a> function that is invoked for successful operation.
24029              * @param {Function} onError
24030              *     <a href="#failure">onError</a> function that is invoked for failed operation.
24031              */
24032             updateChannelState: function (channelId, channelState, onSuccess, onError) {
24033                 _processChannelRequest(channelId, ACTIONS.UPDATE_CHANNEL_STATE, onSuccess, onError, channelState);
24034             },
24035 
24036             /**
24037              * ENUM: Operation status.
24038              * [SUCCESS, FAILURE]
24039              */
24040             STATUS: STATUS,
24041 
24042             /**
24043              * ENUM: State Status indicates the icon color in channel.
24044              * [AVAILABLE, UNAVAILABLE, BUSY]*/
24045             STATE_STATUS: STATE_STATUS,
24046 
24047             /**
24048              * ENUM: Channel Icon location type.
24049              * [COLLAB_ICON, URL]
24050              */
24051             ICON_TYPE: ICON_TYPE,
24052 
24053             /**
24054              * ENUM: Icon Badge Type.
24055              * [NONE, INFO, WARNING, ERROR]
24056              */
24057             ICON_BADGE_TYPE: BADGE_TYPE
24058         };
24059     }());
24060     
24061     
24062     /** @namespace Namespace for JavaScript class objects and methods related to Digital Channel management.*/
24063     finesse.digital = finesse.digital || {};
24064 
24065     window.finesse = window.finesse || {};
24066     window.finesse.digital = window.finesse.digital || {};
24067     window.finesse.digital.ChannelService = ChannelService;
24068 
24069     // CommonJS
24070     if (typeof module === "object" && module.exports) {
24071         module.exports = ChannelService;
24072     }
24073 
24074 
24075     return ChannelService;
24076 });
24077 
24078 /**
24079  * Popover service uses a JSON payload for communication from gadget.
24080  * The aforesaid payload has to conform to this schema.
24081  * This schema has been defined as per <a href='http://json-schema.org/'> JSON schema </a> standard.
24082  *
24083  * @see utilities.JsonValidator
24084  */
24085 /** The following comment is to prevent jslint errors about using variables before they are defined. */
24086 /*global window:true, define:true*/
24087 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
24088 define('containerservices/PopoverSchema',['require','exports','module'],function (require, exports, module) {
24089 
24090     var PopoverSchema = (function () { /** @lends finesse.containerservices.PopoverSchema.prototype */
24091 
24092         var _bannerDataSchema = {
24093                 "$schema": "http://json-schema.org/draft-04/schema#",
24094                 "type": "object",
24095                 "properties": {
24096                     "icon": {
24097                         "type": "object",
24098                         "properties": {
24099                             "type": {
24100                                 "type": "string",
24101                                 "enum": ["collab-icon", "url"]
24102                             },
24103                             "value": {
24104                                 "type": "string"
24105                             }
24106                         },
24107                         "required": [
24108                             "type",
24109                             "value"
24110                         ]
24111                     },
24112                     "content": {
24113                         "type": "array",
24114                         "items": [{
24115                             "type": "object",
24116                             "properties": {
24117                                 "name": {
24118                                     "type": "string"
24119                                 },
24120                                 "value": {
24121                                     "type": "string"
24122                                 }
24123                             },
24124                             "required": [
24125                                 "name",
24126                                 "value"
24127                             ]
24128                         }]
24129                     },
24130                     "headerContent": {
24131                         "type": "object",
24132                         "properties": {
24133                                 "maxHeaderTitle": {
24134                                     "type": "string"
24135                                 },
24136                                 "minHeaderTitle": {
24137                                     "type": "string"
24138                                 }
24139                             }
24140                     }
24141                 },
24142                 "required": [
24143                     "icon"
24144                 ],
24145                 "additionalProperties": false
24146             },
24147 
24148             _timeDataSchema = {
24149                 "$schema": "http://json-schema.org/draft-04/schema#",
24150                 "type": "object",
24151                 "properties": {
24152                     "displayTimeoutInSecs": {
24153                         "type": "integer",
24154                         "oneOf": [
24155                             {
24156                                 "minimum": 3,
24157                                 "maximum": 3600
24158                             },
24159                             {
24160                                 "enum": [-1] /* displayTimeoutInSecs can be in range 3 and 3600, or -1. -1 indicates there is no upper limit for timer */
24161                             }
24162                         ]
24163                     },
24164                     "display": {
24165                         "type": "boolean"
24166                     },
24167                     "counterType": {
24168                         "type": "string",
24169                         "enum": ["COUNT_UP", "COUNT_DOWN"]
24170                     }
24171                 },
24172                 "oneOf": [{
24173                     "properties": {
24174                         "display": {
24175                             "type": "boolean",
24176                             "enum": [
24177                                 false
24178                             ]
24179                         }
24180                     },
24181                     "required": [
24182                         "displayTimeoutInSecs",
24183                         "display"
24184                     ],
24185                     "not": {
24186                         "required": ["counterType"]
24187                     },
24188                 },
24189                 {
24190                     "properties": {
24191                         "display": {
24192                             "type": "boolean",
24193                             "enum": [
24194                                 true
24195                             ]
24196                         }
24197                     },
24198                     "required": [
24199                         "displayTimeoutInSecs",
24200                         "display",
24201                         "counterType"
24202                     ]
24203                 }
24204                 ],
24205                 "additionalProperties": false
24206             },
24207 
24208             _actionDataSchema = {
24209                 "$schema": "http://json-schema.org/draft-04/schema#",
24210                 "type": "object",
24211                 "properties": {
24212                     "keepMaximised": {
24213                         "type": "boolean"
24214                     },
24215                     "dismissible": {
24216                         "type": "boolean"
24217                     },
24218                     "clientIdentifier": {
24219                         "type": "string"
24220                     },
24221                     "requiredActionText": {
24222                         "type": "string"
24223                     },
24224                     "buttons": {
24225                         "type": "array",
24226                         "minItems": 1,
24227                         "maxItems": 2,
24228                         "uniqueItems": true,
24229                         "items": [{
24230                             "type": "object",
24231                             "properties": {
24232                                 "id": {
24233                                     "type": "string"
24234                                 },
24235                                 "label": {
24236                                     "type": "string"
24237                                 },
24238                                 "type": {
24239                                     "type": "string"
24240                                 },
24241                                 "hoverText": {
24242                                     "type": "string"
24243                                 },
24244                                 "confirmButtons": {
24245                                     "type": "array",
24246                                     "uniqueItems": true,
24247                                     "items": [{
24248                                         "type": "object",
24249                                         "properties": {
24250                                             "id": {
24251                                                 "type": "string"
24252                                             },
24253                                             "label": {
24254                                                 "type": "string"
24255                                             },
24256                                             "hoverText": {
24257                                                 "type": "string"
24258                                             }
24259                                         },
24260                                         "required": [
24261                                             "id",
24262                                             "label"
24263                                         ]
24264                                     }]
24265                                 }
24266                             },
24267                             "required": [
24268                                 "id",
24269                                 "label",
24270                                 "type"
24271                             ]
24272                         }]
24273                     }
24274                 },
24275                 "oneOf": [{
24276                         "additionalProperties": false,
24277                         "properties": {
24278                             "requiredActionText": {
24279                                 "type": "string"
24280                             },
24281                             "clientIdentifier": {
24282                                 "type": "string"
24283                             },
24284                             "dismissible": {
24285                                 "type": "boolean",
24286                                 "enum": [
24287                                     false
24288                                 ]
24289                             },
24290                             "keepMaximised": {
24291                                 "type": "boolean"
24292                             },
24293                             "buttons": {
24294                                 "type": "array"
24295                             }
24296                         }
24297                     },
24298                     {
24299                         "additionalProperties": false,
24300                         "properties": {
24301                             "requiredActionText": {
24302                                 "type": "string"
24303                             },
24304                             "clientIdentifier": {
24305                                 "type": "string"
24306                             },
24307                             "dismissible": {
24308                                 "type": "boolean",
24309                                 "enum": [
24310                                     true
24311                                 ]
24312                             },
24313                             "keepMaximised": {
24314                                 "type": "boolean"
24315                             }
24316                         }
24317                     }
24318                 ],
24319                 "required": [
24320                     "dismissible"
24321                 ],
24322                 "additionalProperties": false
24323             },
24324 
24325             _headerDataSchema = {
24326                 "$schema": "http://json-schema.org/draft-04/schema#",
24327                 "type": "object",
24328                 "properties": {
24329                     "maxHeaderTitle": {
24330                         "type": "string"
24331                     },
24332                     "minHeaderTitle": {
24333                         "type": "string"
24334                     }
24335                 },
24336                 "additionalProperties": false
24337             };
24338 
24339         return {
24340 
24341             /**
24342              * @class
24343              * Finesse Voice component and Gadget(s) hosting digital services require 
24344              * the {@link finesse.containerservices.PopoverService} to display a popup
24345              * for incoming call and chat events. <BR>
24346              * This API provides the schema that is used in {@link finesse.containerservices.PopoverService} 
24347              * for managing various operations in the popup.
24348              * 
24349              * This schema has been defined as per http://json-schema.org/
24350              * <style>
24351              *   .schemaTable tr:nth-child(even) { background-color:#EEEEEE; }
24352              *   .schemaTable th { background-color: #999999; }
24353              *   .schemaTable th, td { border: none; }
24354              *   .pad30 {padding-left: 30px;}
24355              *   .pad60 {padding-left: 60px;}
24356              *   .inset {
24357              *          padding: 5px;
24358              *          border-style: inset; 
24359              *          background-color: #DDDDDD; 
24360              *          width: auto; 
24361              *          text-shadow: 2px 2px 3px rgba(255,255,255,0.5); 
24362              *          font: 12px arial, sans-serif; 
24363              *          color:rebeccapurple;
24364              *    }
24365              * </style>
24366              * 
24367              * @constructs
24368              */
24369             _fakeConstuctor: function () {
24370                 /* This is here so we can document init() as a method rather than as a constructor. */
24371             },
24372 
24373             /**
24374              * 
24375              * @example
24376              * <BR><BR><b>Example JSON for <i>BannerData</i>:</b>
24377              * <pre class='inset'>
24378 {
24379     "icon": { // Mandatory
24380         "type": "collab-icon",
24381         "value": "chat"
24382     },
24383     "content": [ // Optional. first 6 name/value pairs is shown in popover
24384         {
24385             "name": "Customer Name",
24386             "value": "Michael Littlefoot"
24387         },
24388         {
24389             "name": "Phone Number",
24390             "value": "+1-408-567-789"
24391         },
24392         {
24393             "name": "Account Number",
24394             "value": "23874567923"
24395         },
24396         {
24397             "name": "Issue", // For the below one, tool tip is displayed
24398             "value": "a very long text. a very long text.a very long text.a very long text.a very long text."
24399         }
24400     ]
24401     "headerContent" : {
24402         "maxHeaderTitle" : "Popover maximised title",
24403         "minHeaderTitle" : "Popover minimized title"
24404     }
24405 }            
24406              * </pre>
24407              * 
24408              * @returns
24409              * Schema for the validation of the below JSON definition
24410              * <table class="schemaTable">
24411              * <thead>
24412              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
24413              * </thead>
24414              * <tbody>
24415              * <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>
24416              * <tr><td class='pad30'>type</td><td>Enum</td><td>collab-icon</td>
24417              * <td>Takes either <i>collab-icon</i> or <i>url</i> as a value. 
24418              * <BR><i>collab-icon</i> applies a <a href="finesse.digital.ChannelSchema.html#cdIcons">stock icon</a>
24419              * <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>
24420              * <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 
24421              * or the url of the custom icon</td></tr>
24422              * <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>
24423              * <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>
24424              * <tr><td class='pad30'>value</td><td>String</td><td>Michael Littlefoot</td><td>The corresponding property value that is displayed on the right. 
24425              * <BR>Note: For long property values, a tooltip is displayed.</td></tr>
24426              * <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>
24427              * <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>
24428              * <tr><td class='pad30'>minHeaderTitle</td><td>String</td><td>firstName</td><td>Popover title when it is minimized</td></tr> 
24429              * 
24430              * </tbody>
24431              * </table>
24432              * 
24433              */
24434             getBannerDataSchema: function () {
24435                 return _bannerDataSchema;
24436             },
24437 
24438             /**
24439              * 
24440              * @example
24441              * <BR><BR><b>Example JSON for <i>TimerData</i>:</b>
24442              * <pre class='inset'>
24443 {
24444     "displayTimeoutInSecs": 30, // mandatory. minimum is 3 and maximum is 3600. -1 indicates no upper limit
24445     "display": true, // false means no displayable UI for timer
24446     "counterType": COUNT_UP or COUNT_DOWN,
24447 }
24448              * </pre>
24449              * @returns
24450              * <table class="schemaTable">
24451              * <thead>
24452              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
24453              * </thead>
24454              * <tbody>
24455              * <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>
24456              * <tr><td>display</td><td>boolean</td><td>true</td><td>false indicates not to display any timer </td></tr>
24457              * <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. 
24458              *  On the other hand before chat is autoclosed for a agent RONA, it would be apt to use a COUNT_DOWN timer. </td></tr>
24459              * 
24460              * </tbody>
24461              * </table>
24462              */
24463             getTimerDataSchema: function () {
24464                 return _timeDataSchema;
24465             },
24466 
24467 
24468             /**
24469              * 
24470              * 
24471              * @example
24472              * <BR><BR><b>Example JSON for <i>ActionData</i>:</b>
24473              * <pre class='inset'>
24474 { 
24475     "dismissible": true, // or "false"
24476     "clientIdentifier" : 'popup1', // A string to uniquely identify a specific popover
24477     "requiredActionText": "Please answer the call from your phone",
24478     "buttons": // Optional. Max 2  
24479     [
24480         {
24481             "id": "No",
24482             "label": "Decline",
24483             "type": "Decline",
24484             "hoverText": "",
24485             "confirmButtons": [ // confirmButtons is an optional property in actionData
24486                 {
24487                     "id": "Yes",
24488                     "label": "Reject - Return to campaign",
24489                     "hoverText": ""
24490                 },
24491                 {
24492                     "id": "No",
24493                     "label": "Close - Remove from campaign",
24494                     "hoverText": ""
24495                 }
24496             ]
24497         }
24498     ]
24499 }
24500              * </pre>
24501              * 
24502              * @returns
24503              * Schema for the validation of the below JSON definition
24504              * 
24505              * <table class="schemaTable">
24506              * <thead>
24507              * <tr><th>Key</th><th>Type</th><th>Example</th><th>Description</th></tr>
24508              * </thead>
24509              * <tbody>
24510              * <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>
24511              * <tr><td>clientIdentifier</td><td>string</td><td>popover1</td><td>A unique identifier across all popovers. 
24512              * This is used in the callback for popover events</td></tr>
24513              * <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>
24514              * <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>
24515              * <tr><td class='pad30'>id</td><td>string</td><td>ok1</td><td> A unique ID that represents a button </td></tr>
24516              * <tr><td class='pad30'>label</td><td>string</td><td>Accept</td><td>The display text of the action button</td></tr>
24517              * <tr><td class='pad30'>type</td><td>enum</td><td>Affirm</td><td>Affirm for green button. Decline for red button</td></tr>
24518              * <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>
24519              * <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>
24520              * <tr><td class='pad60'>id</td><td>string</td><td>id</td><td>Id of the confirmation button</td></tr>
24521              * <tr><td class='pad60'>label</td><td>string</td><td>Reject - Return to campaign</td><td>Label to displayed on the button</td></tr>
24522              * <tr><td class='pad60'>hoverText</td><td>string</td><td>Click here to reject<td>Tooltip message on the button</td></tr>
24523              * </tbody>
24524              * </table>
24525              */
24526             getActionDataSchema: function () {
24527                 return _actionDataSchema;
24528             }
24529         };
24530     }());
24531 
24532     window.finesse = window.finesse || {};
24533     window.finesse.containerservices = window.finesse.containerservices || {};
24534     window.finesse.containerservices.PopoverSchema = PopoverSchema;
24535 
24536     // CommonJS
24537     if (typeof module === "object" && module.exports) {
24538         module.exports = PopoverSchema;
24539     }
24540 
24541     return PopoverSchema;
24542 });
24543 /**
24544  * PopoverService provides API consists of methods that would allow a gadget to request Finesse to show popovers.
24545  *
24546  * @example
24547  *    containerServices = finesse.containerservices.ContainerServices.init();
24548  *    popoverService = finesse.containerservices.PopoverService.init(containerServices);
24549  *    popoverService.showPopover(popoverId, popoverData, actionHandler);
24550  */
24551 /** The following comment is to prevent jslint errors about using variables before they are defined. */
24552 /*global window:true, define:true, finesse:true*/
24553 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
24554 define('containerservices/PopoverService',['require','exports','module','../utilities/Utilities','../clientservices/ClientServices','./PopoverSchema','../utilities/JsonValidator'],function (require, exports, module) {
24555 
24556     var PopoverService = (function () { /** @lends finesse.containerservices.PopoverService.prototype */
24557 
24558         const Utilities = require('../utilities/Utilities');
24559         const ClientService = require('../clientservices/ClientServices');
24560         const SchemaMgr = require('./PopoverSchema');
24561         const SchemaValidator = require('../utilities/JsonValidator');
24562 
24563         var
24564             /**
24565              * Open Ajax topic for the API to publish popover requests to Finesse Popover Component.
24566              * @private
24567              */
24568             _popoverTopic,
24569 
24570             /**
24571              * References to gadget's ContainerService
24572              * @private
24573              */
24574             _containerService,
24575 
24576             /**
24577              * Whether the PopoverService have been initialized or not.
24578              * @private
24579              */
24580             _inited = false,
24581 
24582             /*
24583              * References to ClientServices logger
24584              * @private
24585              */
24586             _logger = {
24587                 log: ClientService.log
24588             },
24589 
24590             /**
24591              * Popover Action enum.
24592              * @private
24593              */
24594             POPOVER_ACTION = Object.freeze({
24595                 SHOW: 'show',
24596                 DISMISS: 'dismiss',
24597                 UPDATE: 'update'
24598             }),
24599 
24600             /**
24601              * Ensure that PopoverService have been inited.
24602              * @private
24603              */
24604             _isInited = function () {
24605                 if (!_inited) {
24606                     throw new Error("PopoverService needs to be inited.");
24607                 }
24608                 return _inited;
24609             },
24610 
24611             /**
24612              * Dynamic registry object, which keeps a map of popover id and its action handler function reference.
24613              * This handler will be invoked on actions on the popover.
24614              * @private
24615              */
24616             _popoverRegistry = (function () {
24617                 var _map = {},
24618 
24619                     _add = function (id, handler) {
24620                         _map[id] = {
24621                             handler: handler
24622                         };
24623                     },
24624 
24625                     _clear = function (id) {
24626                         if (_map[id]) {
24627                             delete _map[id];
24628                         }
24629                     };
24630 
24631                 return {
24632                     add: _add,
24633                     clear: _clear,
24634                     isExist: function (id) {
24635                         return _map[id] !== undefined;
24636                     },
24637                     sendEvent: function (id, data) {
24638                         if (_map[id]) {
24639                             _map[id].handler(data);
24640                         }
24641                     }
24642                 };
24643             }()),
24644 
24645             /**
24646              * Utility function to make a subscription to a particular topic. Only one
24647              * callback function is registered to a particular topic at any time.
24648              *
24649              * @param {String} topic
24650              *     The full topic name. The topic name should follow the OpenAjax
24651              *     convention using dot notation (ex: finesse.api.User.1000).
24652              * @param {Function} handler
24653              *     The function that should be invoked with the data when an event
24654              *     is delivered to the specific topic.
24655              * @returns {Boolean}
24656              *     True if the subscription was made successfully and the callback was
24657              *     been registered. False if the subscription already exist, the
24658              *     callback was not overwritten.
24659              * @private
24660              */
24661             _subscribe = function (topic, handler) {
24662                 try {
24663                     return _containerService.addHandler(topic, handler);
24664                 } catch (error) {
24665                     _logger.log('PopoverService: Error while subscribing to open ajax topic : ' + topic + ', Exception:' + error.toString());
24666                     throw new Error('PopoverService: Unable to subscribe the popover topic with Open Ajax Hub : ' + error);
24667                 }
24668             },
24669 
24670             /**
24671              * Gets the id of the gadget.
24672              * @returns {number} the id of the gadget
24673              * @private
24674              */
24675             _getMyGadgetId = function () {
24676                 var gadgetId = _containerService.getMyGadgetId();
24677                 return gadgetId ? gadgetId : '';
24678             },
24679 
24680             /**
24681              * Function which processes the messages from Finesse Popover Component.
24682              * The messages received mainly for below cases,
24683              * <ul>
24684              *  <li> User Interaction with popover.
24685              *  <li> Timeout.
24686              * <ul>
24687              * The registered callbacks are invoked based on the data.
24688              *
24689              * @param {String} response
24690              *     The actual message sent by Finesse Popover Component via open ajax hub.
24691              * @private
24692              */
24693             _processResponseFromPopover = function (response) {
24694                 _logger.log('PopoverService: Received Message from PopoverComp: ' + JSON.stringify(response));
24695                 if (response) {
24696                     try {
24697                         // if the received data from topic is in text format, then parse it to JSON format
24698                         response = typeof response === 'string' ? JSON.parse(response) : response;
24699                     } catch (error) {
24700                         _logger.log('PopoverService: Error while parsing the popover message: ' + error.toString());
24701                         return;
24702                     }
24703 
24704                     // construct the response for gadget
24705                     var data = {};
24706                     data.popoverId = response.id;
24707                     data.source = response.source;
24708 
24709                     _logger.log('PopoverService: Calling gadget action handler');
24710 
24711                     // invoke the gadget's action handler to process the action taken in popover
24712                     _popoverRegistry.sendEvent(data.popoverId, data);
24713 
24714                     // clear the popover cached data, no more needed
24715                     _popoverRegistry.clear(data.popoverId);
24716                 }
24717             },
24718 
24719             /**
24720              * Validate the gadget passed JSON structure against the schema definition.
24721              *
24722              * @param {Object} bannerData
24723              *     Banner data JSON object as per the spec.
24724              * @param {Object} timerData
24725              *     Timer data JSON object as per the spec.
24726              * @param {Object} actionData
24727              *     Action data JSON object as per the spec.
24728              * @throws {Error} if the data is not as per defined format.
24729              * @private
24730              */
24731             _validateData = function (bannerData, timerData, actionData) {
24732                 var bannerDataResult = SchemaValidator.validateJson(bannerData, SchemaMgr.getBannerDataSchema());
24733                 /* if result.valid is false, then additional details about failure are contained in
24734                    result.error which contains json like below:
24735 
24736                     {
24737                         "code": 0,
24738                         "message": "Invalid type: string",
24739                         "dataPath": "/intKey",
24740                         "schemaPath": "/properties/intKey/type"
24741                     }
24742                 */
24743                 if (!bannerDataResult.valid) {
24744                     _logger.log("PopoverService: Banner data validation bannerDataResult : " + JSON.stringify(bannerDataResult));
24745                     throw new Error("PopoverService: Banner data structure is not in expected format. Refer finesse client logs for more details.");
24746                 }
24747                 var timerDataResult = SchemaValidator.validateJson(timerData, SchemaMgr.getTimerDataSchema());
24748                 if (!timerDataResult.valid) {
24749                     _logger.log("PopoverService: Timer data validation timerDataResult : " + JSON.stringify(timerDataResult));
24750                     throw new Error("PopoverService: Timer data structure is not in expected format. Refer finesse client logs for more details.");
24751                 }
24752                 var actionDataResult = SchemaValidator.validateJson(actionData, SchemaMgr.getActionDataSchema());
24753                 if (!actionDataResult.valid) {
24754                     _logger.log("PopoverService: Action data validation actionDataResult : " + JSON.stringify(actionDataResult));
24755                     throw new Error("PopoverService: Action data structure is not in expected format. Refer finesse client logs for more details.");
24756                 }
24757             },
24758 
24759             /**
24760              * Parse the data for JSON object.
24761              *
24762              * @param {String} data
24763              *     popover data structure.
24764              * @throws {Error} if the JSON parsing fails.
24765              * @private
24766              */
24767             _getJSONObject = function (data) {
24768                 if (data) {
24769                     try {
24770                         // parse the data as user may pass in string or object format
24771                         data = typeof data === 'string' ? JSON.parse(data) : data;
24772                     } catch (err) {
24773                         throw new Error("PopoverService: Data Parsing Failed. Popover data not in expected format. Error: " + err.toString());
24774                     }
24775                     return data;
24776                 }
24777             },
24778 
24779             /**
24780              * Requests Finesse to show notification popover with the given payload. The user interaction or timeout of the popover
24781              * will be notified to gadget through the registered action handler.
24782              *
24783              * @param {Boolean} isExistingPopover
24784              *     If Update or new Show operation .
24785              * @param {String} popoverId
24786              *     Popover Id
24787              * @param {Object} payload
24788              *     Action data JSON object as per the spec.
24789              * @param {Function} actionHandler
24790              *     Callback function invoked when the user interacts with the popover or popover times out.
24791              * @throws Error
24792              *     Throws error when the passed in popoverData is not as per defined format.
24793              */
24794             _display = function (isExistingPopover, popoverId, payload, actionHandler) {
24795                 // subscribe this popover topic with open ajax hub for processing the response from popover component
24796                 _subscribe(payload.topic, _processResponseFromPopover);
24797 
24798                 // cache the gadget action handler, so that it will be used when processing the popover response
24799                 if (!isExistingPopover) _popoverRegistry.add(popoverId, actionHandler);
24800 
24801                 _logger.log('PopoverService: Sending popover show request to Finesse Popover Component: ' + JSON.stringify(payload));
24802 
24803                 // publish the popover show request to popover UI component
24804                 _containerService.publish(_popoverTopic, payload);
24805             },
24806 
24807             /**
24808              * Requests Finesse to dismiss the notification popover.
24809              *
24810              * @param {String} popoverId
24811              *     Popover id which was returned from showPopover call
24812              * @throws Error
24813              *     Throws error if the service is not yet initialized.
24814              */
24815             _dismiss = function (popoverId) {
24816                 // check whether the service is inited
24817                 _isInited();
24818 
24819                 _logger.log('PopoverService: Received popover dismiss request for popover id ' + popoverId);
24820 
24821                 if (popoverId && _popoverRegistry.isExist(popoverId)) {
24822                     // construct the payload required for the popover component
24823                     var payload = {
24824                         id: popoverId,
24825                         action: POPOVER_ACTION.DISMISS
24826                     };
24827 
24828                     // publish the popover dismiss request to popover UI component
24829                     _containerService.publish(_popoverTopic, payload);
24830 
24831                     // clear the popover cached data, no more needed
24832                     _popoverRegistry.clear(popoverId);
24833                 }
24834             };
24835 
24836         return {
24837             /**
24838              * @class
24839              * Finesse Voice component and Gadget(s) hosting digital services require 
24840              * the {@link finesse.containerservices.PopoverService} to display a popup
24841              * for incoming call and chat events. <BR>
24842              * This API provides makes use of the schema as defined in {@link finesse.containerservices.PopoverSchema} 
24843              * and provides various operations for managing the popup.
24844              * 
24845              * <style>
24846              *   .popoverTable tr:nth-child(even) { background-color:#EEEEEE; }
24847              *   .popoverTable th { background-color: #999999; }
24848              *   .popoverTable th, td { border: none; }
24849              *   .pad30 {padding-left: 30px;}
24850              *   .inset {
24851              *          padding: 5px;
24852              *          border-style: inset; 
24853              *          background-color: #DDDDDD; 
24854              *          width: auto; 
24855              *          text-shadow: 2px 2px 3px rgba(255,255,255,0.5); 
24856              *          font: 12px arial, sans-serif; 
24857              *          color:rebeccapurple;
24858              *    } 
24859              * 
24860              * 
24861              * </style>
24862              *
24863              * @example
24864              * <BR><BR><b>Example demonstrating initialization</b>
24865              * 
24866 <pre class='inset'>      
24867     containerServices = finesse.containerservices.ContainerServices.init();
24868     popoverService = finesse.containerservices.PopoverService.init(containerServices);
24869     popoverService.showPopover(popoverId, popoverData, actionHandler);
24870 </pre> 
24871              *    Details of the parameters are described in this API spec below.
24872              *    
24873              * @constructs
24874              */
24875             _fakeConstuctor: function () {
24876                 /* This is here so we can document init() as a method rather than as a constructor. */
24877             },
24878 
24879             /**
24880              * Initialize the PopoverService for use in gadget.
24881              * See example in <i> Class Detail </i> for initialization.
24882              *
24883              * @param {finesse.containerservices.ContainerServices} ContainerService instance. 
24884              * @returns {finesse.containerservices.PopoverService} instance.
24885              */
24886             init: function (containerService) {
24887                 if (!_inited) {
24888                     if (!containerService) {
24889                         throw new Error("PopoverService: Invalid ContainerService reference.");
24890                     }
24891                     _containerService = containerService;
24892                     _popoverTopic = containerService.Topics.FINEXT_POPOVER_EVENT;
24893                     window.finesse.clientservices.ClientServices.setLogger(window.finesse.cslogger.ClientLogger);
24894                     _inited = true;
24895                 }
24896                 return this;
24897             },
24898 
24899             /**
24900              * Combines multiple parameters and generates a single payload for use by the popover service.
24901              *
24902              * @param {Boolean} isExistingPopover
24903              *     True if this popover already exists. False if not.
24904              * @param {String} popoverId
24905              *     The unique identifier for the popover. This id was returned from showPopover call.
24906              * @param {Object} bannerData
24907              *     Refer {@link finesse.containerservices.PopoverSchema#getBannerDataSchema} for description about this JSON payload
24908              * @param {Object} timerData
24909              *     Refer {@link finesse.containerservices.PopoverSchema#getTimerDataSchema} for description about this JSON payload
24910              * @param {Object} actionData
24911              *     Refer {@link finesse.containerservices.PopoverSchema#getActionDataSchema} for description about this JSON payload
24912              * @throws 
24913              *     Throws error when the passed in popoverData is not as per defined format.
24914              */
24915             generatePayload: function (isExistingPopover, popoverId, bannerData, timerData, actionData) {
24916                 // parse the data
24917                 bannerData = _getJSONObject(bannerData);
24918                 timerData = _getJSONObject(timerData);
24919                 actionData = _getJSONObject(actionData);
24920                 var topic = _popoverTopic + '.' + popoverId;
24921 
24922                 // validate the data
24923                 _validateData(bannerData, timerData, actionData);
24924 
24925                 var action = isExistingPopover ? POPOVER_ACTION.UPDATE : POPOVER_ACTION.SHOW;
24926                 // construct the payload required for the popover component
24927                 var payload = {
24928                     id: popoverId,
24929                     topic: topic,
24930                     action: action,
24931                     data: {
24932                         bannerData: bannerData,
24933                         timerData: timerData
24934                     }
24935                 };
24936 
24937                 if (actionData) {
24938                     payload.data.actionData = actionData;
24939                 }
24940 
24941                 return payload;
24942             },
24943 
24944             /**
24945              * 
24946              * Display a popover with the given data. The user interaction or timeout of the popover
24947              * will be notified to gadget through the registered action handler.
24948              *
24949              * @param {Object} bannerData
24950              *     Refer {@link finesse.containerservices.PopoverSchema#getBannerDataSchema} for description about this JSON payload
24951              * @param {Object} timerData
24952              *     Refer {@link finesse.containerservices.PopoverSchema#getTimerDataSchema} for description about this JSON payload
24953              * @param {Object} actionData
24954              *     Refer {@link finesse.containerservices.PopoverSchema#getActionDataSchema} for description about this JSON payload
24955              * @param {Function} actionHandler
24956              *   This is a Handler that gets called for events due to user interaction. Following are the params of this function.
24957              *   <table class="popoverTable">
24958              *   <thead><tr><th>Parameter</th><th>Description</th></tr></thead>
24959              *   <tbody>
24960              *   <tr><td>popoverId</td><td>A unique popover id that was assigned to the new popover.</td></tr>
24961              *   <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>
24962              *   </table>
24963              *   
24964              * @returns {String} 
24965              *     Generated Popover-id, can be used for subsequent interaction with the service.
24966              * @throws 
24967              *     Throws error when the passed in popoverData is not as per defined format.
24968              */
24969             showPopover: function (bannerData, timerData, actionData, actionHandler) {
24970                 // check whether the service is inited
24971                 _isInited();
24972 
24973                 var isExistingPopover = false; 
24974 
24975                 // construct a unique id for the popover
24976                 var popoverId = Utilities.generateUUID(),
24977                     gadgetId = _getMyGadgetId();
24978 
24979                 _logger.log('PopoverService: Received new popover show request from gadgetId ' + gadgetId);
24980 
24981                 var payload = this.generatePayload(isExistingPopover, popoverId, bannerData, timerData, actionData);
24982 
24983                 // check for action handler
24984                 if (typeof actionHandler !== 'function') {
24985                     throw new Error('PopoverService: Action handler should be a function');
24986                 }
24987 
24988                 _display(isExistingPopover, popoverId, payload, actionHandler);
24989 
24990                 return popoverId;
24991             },
24992 
24993             /**
24994              * 
24995              * Modify an active popover's displayed content.
24996              * 
24997              * @param {String} popoverId
24998              *     A unique popover id that was returned in {@link #showPopover}.
24999              * @param {Object} bannerData
25000              *     Refer {@link finesse.containerservices.PopoverSchema#getBannerDataSchema} for description about this JSON payload
25001              * @param {Object} timerData
25002              *     Refer {@link finesse.containerservices.PopoverSchema#getTimerDataSchema} for description about this JSON payload
25003              * @param {Object} actionData
25004              *     Refer {@link finesse.containerservices.PopoverSchema#getActionDataSchema} for description about this JSON payload
25005              * @param {Function} actionHandler
25006              *     Refer The callback function requirements as described in {@link #showPopover}
25007              * @throws 
25008              *     Throws error when the passed in popoverData is not as per defined format.
25009              */
25010             updatePopover: function (popoverId, bannerData, timerData, actionData) {
25011                 // check whether the service is inited
25012                 _isInited();
25013 
25014                 var isExistingPopover = true; 
25015 
25016                 var gadgetId = _getMyGadgetId();
25017 
25018                 _logger.log('PopoverService: Received an update popover request from gadgetId ' + gadgetId);
25019 
25020                 var payload = this.generatePayload(isExistingPopover, popoverId, bannerData, timerData, actionData);
25021 
25022                 _display(isExistingPopover, popoverId, payload, null);
25023             },
25024 
25025             /**
25026              * Dismisses the notification popover.
25027              *
25028              * @param {String} popoverId
25029              *     The unique identifier for the popover. This id was returned from showPopover call.
25030              * @throws
25031              *     Throws error if the service is not yet initialized.
25032              */
25033             dismissPopover: function (popoverId) {
25034                 _dismiss(popoverId);
25035             },
25036 
25037             /**
25038              * ENUM: Determines how the time in popover should update.
25039              * [COUNT_UP, COUNT_DOWN]
25040              */
25041             COUNTER_TYPE: Object.freeze({
25042                 COUNT_UP: 'COUNT_UP',
25043                 COUNT_DOWN: 'COUNT_DOWN'
25044             })
25045         };
25046     }());
25047 
25048     window.finesse = window.finesse || {};
25049     window.finesse.containerservices = window.finesse.containerservices || {};
25050     window.finesse.containerservices.PopoverService = PopoverService;
25051 
25052     // CommonJS
25053     if (typeof module === "object" && module.exports) {
25054         module.exports = PopoverService;
25055     }
25056 
25057     return PopoverService;
25058 });
25059 /**
25060  * UCCX Email/Chat uses a JSON payload for to trigger workflow.
25061  * That payload has to conform to this schema.
25062  * This schema has been defined as per http://json-schema.org/
25063  *
25064  * @see utilities.JsonValidator
25065  */
25066 /** The following comment is to prevent jslint errors about using variables before they are defined. */
25067 /*global window:true, define:true*/
25068 /*jslint nomen: true, unparam: true, sloppy: true, white: true */
25069 define('workflow/WorkflowSchema',['require','exports','module'],function (require, exports, module) {
25070 
25071     var WorkflowSchema = (function () {
25072         var _workflowParamSchema = {
25073             "$schema": "http://json-schema.org/draft-06/schema#",
25074             "additionalProperties": false,
25075             "properties": {
25076                 "mediaType": {
25077                     "type": "string",
25078                     "title": "media type , eg. email, chat etc."
25079                 },
25080                 "dialogId": {
25081                     "type": "string",
25082                     "title": "The Workflow requestId. this is will be used for logging purpose "
25083                 },
25084                 "state": {
25085                     "type": "string",
25086                     "title": "The When to performa ction Schema e.g. EMAIL_PRESENTED, EMAIL_READ etc"
25087                 },
25088                 "taskVariables": {
25089                     "type": "object"
25090                 }
25091             },
25092             "required": [
25093                 "dialogId",
25094                 "state",
25095                 "mediaType"
25096             ]
25097         };
25098 
25099         return {
25100             _fakeConstuctor: function () {
25101                 /* This is here so we can document init() as a method rather than as a constructor. */
25102             },
25103 
25104             /**
25105              * Returns schema that is applicable for workflow param JSON Structure.
25106              */
25107             getWorkflowParamSchema: function () {
25108                 return _workflowParamSchema;
25109             }
25110         };
25111     }());
25112 
25113     window.finesse = window.finesse || {};
25114     window.finesse.workflow = window.finesse.workflow || {};
25115     window.finesse.workflow.WorkflowSchema = WorkflowSchema;
25116 
25117     // CommonJS
25118     if (typeof module === "object" && module.exports) {
25119         module.exports = WorkflowSchema;
25120     }
25121 
25122     return WorkflowSchema;
25123 });
25124 
25125 /**
25126  * WorkflowService provides API consists of methods that would allow a gadgets to submit workflow task.
25127  *
25128  * @example
25129  *    var containerServices = finesse.containerservices.ContainerServices.init();
25130  *    workflowService = finesse.workflow.WorkflowService.init(containerServices);
25131  *    var payload = {
25132             "dialogId": "email1",
25133             "mediaType": "email",
25134             "state": "EMAIL_READ",
25135             "taskVariables": {
25136                 "from": "mme@cisco.com",
25137                 "cc": "yyy@cisco.com"
25138             }
25139         }
25140  *    workflowService.submitTask(payload);
25141  */
25142 /** @private */
25143 define('workflow/WorkflowService',['require','exports','module','./WorkflowSchema','../utilities/JsonValidator','../clientservices/ClientServices'],function (require, exports, module) {
25144     /** @lends finesse.workflow.WorkflowService.prototype */
25145 
25146     var WorkflowService = (function () {
25147         var WorkflowSchema = require('./WorkflowSchema'),
25148             SchemaValidator = require('../utilities/JsonValidator'),
25149             ClientService = require('../clientservices/ClientServices'),
25150             /**
25151              * References to ContainerService
25152              * @private
25153              */
25154             _containerService,
25155 
25156             /**
25157              * Whether the WorkflowService have been initialized or not.
25158              * @private
25159              */
25160             _inited = false,
25161 
25162             /**
25163              * References to ClientServices logger
25164              * @private
25165              */
25166             _logger = {
25167                 log: ClientService.log
25168             },
25169 
25170             /**
25171              * Ensure that WorkflowService have been initiated.
25172              * @private
25173              */
25174             _isInitiated = function () {
25175                 if (!_inited) {
25176                     throw new Error("WorkflowService needs to be initiated.");
25177                 }
25178             },
25179 
25180             /**
25181              * Gets the id of the gadget.
25182              * @returns {number} the id of the gadget
25183              * @private
25184              */
25185             _getMyGadgetId = function () {
25186                 var gadgetId = _containerService.getMyGadgetId();
25187                 return gadgetId || '';
25188             },
25189 
25190             /**
25191              * Validate the workflow parameter payload structure..
25192              *
25193              * @param {Object} payload
25194              *     workflow parameter.
25195              * @throws {Error} if the payload is not in defined format.
25196              * @private
25197              */
25198             _validateWorkflowParam = function (payload) {
25199                 return SchemaValidator.validateJson(payload, WorkflowSchema.getWorkflowParamSchema());
25200             },
25201 
25202             /**
25203              * Function which processes the the trigger workflow for digital channels.
25204              *
25205              * @param {Object} payload
25206              *     The payload containing workflow submit parameter.
25207              * @private
25208              */
25209             _processWorkflowRequest = function (payload) {
25210                 var result = _validateWorkflowParam(payload), data;
25211 
25212                 if (!result.valid) {
25213                     _logger.log("WorkflowService: Finesse workflow trigger API parameter  Validation result : " + JSON.stringify(result));
25214                     throw new Error("workflow parameter structure is not in expected format. Refer finesse client logs for more details.");
25215                 }
25216 
25217                 _logger.log("[WorkflowService] workflow task submitted for Gadget : " + _getMyGadgetId() + ' : Workflow dialogId: ' + payload.dialogId);
25218                 data = {gadgetId: _getMyGadgetId(), payload: payload};
25219 
25220                 _containerService.publish("finesse.workflow.digitalChannels", data);
25221             };
25222 
25223         return {
25224 
25225             /**
25226              * Method to trigger workflow for digital channels.
25227              *
25228              * @example
25229              * var containerServices = finesse.containerservices.ContainerServices.init();
25230              * workflowService = finesse.workflow.WorkflowService.init(containerServices);
25231              * var payload = {
25232                     "dialogId": "email1",
25233                     "mediaType": "email",
25234                     "state": "EMAIL_READ",
25235                     "taskVariables": {
25236                         "from": "mme@cisco.com",
25237                         "cc": "yyy@cisco.com"
25238                     }
25239                 }
25240              * workflowService.submitTask(payload);
25241              *
25242              * @param {Object} payload
25243              *
25244              */
25245             submitTask: function (payload) {
25246                 _isInitiated();
25247                 _processWorkflowRequest(payload);
25248             },
25249             /**
25250              * @class
25251              * WorkflowService provides API consists of methods that would allow a gadgets to submit workflow task.
25252              *
25253              * @example
25254              *    var containerServices = finesse.containerservices.ContainerServices.init();
25255              *    workflowService = finesse.workflow.WorkflowService.init(containerServices);
25256              *    workflowService.submitTask(payload);
25257              *
25258              * @constructs
25259              */
25260             _fakeConstuctor: function () {
25261                 /* This is here so we can document init() as a method rather than as a constructor. */
25262             },
25263 
25264             /**
25265              *
25266              * Initialize WorkflowService.
25267              *
25268              * @example
25269              * var containerServices = finesse.containerservices.ContainerServices.init();
25270              * workflowService = finesse.workflow.WorkflowService.init(containerServices);
25271              *
25272              * @returns {finesse.workflow.WorkflowService} instance.
25273              *
25274              */
25275             init: function (containerServices) {
25276                 _logger.log("WorkflowService is getting initiated.....");
25277                 if (!_inited) {
25278                     if (!containerServices) {
25279                         throw new Error("WorkflowService: Invalid ContainerService reference.");
25280                     }
25281                     _inited = true;
25282                     _containerService = containerServices;
25283                     window.finesse.clientservices.ClientServices.setLogger(window.finesse.cslogger.ClientLogger);
25284                 }
25285                 return this;
25286             }
25287         };
25288     }());
25289 
25290     window.finesse = window.finesse || {};
25291     window.finesse.workflow = window.finesse.workflow || {};
25292     window.finesse.workflow.WorkflowService = WorkflowService;
25293 
25294     // CommonJS
25295     if (typeof module === "object" && module.exports) {
25296         module.exports = WorkflowService;
25297     }
25298 
25299     return WorkflowService;
25300 });
25301 
25302 
25303 /**
25304  * JavaScript representation of the Finesse UserTeamMessages object for a Supervisor.
25305  *
25306  * @requires Class
25307  * @requires finesse.FinesseBase
25308  * @requires finesse.restservices.RestBase
25309  * @requires finesse.utilities.Utilities
25310  * @requires finesse.restservices.TeamMessage
25311  */
25312 
25313 /** The following comment is to prevent jslint errors about 
25314  * using variables before they are defined.
25315  */
25316 /*global Exception */
25317 
25318 /** @private */
25319 define('restservices/UserTeamMessages',[
25320     'restservices/RestCollectionBase',
25321     'restservices/RestBase',
25322     'utilities/Utilities',
25323     'restservices/TeamMessage',
25324     'restservices/TeamMessages'
25325 ],
25326 function (RestCollectionBase, RestBase,Utilities, TeamMessage, TeamMessages) {
25327     
25328     var UserTeamMessages = TeamMessages.extend({
25329     
25330       /**
25331        * @class
25332        * JavaScript representation of a LayoutConfig object for a Team. Also exposes
25333        * methods to operate on the object against the server.
25334        *
25335        * @param {Object} options
25336        *     An object with the following properties:<ul>
25337        *         <li><b>id:</b> The id of the object being constructed</li>
25338        *         <li><b>onLoad(this): (optional)</b> when the object is successfully loaded from the server</li>
25339        *         <li><b>onChange(this): (optional)</b> when an update notification of the object is received</li>
25340        *         <li><b>onAdd(this): (optional)</b> when a notification that the object is created is received</li>
25341        *         <li><b>onDelete(this): (optional)</b> when a notification that the object is deleted is received</li>
25342        *         <li><b>onError(rsp): (optional)</b> if loading of the object fails, invoked with the error response object:<ul>
25343        *             <li><b>status:</b> {Number} The HTTP status code returned</li>
25344        *             <li><b>content:</b> {String} Raw string of response</li>
25345        *             <li><b>object:</b> {Object} Parsed object of response</li>
25346        *             <li><b>error:</b> {Object} Wrapped exception that was caught:<ul>
25347        *                 <li><b>errorType:</b> {String} Type of error that was caught</li>
25348        *                 <li><b>errorMessage:</b> {String} Message associated with error</li>
25349        *             </ul></li>
25350        *         </ul></li>
25351        *         <li><b>parentObj: (optional)</b> The parent object</li></ul>
25352        * @constructs
25353        **/
25354       init: function (options) {
25355     	  var options = options || {};
25356     	  options.parentObj = this;
25357     	  this._super(options);
25358       },
25359     
25360       /**
25361        * @private
25362        * Gets the REST class for the current object - this is the TeamMessages class.
25363        * @returns {Object} The LayoutConfigs class.
25364        */
25365       getRestClass: function () {
25366           return TeamMessages;
25367       },
25368 
25369       getRestItemClass: function () {
25370             return TeamMessage;
25371         },
25372 
25373 	  getRestType: function () {
25374 		return "TeamMessages";
25375 	  }, 
25376     
25377       /**
25378        * @private
25379        * Override default to indicate that this object doesn't support making
25380        * requests.
25381        */
25382       supportsRequests: false,
25383     
25384       /**
25385        * @private
25386        * Override default to indicate that this object doesn't support subscriptions.
25387        */
25388       supportsSubscriptions: false,
25389 
25390       supportsRestItemSubscriptions: false,
25391     
25392       getTeamMessages: function (createdBy, handlers) {
25393 
25394           var self = this, contentBody, reasonCode, url;
25395           contentBody = {};
25396           // Protect against null dereferencing of options allowing its (nonexistent) keys to be read as undefined
25397           handlers = handlers || {};
25398           this.restRequest('/finesse/api/'+this.getRestType()+'?createdBy='+createdBy, {
25399               method: 'GET',
25400               success: function(rsp) {
25401             	  var teamMessage = rsp.object.teamMessages? rsp.object.teamMessages.TeamMessage: null
25402             	  handlers.success(teamMessage);
25403               },
25404 	          error: function (rsp) {
25405 	              handlers.error(rsp);
25406 	          },
25407 	          content: contentBody
25408           });
25409 
25410           return this; // Allow cascading
25411       },
25412       
25413      /**
25414       * 
25415       */
25416       deleteTeamMessage: function (messageId, handlers) {
25417           handlers = handlers || {};
25418           this.restRequest(this.getRestItemBaseUrl() +'/'+messageId, {
25419               method: 'DELETE',
25420               success: handlers.success,
25421               error: handlers.error,
25422               content: undefined
25423           });
25424           return this; // Allow cascading
25425       }
25426       
25427       });
25428         
25429     window.finesse = window.finesse || {};
25430     window.finesse.restservices = window.finesse.restservices || {};
25431     window.finesse.restservices.UserTeamMessages = UserTeamMessages;
25432       
25433     return UserTeamMessages;
25434 });
25435 define('finesse',[
25436     'restservices/Users',
25437     'restservices/Teams',
25438     'restservices/SystemInfo',
25439     'restservices/Media',
25440     'restservices/MediaDialogs',
25441     'restservices/DialogLogoutActions',
25442     'restservices/InterruptActions',
25443     'restservices/ReasonCodeLookup',
25444     'restservices/ChatConfig',
25445     'utilities/I18n',
25446     'utilities/Logger',
25447     'utilities/SaxParser',
25448     'utilities/BackSpaceHandler',
25449     'utilities/JsonValidator',
25450     'cslogger/ClientLogger',
25451     'cslogger/FinesseLogger',
25452     'containerservices/ContainerServices',
25453     'containerservices/FinesseToaster',
25454     'interfaces/RestObjectHandlers',
25455     'interfaces/RequestHandlers',
25456     'gadget/Config',
25457     'digital/ChannelSchema',
25458     'digital/ChannelService',
25459     'containerservices/PopoverService',
25460     'containerservices/PopoverSchema',
25461     'workflow/WorkflowSchema',
25462     'workflow/WorkflowService',
25463     'restservices/UserTeamMessages'
25464 ],
25465 function () {
25466     return window.finesse;
25467 });
25468 
25469 require(["finesse"]);
25470 return require('finesse'); }));
25471 
25472 // Prevent other JS files from wiping out window.finesse from the namespace
25473 var finesse = window.finesse;